2017-10-11 16 views
0

다른 열과 떨어져 orderId, locationId 및 sequenceNum 열이있는 테이블이 있습니다. 이 세 개의 열은 기본 키 제약 조건의 일부입니다. orderNum 열은 orderId/locationId 조합에 대해 증가하고 다음 orderId/locationId에 대해 1에서 다시 시작됩니다. 따라서 sequenceNum 생성은 orderId/locationId 조합에 대해 max (sequenceNum) +1을 기반으로해야합니다. JPA @Embeddable 및 @EmbeddedId로 모델링 할 수 있습니까? orderId/locationId 조합에 대해 시퀀스를 다시 1로 재설정하는 방법은 무엇입니까?시퀀스 필드가있는 복합 기본 키 모델링

답변

0

사용자를 최대 절전 모드로 전환하면이 예제를 사용할 수 있습니다. 그렇지 않다면, 사용중인 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; 
    } 
} 
,

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<Long> { 

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

CustomerItemsId.java는

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; 
} 
(Item.java는 SingleKeyEntity 예를 다음과 같이 ommited)