JPA @EmbeddedId is not generating sequence

前端 未结 2 874
清酒与你
清酒与你 2021-01-05 07:39

I have a table which has composite primary key consisting one sequence and two foreign keys I am able to persist My entity class but it is not generating according to the se

2条回答
  •  借酒劲吻你
    2021-01-05 08:23

    Just wanna to add my 2c. This works with composite and single primary keys. Prevent for creating sequences, instead, select max + 1 from the table.

    Identifiable.java

    package my.app.hibernate;
    
    import java.io.Serializable;
    
    public interface Identifiable {
        T getId();
    }
    

    CompositeKeyEntity.java

     package my.app.hibernate;
    
     import java.io.Serializable;
    
     public interface CompositeKeyEntity extends Identifiable {
     }
    

    SingleKeyEntity.java

    package my.app.hibernate;
    
    import java.io.Serializable;
    
    public interface SingleKeyEntity extends Identifiable {
    }
    

    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 identifiable = (Identifiable)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 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;
        }
    }
    

    Customer.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 {
    
        @Id
        @GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
        private Long id;
        @Column(nullable = false)
        private String name;
    }
    

    CustomerItemsId.java (Item.java ommited as it follows SingleKeyEntity example)

    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 {
    
        @GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
        private CustomerItems id;
        @Column(nullable = false)
        private String randomColumn1;
        @Column(nullable = false)
        private String randomColumn2;
    }
    

提交回复
热议问题