JPA @EmbeddedId 未生成序列

Posted

技术标签:

【中文标题】JPA @EmbeddedId 未生成序列【英文标题】:JPA @EmbeddedId is not generating sequence 【发布时间】:2010-12-15 05:32:19 【问题描述】:

我有一个包含一个序列和两个外键的复合主键的表 我能够坚持我的实体类,但它没有按照顺序生成。具有由一个序列和两个外键组成的复合主键的表,maven中的hbm2java给出了以下实体

这里是主要实体

package aop.web.teacher.rmodels; // Generated Dec 14, 2010 8:45:32 PM by Hibernate Tools 3.2.2.GA import java.util.Date; import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverrides; import javax.persistence.Column; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; /** * Schoolmaster generated by hbm2java */ @Entity @Table(name = "schoolmaster", schema = "public") public class Schoolmaster implements java.io.Serializable private SchoolmasterId id; ... @EmbeddedId @AttributeOverrides( @AttributeOverride(name = "id", column = @Column(name = "id", nullable = false)), @AttributeOverride(name = "districtId", column = @Column(name = "district_id", nullable = false)), @AttributeOverride(name = "typeOfSchool", column = @Column(name = "type_of_school", nullable = false)) ) public SchoolmasterId getId() return this.id; public void setId(SchoolmasterId id) this.id = id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "type_of_school", nullable = false, insertable = false, updatable = false) public AopTeachersTypeMaster getAopTeachersTypeMaster() return this.aopTeachersTypeMaster; public void setAopTeachersTypeMaster( AopTeachersTypeMaster aopTeachersTypeMaster) this.aopTeachersTypeMaster = aopTeachersTypeMaster; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "school_nature") public AopTeachersSchoolNatureMaster getAopTeachersSchoolNatureMaster() return this.aopTeachersSchoolNatureMaster; public void setAopTeachersSchoolNatureMaster( AopTeachersSchoolNatureMaster aopTeachersSchoolNatureMaster) this.aopTeachersSchoolNatureMaster = aopTeachersSchoolNatureMaster; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "district_id", nullable = false, insertable = false, updatable = false) public AopTeachersDistrictMaster getAopTeachersDistrictMaster() return this.aopTeachersDistrictMaster; public void setAopTeachersDistrictMaster( AopTeachersDistrictMaster aopTeachersDistrictMaster) this.aopTeachersDistrictMaster = aopTeachersDistrictMaster; @Column(name = "school_name", length = 50) public String getSchoolName() return this.schoolName; public void setSchoolName(String schoolName) this.schoolName = schoolName; @Column(name = "school_address") public String getSchoolAddress() return this.schoolAddress; public void setSchoolAddress(String schoolAddress) this.schoolAddress = schoolAddress; @Column(name = "school_phone_number", length = 12) public String getSchoolPhoneNumber() return this.schoolPhoneNumber; public void setSchoolPhoneNumber(String schoolPhoneNumber) this.schoolPhoneNumber = schoolPhoneNumber; @Temporal(TemporalType.DATE) @Column(name = "establishment_date", length = 13) public Date getEstablishmentDate() return this.establishmentDate; public void setEstablishmentDate(Date establishmentDate) this.establishmentDate = establishmentDate; @Column(name = "school_no_of_teachers") public Integer getSchoolNoOfTeachers() return this.schoolNoOfTeachers; public void setSchoolNoOfTeachers(Integer schoolNoOfTeachers) this.schoolNoOfTeachers = schoolNoOfTeachers; @Column(name = "school_no_of_students") public Integer getSchoolNoOfStudents() return this.schoolNoOfStudents; public void setSchoolNoOfStudents(Integer schoolNoOfStudents) this.schoolNoOfStudents = schoolNoOfStudents;

这是嵌入式 PK 类。

/** * SchoolmasterId generated by hbm2java */ @Embeddable public class SchoolmasterId implements java.io.Serializable private long id; private long districtId; private long typeOfSchool; public SchoolmasterId() public SchoolmasterId(long id, long districtId, long typeOfSchool) this.id = id; this.districtId = districtId; this.typeOfSchool = typeOfSchool; @Column(name="id", nullable=false) @GeneratedValue(strategy=GenerationType.SEQUENCE) public long getId() return this.id; public void setId(long id) this.id = id; @NaturalId @Column(name="district_id", nullable=false) public long getDistrictId() return this.districtId; public void setDistrictId(long districtId) this.districtId = districtId; @NaturalId @Column(name="type_of_school", nullable=false) public long getTypeOfSchool() return this.typeOfSchool; public void setTypeOfSchool(long typeOfSchool) this.typeOfSchool = typeOfSchool; public boolean equals(Object other) if ( (this == other ) ) return true; if ( (other == null ) ) return false; if ( !(other instanceof SchoolmasterId) ) return false; SchoolmasterId castOther = ( SchoolmasterId ) other; return (this.getId()==castOther.getId()) && (this.getDistrictId()==castOther.getDistrictId()) && (this.getTypeOfSchool()==castOther.getTypeOfSchool()); public int hashCode() int result = 17; result = 37 * result + (int) this.getId(); result = 37 * result + (int) this.getDistrictId(); result = 37 * result + (int) this.getTypeOfSchool(); return result;

在这里,我希望 Id 是自动生成的... 我只添加了

@NaturalId

@GeneratedValue(strategy=GenerationType.SEQUENCE)

我也尝试过使用 GenerationType.AUTO 但没有用。 请提出建议。

【问题讨论】:

我怀疑您没有收到答案,因为它无法完成。我有一个类似的用例(三个中的一个主键字段是自动生成的(PostgreSQL 中的 bigserial))并且发现 @GeneratedValue 只能与 @Id 结合使用。 令人惊讶的是,鉴于我看到的关于它的帖子数量,它无法完成。我把它归结为 hibernate/jpa/eclipselink 开发人员的顽固,他们不想提供一些常见的用例。 “我们知道得更好”的态度之一。 【参考方案1】:

这个问题有一个解决方法。我遇到了同样的情况,有 4 个字段作为复合键,其中 1 个需要按顺序生成。 我根本没有创建嵌入式类,只有 1 个需要按序列生成的 @Id 字段。其余所有字段值将是简单列,因为在 DB 中强制执行参照完整性,并且我正在检查代码中其余 3 个字段的值是否不为空。

如果发生错误,事务将回滚。

【讨论】:

【参考方案2】:

只想添加我的 2c。这适用于复合主键和单个主键。防止创建序列,而是从表中选择 max + 1。

Identifiable.java

package my.app.hibernate;

import java.io.Serializable;

public interface Identifiable<T extends Serializable> 
    T getId();

CompositeKeyEntity.java

 package my.app.hibernate;

 import java.io.Serializable;

 public interface CompositeKeyEntity<T extends Serializable> extends Identifiable<T> 
 

SingleKeyEntity.java

package my.app.hibernate;

import java.io.Serializable;

public interface SingleKeyEntity<T extends Serializable> extends Identifiable<T> 

AssignedIdentityGenerator.java

package my.app.hibernate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.internal.CriteriaImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.util.FieldUtils;

public class AssignedIdentityGenerator extends IdentityGenerator 
    private static final String ID_FIELD_NAME = "id";
    private final Logger LOG = LoggerFactory.getLogger(this.getClass());
    private Field sequenceField;
    private String entityClassName;

    @Override
    public Serializable generate(SessionImplementor session, Object obj) 
        @SuppressWarnings("unchecked")
        Identifiable<Serializable> identifiable = (Identifiable<Serializable>)obj;

        entityClassName = obj.getClass().getName();
        Criteria criteria = new CriteriaImpl(entityClassName, session);
        criteria.setReadOnly(true);
        Object toSet = null;

        if (identifiable instanceof CompositeKeyEntity) 
            Serializable id = identifiable.getId();
            if (id != null) 
                String embaddebleClassName = id.getClass().getName();
                buildCriteriaForEmbeddedId(id, embaddebleClassName, criteria);
                toSet = id;
            
         else if (obj instanceof SingleKeyEntity) 
            toSet = identifiable;
            sequenceField = FieldUtils.getField(identifiable.getClass(), ID_FIELD_NAME);
            buildCriteriaForSingleId(criteria);
        

        Number one = castToSequenceNumberType(1L);
        Number value = (Number) criteria.uniqueResult();

        if(value != null) 
            value = castToSequenceNumberType(value.longValue() + one.longValue());

            setFieldValue(sequenceField, value, toSet);
         else 
            value = one;
            setFieldValue(sequenceField, value, toSet);
        

        return identifiable.getId();
    

    private void buildCriteriaForSingleId(Criteria criteria) 
        criteria.setProjection(Projections.max(ID_FIELD_NAME).as("seq"));
    

    private void buildCriteriaForEmbeddedId(Serializable id, String embaddebleClassName, Criteria criteria) 
        List<Field> fields = Arrays.asList(id.getClass().getDeclaredFields());

        class Utils 
            Field field;
            boolean numberFound = false;
        
        final Utils utils = new Utils();

        for (Field field : fields) 
            if ("serialVersionUID".equals(field.getName()) || "$jacocoData".equals(field.getName())) 
                continue;
            

            if (Number.class.isAssignableFrom(field.getType())) 
                if (utils.numberFound) 
                    throw new IllegalArgumentException(
                            embaddebleClassName + " has more then one sequence field: " + field.getName() + ", "
                                    + utils.field.getName() + ",...");
                

                utils.numberFound = true;
                utils.field = field;
                sequenceField = field;

                criteria.setProjection(Projections.max(ID_FIELD_NAME + "." + sequenceField.getName()).as("seq"));
             else 
                criteria.add(Restrictions.eq(ID_FIELD_NAME + "." + field.getName(), getFieldValue(field, id)));
            
        
    

    private Number castToSequenceNumberType(Number n) 
        return (Number) sequenceField.getType().cast(n);
    

    private void setFieldValue(Field field, Object value, Object to) 
        try 
            field.setAccessible(true);
            field.set(to, value);
         catch (IllegalArgumentException | IllegalAccessException e) 
            LOG.error(e.getMessage(), e);
        
    

    private Object getFieldValue(Field field, Object from) 
        try 
            field.setAccessible(true);
            return field.get(from);
         catch (IllegalArgumentException | IllegalAccessException e) 
            LOG.error(e.getMessage(), e);
        

        return null;
    

客户.java

package my.app.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;

import my.app.hibernate.SingleKeyEntity;

@Entity(name = "whatever_entity_name")
@GenericGenerator(name = "WHATEVER_NAMED_GENERATOR", strategy = "my.app.hibernate.AssignedIdentityGenerator")
public class Customer implements SingleKeyEntity<Long> 

    @Id
    @GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
    private Long id;
    @Column(nullable = false)
    private String name;

CustomerItemsId.java(Item.java 省略,因为它遵循 SingleKeyEntity 示例)

package my.app.entities;

import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Embeddable
public class CustomerItemsId implements Serializable 
    private static final long serialVersionUID = 1L; //generate one

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;
    @ManyToOne
    @JoinColumn(name = "item_id")
    private Item item;
    private Long seq; //name as you wish

CustomerItems.java

package my.app.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;

import my.app.hibernate.CompositeKeyEntity;

@Entity(name = "whatever_entity_name")
@GenericGenerator(name = "WHATEVER_NAMED_GENERATOR", strategy = "my.app.hibernate.AssignedIdentityGenerator")
public class CustomerItems implements CompositeKeyEntity<CustomerItemsId> 

    @GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
    private CustomerItems id;
    @Column(nullable = false)
    private String randomColumn1;
    @Column(nullable = false)
    private String randomColumn2;

【讨论】:

以上是关于JPA @EmbeddedId 未生成序列的主要内容,如果未能解决你的问题,请参考以下文章

如何在 jpa 中为 EmbeddedId 编写选择命名查询?

Embeddable 和 EmbeddedId 之间的 JPA 映射 @ManyToOne

我应该使用哪个注释:@IdClass 或 @EmbeddedId

将 @EmbeddedId 与 JpaRepository 一起使用

JPA中的实体序列生成器

jpa序列ID生成