Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bypass GeneratedValue in Hibernate (merge data not in db?)

My problem is the same as described in [1] or [2]. I need to manually set a by default auto-generated value (why? importing old data). As described in [1] using Hibernate's entity = em.merge(entity) will do the trick.

Unfortunately for me it does not. I neither get an error nor any other warning. The entity is just not going to appear in the database. I'm using Spring and Hibernate EntityManager 3.5.3-Final.

Any ideas?

like image 770
Jan Avatar asked Jul 07 '10 12:07

Jan


People also ask

How does merge work in Hibernate?

So the merge method does exactly that: finds an entity instance by id taken from the passed object (either an existing entity instance from the persistence context is retrieved, or a new instance loaded from the database) copies fields from the passed object to this instance. returns a newly updated instance.

What is Generationtype in Hibernate?

SEQUENCE is the generation type recommended by the Hibernate documentation. The generated values are unique per sequence. If we don't specify a sequence name, Hibernate will reuse the same hibernate_sequence for different types.

What is JPA @GeneratedValue?

persistence. GeneratedValueJPA annotationProvides for the specification of generation strategies for the values of primary keys. See JavaDoc Reference Page... annotation specifies that a value will be automatically generated for that field.

How would you implement a custom sequence based id generator?

If you want to use a custom generator, you need to define the generator in a @GenericGenerator annotation and provide the fully-qualified classname as the strategy. You can also configure a set of parameters that will be provided to the configure method when Hibernate instantiates the generator.


2 Answers

Another implementation, way simpler.

This one works with both annotation-based or xml-based configuration: it rely on hibernate meta-data to get the id value for the object. Replace SequenceGenerator by IdentityGenerator (or any other generator) depending on your configuration. (The creation of a decorator instead of subclassing, passing the decorated ID generator as a parameter to this generator, is left as an exercise to the reader).

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

Answer to the exercise (using a decorator pattern, as requested), not really tested:

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);     } } 
like image 110
Laurent Grégoire Avatar answered Sep 27 '22 18:09

Laurent Grégoire


it works on my project with the following code:

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

and

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

where you basically define your own ID generator (based on the Identity strategy), and if the ID is not set, you delegate the generation to the default generator.

The main drawback is that it bounds you to Hibernate as JPA provider ... but it works perfectly with my MySQL project

like image 31
Kevin Avatar answered Sep 27 '22 17:09

Kevin