Обойти GeneratedValue в Hibernate (объединить данные не в БД?) - PullRequest
34 голосов
/ 07 июля 2010

Моя проблема такая же, как описано в [1] или [2] . Мне нужно вручную установить автоматически сгенерированное значение ( почему? Импорт старых данных ). Как описано в [1] , использование Hibernate's entity = em.merge(entity) поможет.

К сожалению, для меня это не так. Я не получаю ни ошибки, ни каких-либо других предупреждений. Сущность просто не появится в базе данных . Я использую Spring и Hibernate EntityManager 3.5.3-Final.

Есть идеи?

Ответы [ 8 ]

45 голосов
/ 16 декабря 2011

Еще одна реализация, более простая.

Эта работает с обеими конфигурациями на основе аннотаций или на основе xml: она использует метаданные hibernate для получения значения id для объекта,Замените SequenceGenerator на IdentityGenerator (или любой другой генератор) в зависимости от вашей конфигурации.(Создание декоратора вместо создания подкласса с передачей генератора декорированных идентификаторов в качестве параметра этому генератору оставлено читателю в качестве упражнения).

public class UseExistingOrGenerateIdGenerator extends SequenceGenerator {
    @Override
    public Serializable generate(SessionImplementor session, Object object)
                        throws HibernateException {
        Serializable id = session.getEntityPersister(null, object)
                      .getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    }
}

Ответ на упражнение (используяшаблон декоратора, как и было запрошено), на самом деле не проверено:

public class UseExistingOrGenerateIdGenerator implements IdentifierGenerator, Configurable {

    private IdentifierGenerator defaultGenerator;

    @Override
    public void configure(Type type, Properties params, Dialect d) 
                        throws MappingException;
        // For example: take a class name and create an instance
        this.defaultGenerator = buildGeneratorFromParams(
                params.getProperty("default"));
    }

    @Override
    public Serializable generate(SessionImplementor session, Object object)
                        throws HibernateException {
        Serializable id = session.getEntityPersister(null, object)
                      .getClassMetadata().getIdentifier(object, session);
        return id != null ? id : defaultGenerator.generate(session, object);
    }
}
43 голосов
/ 10 июля 2010

он работает в моем проекте со следующим кодом:

@XmlAttribute
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
@GenericGenerator(name="IdOrGenerated",
                  strategy="....UseIdOrGenerate"
)
@Column(name = "ID", nullable = false)
private Integer id;

и

import org.hibernate.id.IdentityGenerator;
...
public class UseIdOrGenerate extends IdentityGenerator {
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class.getName());

@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
    if (obj == null) throw new HibernateException(new NullPointerException()) ;

    if ((((EntityWithId) obj).getId()) == null) {
        Serializable id = super.generate(session, obj) ;
        return id;
    } else {
        return ((EntityWithId) obj).getId();

    }
}

, где вы в основном определяете свой собственный генератор идентификаторов (на основе стратегии Identity), и еслиидентификатор не установлен, вы делегируете генерацию генератору по умолчанию.

Основной недостаток заключается в том, что он привязывает вас к Hibernate в качестве поставщика JPA ... но он отлично работает с моим проектом MySQL

12 голосов
/ 16 февраля 2018

Обновление ответа Лорана Грегуара для hibernate 5.2, потому что он, кажется, немного изменился.

public class UseExistingIdOtherwiseGenerateUsingIdentity extends IdentityGenerator {

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    }
}

и используйте его так: (замените имя пакета)

@Id
@GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity")
@GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
@Column(unique = true, nullable = false)
protected Integer id;
6 голосов
/ 20 апреля 2017

Я даю решение, которое сработало для меня:
создайте свой собственный идентификатор генератора / генератора последовательностей

public class FilterIdentifierGenerator extends IdentityGenerator implements IdentifierGenerator{

@Override
public Serializable generate(SessionImplementor session, Object object)
        throws HibernateException {
    // TODO Auto-generated method stub
    Serializable id = session.getEntityPersister(null, object)
            .getClassMetadata().getIdentifier(object, session);
    return id != null ? id : super.generate(session, object);
}

}

измените вашу сущность следующим образом:

@Id
@GeneratedValue(generator="myGenerator")
@GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
@Column(unique=true, nullable=false)
private int id;
...

и при сохранениивместо использования persist() используйте merge() или update()

3 голосов
/ 13 августа 2018

Если вы используете hibernate's org.hibernate.id.UUIDGenerator для генерации идентификатора строки, я предлагаю вам использовать:

public class UseIdOrGenerate extends UUIDGenerator {


    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    }
}
2 голосов
/ 29 октября 2010

Для тех, кто хочет это сделать, вышеприведенное работает хорошо.Просто рекомендация получить идентификатор от объекта, а не наследование для каждого класса Entity (только для идентификатора), вы можете сделать что-то вроде:

import org.hibernate.id.IdentityGenerator;

public class UseIdOrGenerate extends IdentityGenerator {

    private static final Logger log = Logger.getLogger(UseIdOrGenerate.class
            .getName());

    @Override
    public Serializable generate(SessionImplementor session, Object object)
            throws HibernateException {
        if (object == null)
            throw new HibernateException(new NullPointerException());

        for (Field field : object.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)
                    && field.isAnnotationPresent(GeneratedValue.class)) {
                boolean isAccessible = field.isAccessible();
                try {
                    field.setAccessible(true);
                    Object obj = field.get(object);
                    field.setAccessible(isAccessible);
                    if (obj != null) {
                        if (Integer.class.isAssignableFrom(obj.getClass())) {
                            if (((Integer) obj) > 0) {
                                return (Serializable) obj;
                            }
                        }
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return super.generate(session, object);
    }
}
2 голосов
/ 07 июля 2010

Согласно Выборочно отключить создание нового потока ID на форумах Hibernate, merge() может быть не решением (по крайней мере, не в одиночку), и вам, возможно, придется использовать генератор (это вторая ссылка, которую вы разместили).

Я сам не тестировал, поэтому не могу подтвердить, но рекомендую прочитать ветку форумов Hibernate.

1 голос
/ 07 июля 2010

Вам нужна текущая транзакция.

Если ваша транзакция управляется вручную:

entityManager.getTransaction().begin();

(конечно, не забудьте зафиксировать)

Если вы используете декларативные транзакции, используйте соответствующую декларацию (с помощью аннотаций, скорее всего)

Кроме того, установите уровень ведения журнала гибернации на debug (log4j.logger.org.hibernate=debug) в ваших свойствах log4j.properties, чтобы отслеживать происходящее более подробно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...