在 Hibernate 中,你可以有一个组合键,其中一个值是数据库生成的吗?

In Hibernate, can you have a composite key where one value is database generated

我有一个带有复合键的 table 数据库,其中一个值是在数据库中生成的,另一个是基于父 class 设置的。我正在尝试在休眠中实现它。我已经看到大量与此矛盾的文章,可能基于不同的版本。对于 Hibernate 4.3,我该怎么做?

这是似乎最合理但行不通的解决方案。数据库最大的限制是要生成的key不应该在insert

中传入

内部邮件列表class

     @EmbeddedId
     @AttributeOverrides( {
     @AttributeOverride(name="mailLists", column=@Column(name="Mail_Lists", nullable=false) ), 
     @AttributeOverride(name="ien", column=@Column(name="IEN", nullable=false, insertable=false, updatable=false) ) } )
     public MailListsSubscriberId getId() {
         return this.id;
     }

单独的复合 ID class

@Embeddable
public class MailListsSubscriberId  implements java.io.Serializable {
 private int mailLists;
 private int ien;

 @Column(name="Mail_Lists", nullable=false)
 public int getMailLists() {
    return this.mailLists;
 }

 ...    

 @Generated(GenerationTime.ALWAYS)
 @Column(name="IEN", nullable=false, insertable=false, updatable=false)
 public int getIen() {
    return this.ien;
 }
...

日志

Caused by: org.hibernate.id.IdentifierGenerationException: null id generated for:class edu.ucdavis.vmth.vmacs.persistence.model.MailListsSubscriber
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:120)
at org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:271)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:251)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:189)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:886)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:868)
at org.hibernate.engine.spi.CascadingActions.cascade(CascadingActions.java:277)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:474)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:343)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:186)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)

问题是您的数据库模型不正确,无法与 hibernate 或任何其他 ORM 一起使用。提示是你在问题中所说的

The big limitation on the database is that the key to be generated shouldn't be passed in the update

这意味着生成的列不是键的一部分,否则您将需要它,否则它不是标识符的一部分。

如果您想了解为什么会这样,您可以阅读 Object-Relational impedance mismatch

如果您正在处理设计不佳的遗留架构并且您无法更改它,我建议您放弃休眠并使用类似 MyBatis 的东西。

编辑

好的,在澄清不能在 insert 而不是 update 上传递密钥之后,有一个解决方案(我'我从未使用过,所以希望它会起作用)。 Hibernate 支持一种叫做 selectgenerator 类型,它就是为这种情况而存在的。

例如,您可以尝试使用以下注释注释 ien 列(示例取自 Hibernate docs

@Id
private int mailLists;

@Id @GeneratedValue(generator="trigger-generated")
@GenericGenerator(
    name="trigger-generated", 
    strategy = "select",
    parameters = @Parameter(name="key", value = "surrogateIdColumnName")
)
private String ien;

奇怪 事情是,为了使休眠能够检索生成的密钥,它需要 select 行...为此,它需要知道另一列用作代理键。在上面的例子中,table 必须有一个名为 surrogateIdColumnName 的列,它是唯一的。

理想情况下,如果您可以更新架构,最好为 table 分配一个具有自动增量值的单个 id 列,并使另一个键 (mailList + ien) 成为唯一键。