Java 使用 JPA 时,如何设置联合主键

在 Java 中使用 JPA(Java Persistence API)时,可以通过 @Embeddable@EmbeddedId@IdClass 两种方式来配置联合主键。以下是详细的实现步骤和两种方式的示例代码。


1. 使用 @Embeddable@EmbeddedId 实现联合主键

这种方式的核心思路是将联合主键封装在一个独立的类中,并使用 @Embeddable 注解该类,然后在实体类中使用 @EmbeddedId 来引用它。推荐这种方式,因为它更加模块化和易于管理。

实现步骤

  1. 创建一个主键类,并用 @Embeddable 注解。
  2. 在实体类中使用 @EmbeddedId 来嵌入这个主键类。

示例代码

1. 定义联合主键类

import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;

@Embeddable
public class OrderItemId implements Serializable {
    private Long orderId;
    private Long productId;

    // 构造方法、Getter、Setter、hashCode 和 equals 方法

    public OrderItemId() {}

    public OrderItemId(Long orderId, Long productId) {
        this.orderId = orderId;
        this.productId = productId;
    }

    public Long getOrderId() { return orderId; }
    public void setOrderId(Long orderId) { this.orderId = orderId; }

    public Long getProductId() { return productId; }
    public void setProductId(Long productId) { this.productId = productId; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OrderItemId that = (OrderItemId) o;
        return Objects.equals(orderId, that.orderId) &&
               Objects.equals(productId, that.productId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }
}

2. 定义实体类

import jakarta.persistence.Entity;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Table;

@Entity
@Table(name = "order_item")
public class OrderItem {
    
    @EmbeddedId
    private OrderItemId id;

    private Integer quantity;

    // 构造方法、Getter 和 Setter 方法

    public OrderItem() {}

    public OrderItem(OrderItemId id, Integer quantity) {
        this.id = id;
        this.quantity = quantity;
    }

    public OrderItemId getId() { return id; }
    public void setId(OrderItemId id) { this.id = id; }

    public Integer getQuantity() { return quantity; }
    public void setQuantity(Integer quantity) { this.quantity = quantity; }
}

总结

使用 @Embeddable@EmbeddedId 的方式将联合主键封装在独立的类中,使代码更加清晰和模块化。OrderItemId 作为主键类,OrderItem 实体类直接引用了这个主键类。JPA 在存储和查询时会将 orderIdproductId 作为联合主键使用。


2. 使用 @IdClass 实现联合主键

另一种方式是使用 @IdClass,这种方式需要在主实体类中直接声明主键字段,并在实体类上使用 @IdClass 注解指定联合主键类。

实现步骤

  1. 创建一个联合主键类,并实现 Serializable 接口。
  2. 在实体类中使用 @IdClass 注解,并直接在实体类中定义每个主键字段。

示例代码

1. 定义联合主键类

import java.io.Serializable;
import java.util.Objects;

public class OrderItemId implements Serializable {
    private Long orderId;
    private Long productId;

    // 构造方法、Getter、Setter、hashCode 和 equals 方法

    public OrderItemId() {}

    public OrderItemId(Long orderId, Long productId) {
        this.orderId = orderId;
        this.productId = productId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OrderItemId that = (OrderItemId) o;
        return Objects.equals(orderId, that.orderId) &&
               Objects.equals(productId, that.productId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }
}

2. 定义实体类

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.Table;

@Entity
@Table(name = "order_item")
@IdClass(OrderItemId.class)
public class OrderItem {

    @Id
    private Long orderId;

    @Id
    private Long productId;

    private Integer quantity;

    // 构造方法、Getter 和 Setter 方法

    public OrderItem() {}

    public OrderItem(Long orderId, Long productId, Integer quantity) {
        this.orderId = orderId;
        this.productId = productId;
        this.quantity = quantity;
    }

    public Long getOrderId() { return orderId; }
    public void setOrderId(Long orderId) { this.orderId = orderId; }

    public Long getProductId() { return productId; }
    public void setProductId(Long productId) { this.productId = productId; }

    public Integer getQuantity() { return quantity; }
    public void setQuantity(Integer quantity) { this.quantity = quantity; }
}

总结

使用 @IdClass 的方式更加直接,它将每个主键字段都在实体类中直接定义,但联合主键的逻辑分散在实体类中。适合简单场景,或无需重用联合主键逻辑的情况。

比较和选择

  • @Embeddable@EmbeddedId:模块化更好,推荐使用。如果联合主键需要复用,建议使用这种方式。
  • @IdClass:在实体类中直接定义主键字段,适用于简单结构的联合主键。

在大多数场景下,@EmbeddedId 的方式更灵活和清晰,便于管理复杂的主键关系。



假如一个表中所有的列都是参与了构建联合主键,那么JPA 的Entity 如何写呢?

如果一个表中的所有列都参与联合主键,我们可以使用 @Embeddable@EmbeddedId 或者 @IdClass 实现,下面是两种实现方式的详细代码。

假设我们有一个表 Record,其中包含 columnA, columnB, 和 columnC 三列,这三列一起构成联合主键。


使用 @Embeddable@EmbeddedId

首先,我们创建一个嵌入式主键类,并用 @Embeddable 注解标识它,然后在实体类中通过 @EmbeddedId 嵌入该主键类。

1. 创建嵌入式主键类

import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;

@Embeddable
public class RecordId implements Serializable {
    private String columnA;
    private String columnB;
    private String columnC;

    // 构造方法
    public RecordId() {}

    public RecordId(String columnA, String columnB, String columnC) {
        this.columnA = columnA;
        this.columnB = columnB;
        this.columnC = columnC;
    }

    // Getter 和 Setter 方法
    public String getColumnA() { return columnA; }
    public void setColumnA(String columnA) { this.columnA = columnA; }

    public String getColumnB() { return columnB; }
    public void setColumnB(String columnB) { this.columnB = columnB; }

    public String getColumnC() { return columnC; }
    public void setColumnC(String columnC) { this.columnC = columnC; }

    // 重写 equals 和 hashCode 方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RecordId recordId = (RecordId) o;
        return Objects.equals(columnA, recordId.columnA) &&
               Objects.equals(columnB, recordId.columnB) &&
               Objects.equals(columnC, recordId.columnC);
    }

    @Override
    public int hashCode() {
        return Objects.hash(columnA, columnB, columnC);
    }
}

2. 创建实体类并嵌入主键

import jakarta.persistence.Entity;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Table;

@Entity
@Table(name = "record")
public class Record {

    @EmbeddedId
    private RecordId id;

    // 构造方法
    public Record() {}

    public Record(RecordId id) {
        this.id = id;
    }

    // Getter 和 Setter 方法
    public RecordId getId() { return id; }
    public void setId(RecordId id) { this.id = id; }
}

通过这种方式,Record 实体类中使用了 @EmbeddedId 来引用 RecordId,并实现了所有列作为联合主键的效果。


使用 @IdClass

另一种方式是使用 @IdClass,在主实体类中直接定义每个主键字段,并用 @IdClass 注解标识联合主键类。

1. 定义联合主键类

import java.io.Serializable;
import java.util.Objects;

public class RecordId implements Serializable {
    private String columnA;
    private String columnB;
    private String columnC;

    // 构造方法
    public RecordId() {}

    public RecordId(String columnA, String columnB, String columnC) {
        this.columnA = columnA;
        this.columnB = columnB;
        this.columnC = columnC;
    }

    // 重写 equals 和 hashCode 方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RecordId recordId = (RecordId) o;
        return Objects.equals(columnA, recordId.columnA) &&
               Objects.equals(columnB, recordId.columnB) &&
               Objects.equals(columnC, recordId.columnC);
    }

    @Override
    public int hashCode() {
        return Objects.hash(columnA, columnB, columnC);
    }
}

2. 定义实体类并直接设置主键字段

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.Table;

@Entity
@Table(name = "record")
@IdClass(RecordId.class)
public class Record {

    @Id
    private String columnA;

    @Id
    private String columnB;

    @Id
    private String columnC;

    // 构造方法
    public Record() {}

    public Record(String columnA, String columnB, String columnC) {
        this.columnA = columnA;
        this.columnB = columnB;
        this.columnC = columnC;
    }

    // Getter 和 Setter 方法
    public String getColumnA() { return columnA; }
    public void setColumnA(String columnA) { this.columnA = columnA; }

    public String getColumnB() { return columnB; }
    public void setColumnB(String columnB) { this.columnB = columnB; }

    public String getColumnC() { return columnC; }
    public void setColumnC(String columnC) { this.columnC = columnC; }
}

总结

这两种方法均可实现联合主键。推荐使用 @Embeddable@EmbeddedId 的方式,因为它更加模块化和灵活,尤其在多个地方复用联合主键类时更为方便。

posted @ 2024-11-05 01:25  gongchengship  阅读(123)  评论(0)    收藏  举报