Bypass GeneratedValue in Hibernate (unisce i dati non in db?)

Il mio problema è lo stesso descritto in [1] o [2] . Devo impostare manualmente un valore predefinito generato automaticamente ( perché? Importando i vecchi dati ). Come descritto in [1] usando l’ entity = em.merge(entity) di Hibernate entity = em.merge(entity) farà il trucco.

Sfortunatamente per me no. Non ricevo né un errore né altri avvisi. L’ quadro non apparirà nel database . Sto usando Spring e Hibernate EntityManager 3.5.3-Final.

Qualche idea?

funziona sul mio progetto con il seguente codice:

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

e

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

dove fondamentalmente si definisce il proprio generatore di ID (basato sulla strategia Identity) e se l’ID non è impostato, si delegherà la generazione al generatore predefinito.

Lo svantaggio principale è che ti lega a Hibernate come provider JPA … ma funziona perfettamente con il mio progetto MySQL

Un’altra implementazione, molto più semplice.

Questo funziona con la configurazione basata su annotazioni o su XML: fa affidamento sui metadati in ibernazione per ottenere il valore id per l’object. Sostituisci SequenceGenerator di IdentityGenerator (o qualsiasi altro generatore) a seconda della configurazione. (La creazione di un decoratore invece di una sottoclass, passando il generatore di ID decorato come parametro per questo generatore, è lasciato come esercizio al lettore).

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

Risposta all’esercizio (utilizzando un modello decoratore, come richiesto), non realmente testato:

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

Aggiornamento della risposta di Laurent Grégoire per hibernate 5.2 perché sembra essere cambiato un po ‘.

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

e usalo in questo modo: (sostituisci il nome del pacchetto)

 @Id @GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity") @GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity") @Column(unique = true, nullable = false) protected Integer id; 

Sto dando una soluzione qui che ha funzionato per me:
crea il tuo identificatore / generatore di sequenze

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

modifica la tua quadro come:

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

e mentre si salva invece di usare persist() usa merge() o update()

Secondo la generazione selettivamente disabilitata di un nuovo thread ID sui forum di Hibernate, l’ merge() potrebbe non essere la soluzione (almeno non solo) e potrebbe essere necessario utilizzare un generatore personalizzato (che è il secondo link che hai postato).

Non l’ho testato personalmente, quindi non posso confermare ma consiglio di leggere il thread dei forum di Hibernate.

Per chiunque altro stia cercando di farlo, sopra funziona bene. Solo una raccomandazione per ottenere l’identificatore dall’object piuttosto che avere un’eredità per ogni class Entity (Solo per l’Id), potresti fare qualcosa come:

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

Hai bisogno di una transazione in corso.

Nel caso in cui la transazione sia gestita manualmente:

 entityManager.getTransaction().begin(); 

(ovviamente non dimenticare di impegnarsi)

Se si utilizzano transazioni dichiarative, utilizzare la dichiarazione appropriata (tramite annotazioni, molto probabilmente)

Inoltre, imposta il livello di registrazione di ibernazione per debug ( log4j.logger.org.hibernate=debug ) nel tuo log4j.properties per tracciare ciò che sta accadendo in maggiori dettagli.

Se utilizzi org.hibernate.id.UUIDGenerator ‘s org.hibernate.id.UUIDGenerator per generare un ID stringa, ti suggerisco di utilizzare:

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