在embeddable / embedded-id中使用hibernate Sequence生成器

时间:2016-02-17 17:20:34

标签: hibernate jpa orm embeddable

在Hibernate中,是否可以将@GeneratedValue和@SequenceGenerator与@EmbeddedId或@Embeddable一起使用?

我需要一个用于嵌入式id(或@embeddable)的字符串序列生成器。

1 个答案:

答案 0 :(得分:0)

我在我的项目中使用它并且它到目前为止非常令人满意,没有序列。它使用"选择max(id)+ 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;
    }
}

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