【问题标题】:How to create an entity with a composite primary key containing a generated value如何使用包含生成值的复合主键创建实体
【发布时间】:2010-06-07 09:04:21
【问题描述】:

使用 Hibernate + 注释,我正在尝试执行以下操作:

两个实体,Entity1 和 Entity2。

  • Entity1 包含一个简单的生成值主键。
  • Entity2 主键由一个简单的生成值 + 实体 1 的 id 组成(具有多对一关系)

很遗憾,我做不到。

这里是代码的摘录:

@Entity
public class Entity1 {

 @Id @GeneratedValue
 private Long id;

 private String name;

    ...
}

@Entity
public class Entity2 {

 @EmbeddedId
 private Entity2PK pk = new Entity2PK();

 private String miscData;

    ...
}

@Embeddable
public class Entity2PK implements Serializable {

 @GeneratedValue
 private Long id;

 @ManyToOne
 private Entity1 entity;
}

void test() {

    Entity1 e1 = new Entity1();
    e1.setName("nameE1");
    Entity2 e2 = new Entity2();

    e2.setEntity1(e1);
    e2.setMiscData("test");

    Transaction transaction = session.getTransaction();
    try {
     transaction.begin();

     session.save(e1);
     session.save(e2);

     transaction.commit();
    } catch (Exception e) {
     transaction.rollback();
    } finally {
     session.close();
    }
}

当我运行测试方法时,出现以下错误:

Hibernate: insert into Entity1 (id, name) values (null, ?)
Hibernate: call identity()
Hibernate: insert into Entity2 (miscData, entity_id, id) values (?, ?, ?)
07-Jun-2010 10:51:11 org.hibernate.util.JDBCExceptionReporter logExceptions
WARNING: SQL Error: 0, SQLState: null
07-Jun-2010 10:51:11 org.hibernate.util.JDBCExceptionReporter logExceptions
SEVERE: failed batch
07-Jun-2010 10:51:11 org.hibernate.event.def.AbstractFlushingEventListener performExecutions
SEVERE: Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
 at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
 at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
 at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
 at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:254)
 at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
 at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
 at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
 at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
 at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1001)
 at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:339)
 at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
 at test.App.main(App.java:32)
Caused by: java.sql.BatchUpdateException: failed batch
 at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
 at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
 at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
 at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:247)
 ... 8 more

请注意,我使用 HSQLDB。

关于什么是错的任何想法?

【问题讨论】:

    标签: java hibernate jpa primary-key composite


    【解决方案1】:

    如果你想要这种行为,请执行以下操作(你需要使用属性访问而不是字段一)

    Parent.class(注意类型 MutableInt 和 addChild 的属性作为设置双方的一种方式)

    @Entity
    public class Parent implements Serializable {
    
        private MutableInt id = new MutableInt();
    
        private List<Child> childList = new ArrayList();
    
        @OneToMany(mappedBy="parent")
        @JoinColumn(name="PARENT_ID", insertable=false, updatable=false)
        @Cascade(CascadeType.SAVE_UPDATE)
        public List<Child> getChildList() {
            return childList;
        }
    
        public void setChildList(List<Child> childList) {
            this.childList = childList;
        }
    
        @Id
        @GeneratedValue
        public Integer getId() {
            return id.intValue();
        }
    
        public void setId(Integer id) {
            this.id.setValue(id);
        }
    
        @Transient
        public MutableInt getIdAsMutableInt() {
            return id;
        }
    
        /**
         * Add convenience method 
         * 
         * A way to set up both sides (You have a bi-directional relationship, right ???)
         */
        public void addChild(Child child) {
            if(child.getChildId() == null)
                child.setChildId(new Child.ChildId());
    
            child.getChildId().setParentIdAsMutableInt(id);
    
            getChildList().add(child);
    
            child.setParent(this);
        }
    
    }
    

    Child.class(注意静态内部类)

    @Entity
    public class Child implements Serializable {
    
        private ChildId childId;
    
        private Parent parent;
    
        @EmbeddedId
        public ChildId getChildId() {
            return childId;
        }
    
        public void setChildId(ChildId childId) {
            this.childId = childId;
        }
    
        @ManyToOne
        @JoinColumn(name="PARENT_ID", insertable=false, updatable=false)
        public Parent getParent() {
            return parent;
        }
    
        public void setParent(Parent parent) {
            this.parent = parent;
        }
    
        /**
          * Composite primary key class MUST override equals and hashCode
          */
        @Embeddable
        public static class ChildId implements Serializable {
    
            private MutableInt parentId = new MutableInt();
    
            private Integer chId;
    
            public void setParentIdAsMutableInt(MutableInt parentId) {
                this.parentId = parentId;
            }
    
            @GeneratedValue
            public Integer getChId() {
                return chId;
            }
    
            public void setChId(Integer chId) {
                this.chId = chId;
            }
    
            @Column(name="PARENT_ID")
            public Integer getParentId() {
                return parentId.intValue();
            }
    
            public void setParentId(Integer parentId) {
                this.parentId.setValue(parentId);
            }
    
            @Override
            public boolean equals(Object o) {
                if(!(o instanceof ChildId))
                    return false;
    
                final ChildId other = (ChildId) o;
                return new EqualsBuilder()
                           .append(getChId(), other.getChId())
                           .append(getParentId(), other.getParentId()).isEquals();
            }
    
            @Override
            public int hashCode() {
                int hash = 5;
                hash = 11 * hash + getParentId().hashCode();
                hash = 11 * hash + getChId().hashCode();
                return hash;
            }
    
        }
    
    }
    

    测试

    Session session = configuration.buildSessionFactory().openSession();
    session.beginTransaction();
    
    Parent parent = new Parent();
    parent.addChild(new Child());
    
    session.save(parent);
    
    session.getTransaction().commit();
    

    你需要一个 MutableInt 因为 Integer 是不可变的实例。但是您的 MutableInt 字段由 Integer 属性封装。仔细看

    【讨论】:

    • 感谢您的解决方案,但我觉得这很乏味。为什么不能像我写的代码那么简单?
    • @David 如果你真的想要这种行为,你应该按照所示做。请记住:如果您的@Entity 需要复合主键,例如 Child,您应该提供复合主键类并且必须覆盖 equals 和 hashCode - 请参阅 ChildId 类.添加便利方法 - 请参阅父类 - 负责设置双向关系的双方。
    【解决方案2】:

    您可以在嵌入的 id 中使用外键作为单独的只读列:

    @Entity 
    public class Entity2 { 
    
        @EmbeddedId 
        private Entity2PK pk = new Entity2PK(); 
    
        @ManyToOne 
        @JoinColumn(name = "entity1_id")
        private Entity1 entity; 
    
        ... 
    } 
    
    @Embeddable 
    public class Entity2PK implements Serializable { 
    
        @GeneratedValue 
        private Long id; 
    
        @Column(name = "entity1_id", insertable = false, updateable = false)
        private Long entity1Id;
    
    }
    

    【讨论】:

    • 我尝试了您的解决方案,但不幸的是无法使其工作:原因:org.hibernate.MappingException:实体映射中的重复列:test.Entity2 列:entity1_id(应该使用 insert= 映射“假”更新=“假”)
    • @David:注意Entity2PK中的insertable = false, updateable = false
    猜你喜欢
    • 1970-01-01
    • 2018-12-25
    • 2011-12-17
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多