JPA @EmbeddedId не генерирует последовательность

у меня есть таблица, которая имеет составной первичный ключ, состоящий одной последовательности и двух внешних ключей Я могу сохранить свой класс сущности, но он не генерируется в соответствии с последовательностью. Таблица, имеющая составной первичный ключ, состоящий из одной последовательности и двух внешних ключей, hbm2java в maven дает следующие сущности

вот основная сущность


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.АВТОМАТИЧЕСКИЙ но не получилось. Пожалуйста, предложите.

2 ответов


просто хочу добавить свой 2c. Это работает с составными и одиночными первичными ключами. Запретить для создания последовательностей вместо этого выберите max + 1 из таблицы.

идентифицировать.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 ommited, как следует из 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;
}

есть один обходной путь для этой проблемы. Я столкнулся с тем же условием, имею 4 поля в качестве составных ключей, из которых 1 должен быть сгенерирован последовательностью. Я вообще не создавал встроенный класс, только поле 1 @Id, которое должно быть сгенерировано последовательностью. Rest все значения полей будут простыми столбцами, поскольку ссылочная целостность применяется в DB, а также я проверяю значения остальных 3 полей в коде, чтобы не быть нулевыми.

в случае ошибки транзакции отмена.