Bypass Generatedvalue in Hibernate (Merge Data Not in Db)

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

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

Bypass GeneratedValue in Hibernate

I know you can do this in the JPA spec, so you should be able to in Hibernate (using JPA+ annotations).

If you just fill in the ID field of the new persistent model you're creating, then when you "Merge" that model into the EntityManager, it will use the ID you've set.

This does have ramifications, though. You've just used up that ID, but the sequence specified by the GeneratedValue annotation doesn't know that. Unless you're specifying an ununsed ID that's LESS than the current sequence value, you're going to get a problem once the sequence catches up to the value you just used.

So, maybe I can see where you might want the user to be able to specify an ID, but then you need to catch the possible Exception (duplicate ID) that may come in the future.

Is it possible to remove an @GeneratedValue field annotation applied to an @Id column in @MappedSuperclass in a subclass?

Edit: Working answer: Ok, now I see that there is no way to do this in pure JPA-way, but if you're using Hibernate you might try to write custom SequenceGenerator as it is described here. Basically, it will generate a value (call to super.generate()) only when the id is not set.

Can I override generated IDs?

My thought was to just delete the old ones and re-create it from the incoming XML. However, doing a persist when my entities having existing IDs seem to make Hibernate very angry..

Indeed, you cannot assign an Id when it is supposed to be generated, at least not with Hibernate that won't consider the entity as new but as detached (the JPA specification is a bit blurry on the exact rules in this case but that's how Hibernate behaves, see 5.1.4.5. Assigned identifiers for more hints).

So is this actually possible or is it better to avoid deleting them and just trying to do it with merge?

To make the delete/insert possible for the web service use case, you'd have to either:

  • not assign the id ~or~
  • use a special version of the entity without a generated identifier ~or~
  • use bulk operations(?)

The alternative if you're actually updating detached entities would be indeed to use a merge (but have a look at these previous questions just in case).

Which approach is better? I don't know, it think it depends on your needs. The later seems more natural if you're updating existing entities. With the former, you'd really get "new" entities (including a new value for the optimistic locking column). Depending on the exact implementation of the process, performances might also vary. And, by the way, what about concurrency (just to mention it, I'm not really expecting an answer)?

JPA Hibernate - Insert with given ID, not with sequence

Maybe this is not the most elegant solution I could end up with, but it solved my problem for now.

I created a custom sequence generator as follows:

public class CustomSequenceGenerator extends SequenceStyleGenerator implements Configurable {

private String sequenceCallSyntax;

@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
sequenceCallSyntax = "SELECT nextval('" + params.getProperty("sequence_name") + "')";
super.configure(type, params, serviceRegistry);
}

@Override
public Serializable generate(SessionImplementor s, Object obj) {
Serializable id = s.getEntityPersister(null, obj).getClassMetadata().getIdentifier(obj, s);

if (id != null && Integer.valueOf(id.toString()) > 0) {
return id;
} else {
Integer seqValue = ((Number) Session.class.cast(s)
.createSQLQuery(sequenceCallSyntax)
.uniqueResult()).intValue();
return seqValue;
}
}

}

And my entity got like this:

@Entity
@Table(name = "usuario", schema = "adm")
@GenericGenerator(name = "CustomSequenceGenerator",
strategy = "<....my_package...>.CustomSequenceGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "adm.seq_usuario")
})
public class UsuarioEntity implements java.io.Serializable {

@Id
@GeneratedValue(generator = "CustomSequenceGenerator", strategy = GenerationType.SEQUENCE)
@Column(name = "id_usuario", unique = true, nullable = false)
private int id;

@Column(name = "name", length = 50)
private String name;

@Column(name = "active")
private Boolean active;

// getter and setter ommited
....
}

This way, when I save an entity with ID as null on my service, the generator returns the nextval from my sequence, and if I set an ID that was given by the central server it gets inserted in my table correctly.

If there is some more elegant solution, please let me know to update this answer.

@Id @GeneratedValue but set own ID value

It may be an overkill but have you thought about writing your own CustomIDGenerator which probably subclasses say the AutoGenerator of hibernate and exposes a couple of methods where you can set the id of the next class object to be generated so for example

class MyGenerator extends .... {

public void setIdForObject(Class clazz, Long id) {
//once you use this API, the next time an object of
//type clazz is saved the id is used
}

public void setIdForObject(Class clazz, Long id, Matcher matcher) {
//once you use this API, the next time an object of
//type clazz is saved and the matcher matches yes the id will be
//assigned. Your matcher can match properties like name, age etc
//to say the matched object
}
}

This could get complicated but at the least is possible as per hibernate doco



Related Topics



Leave a reply



Submit