【问题标题】:Modeling composite primary key with sequence field使用序列字段建模复合主键
【发布时间】:2017-10-11 20:28:03
【问题描述】:

我有一个表,其中除了其他列之外,还有 orderId、locationId 和 sequenceNum 列。这三列是主键约束的一部分。对于 orderId/locationId 组合,sequenceNum 列递增,并从 1 重新开始以获取下一个 orderId/locationId。所以 sequenceNum 的生成应该基于 max(sequenceNum)+1 的 orderId/locationId 组合。这可以用 JPA @Embeddable 和 @EmbeddedId 建模吗?如何将 orderId/locationId 组合的序列重置回 1?

【问题讨论】:

    标签: java jpa openjpa


    【解决方案1】:

    如果你使用 hibernate,你可以使用这个例子。如果没有,在你正在使用的 API 上找到序列生成器,并使用相同的逻辑。

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-02
      • 1970-01-01
      • 2013-10-04
      • 2017-07-22
      • 2018-11-07
      • 2019-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多