最近我的博客老是搬家,最后还是回到开源中国的博客,真心对不起那些加过我贴子收藏的人,我悔过!希望我的贴子能帮助到和我遇到同样问题的朋友!
正题。如何用Hibernate实现复杂主键,@EmbeddedId、@IdClass用哪个实现?
一个表有多个主键,有外键,这个外键还是主键,其他的主键有一个还是自动产生id的,我遇到这个问题要设计实体类的时候也是醉了~
当然反向生成实体类方便很多。但是生成的实体类复合主键用的都是@EmbeddedId、和@Embeddable注解。这种注解需要手动new一个被@Embeddable注解的嵌入类,set完属性后将这个嵌入对象set给@EmbeddedId注解getter,不能使是其中的属性自动产生值。
我一开始找的办法是怎么在被@EmbeddedId或@Embeddable注解的类添加序列或uuid,后来查JPA文档才想起了注解ID还有个@IdClass。发现改写还挺容易的,下面是简化的例子。
反向生成被@EmbeddedId或@Embeddable注解的类:
表对应的实体类:
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverrides; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.kcomdata.entity.PdBvd; import com.kcomdata.entity.PdBvdIndId; @Entity @Table(name = "PD_BVD_IND") public class PdBvdInd implements java.io.Serializable { private PdBvdIndId id;// 这一个联合主键嵌入的类,存放多个主键。 private PdBvd pdBvd;// 这个会产生一个外键字段BVD_ID,但是表中它还是个主键。 /** * 嵌套进来的PdBvdIndId类,其属性是多个主键,其中BVD_ID和外键是同一个,所以就同时是主键,ID字段需要使用uuid生成。 */ @EmbeddedId @AttributeOverrides({ @AttributeOverride(name = "bvdId", column = @Column(name = "BVD_ID", nullable = false, length = 50)), @AttributeOverride(name = "type", column = @Column(name = "TYPE", nullable = false, length = 50)), @AttributeOverride(name = "id", column = @Column(name = "ID", nullable = false, precision = 19)) }) public PdBvdIndId getId() { return this.id; } public void setId(PdBvdIndId id) { this.id = id; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "BVD_ID", nullable = false, insertable = false, updatable = false) public PdBvd getPdBvd() { return this.pdBvd; } public void setPdBvd(PdBvd pdBvd) { this.pdBvd = pdBvd; } }
实体类的ID嵌入类:
import java.math.BigDecimal; import javax.persistence.Column; import javax.persistence.Embeddable; @Embeddable public class PdBvdIndId implements java.io.Serializable { private String bvdId; private String type; private BigDecimal id; @Column(name = "BVD_ID", nullable = false, length = 50) public String getBvdId() { return this.bvdId; } public void setBvdId(String bvdId) { this.bvdId = bvdId; } @Column(name = "TYPE", nullable = false, length = 50) public String getType() { return this.type; } public void setType(String type) { this.type = type; } @Column(name = "ID", nullable = false, precision = 19) public BigDecimal getId() { return id; } public void setId(BigDecimal id) { this.id = id; } public boolean equals(Object other) { if ((this == other)) return true; if ((other == null)) return false; if (!(other instanceof PdBvdIndId)) return false; PdBvdIndId castOther = (PdBvdIndId) other; return ((this.getBvdId() == castOther.getBvdId()) || (this.getBvdId() != null && castOther.getBvdId() != null && this.getBvdId().equals(castOther.getBvdId()))) && ((this.getType() == castOther.getType()) || (this.getType() != null && castOther.getType() != null && this.getType() .equals(castOther.getType()))) && ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId()))); } public int hashCode() { int result = 17; result = 37 * result + (getBvdId() == null ? 0 : this.getBvdId().hashCode()); result = 37 * result + (getType() == null ? 0 : this.getType().hashCode()); result = 37 * result + (getId() == null ? 0 : this.getId().hashCode()); return result; } }
生成的这两个类不能满足其中一个主键用uuid、序列或自动增长生成。改写成@IdClass的步骤:
表对应的实体类:
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverrides; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; import com.kcomdata.entity.PdBvd; import com.kcomdata.entity.PdBvdIndId; @Entity @Table(name = "PD_BVD_IND") @IdClass(PdBvdIndId.class)//指定id类 public class PdBvdInd implements java.io.Serializable { /* 去掉那个主键类属性,将主键类属性全部搬过来 */ private String bvdId;//这个属性映射着pdBvd属性生成的外键字段,持久化时需要手动填值,不需要set入关联对象即调用getPdBvd()。 private String type;//这个主键持久化的时候需要手动填写。 private String id;//这个用uuid生成,但原表的类型是number(19),uuid需要用varchar存储,将原来的类型改成String。 private PdBvd pdBvd;// 这个会产生一个外键字段BVD_ID,但是表中它还是个主键。 /*这个不需要了删除掉 @EmbeddedId @AttributeOverrides({ @AttributeOverride(name = "bvdId", column = @Column(name = "BVD_ID", nullable = false, length = 50)), @AttributeOverride(name = "type", column = @Column(name = "TYPE", nullable = false, length = 50)), @AttributeOverride(name = "id", column = @Column(name = "ID", nullable = false, precision = 19)) }) public PdBvdIndId getId() { return this.id; } public void setId(PdBvdIndId id) { this.id = id; }*/ @ManyToOne(fetch = FetchType.LAZY)//这是外键,非空的。 @JoinColumn(name = "BVD_ID", nullable = false, insertable = false, updatable = false) public PdBvd getPdBvd() { return this.pdBvd; } public void setPdBvd(PdBvd pdBvd) { this.pdBvd = pdBvd; } @Id//这个不需要生成策略,它映射着外键,插入时需要手动插入数据,外键也就有了数据,所以持久化时不需要调用getPdBvd() @Column(name = "BVD_ID", nullable = false, length = 50) public String getBvdId() { return bvdId; } public void setBvdId(String bvdId) { this.bvdId = bvdId; } @Id//这个不需要生成策略,但持久化时要手动set值 @Column(name = "TYPE", nullable = false, length = 50) public String getType() { return type; } public void setType(String type) { this.type = type; } @Id//这个是用uuid自动生成 @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid") @Column(name = "ID", nullable = false) public String getId() { return id; } public void setId(String id) { this.id = id; } }
@IdClass的复合组件类:
public class PdBvdIndId implements java.io.Serializable { private String bvdId; private String type; private String id;//将原来的BigDecimal改成String,用来保存uuid。 public String getBvdId() { return this.bvdId; } public void setBvdId(String bvdId) { this.bvdId = bvdId; } public String getType() { return this.type; } public void setType(String type) { this.type = type; } public String getId() { return id; } public void setId(String id) { this.id = id; } /** * 作为@IdClass的复合主键类,必须重写equals方法和hashCode(),但是从数据库反向生成的@Embeddable类也必须重写, * 所以这里已经自动生成。 */ public boolean equals(Object other) { if ((this == other)) return true; if ((other == null)) return false; if (!(other instanceof PdBvdIndId)) return false; PdBvdIndId castOther = (PdBvdIndId) other; return ((this.getBvdId() == castOther.getBvdId()) || (this.getBvdId() != null && castOther.getBvdId() != null && this.getBvdId().equals(castOther.getBvdId()))) && ((this.getType() == castOther.getType()) || (this.getType() != null && castOther.getType() != null && this.getType() .equals(castOther.getType()))) && ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId()))); } public int hashCode() { int result = 17; result = 37 * result + (getBvdId() == null ? 0 : this.getBvdId().hashCode()); result = 37 * result + (getType() == null ? 0 : this.getType().hashCode()); result = 37 * result + (getId() == null ? 0 : this.getId().hashCode()); return result; } }
表结构:
PD_BVD_IND | 表名 |
BVD_ID | varchar2(50) |
TYPE | varchar2(50) |
ID | varchar2(255) |
主键:BVD_ID、TYPE、ID(BVD_ID来自外键,TYPE手动填写,ID自动生成)
外键:BVD_ID