Hibernate saveOrUpdate behavior - java

Does anyone know how Hibernate knows whether to INSERT or to UPDATE a value in the database when session.saveOrUpdate() is called?
So far, I have only determined that it is not dependent on the information in the cache, and that the existence of the entity in the database is determined by the primary key.

When you use .saveOrUpdate() Hibernate will check if the object is transient (it has no identifier property) and if so it will make it persistent by generating it the identifier and assigning it to session. If the object has an identifier already it will perform .update().
From the documentation:
saveOrUpdate() does the following:
if the object is already persistent
in this session, do nothing
if another object associated with the
session has the same identifier,
throw an exception
if the object has no identifier
property, save() it
if the object's identifier has the
value assigned to a newly
instantiated object, save() it
if the object is versioned by a
"version" or "timestamp", and the
version property value is the same
value assigned to a newly
instantiated object, save() it
otherwise update() the object

Perhaps it is helpful to quote the Hibernate bible (Java Persistence with Hibernate, 2nd ed., page 528):
More experienced Hibernate users use saveOrUpdate() exclusively; it's much easier to let Hibernate decide what is new and what is old, especially in a more complex network of objects with mixed state. The only (not really serious) disadvantage of exclusive saveOrUpdate() is that it sometimes can't guess whether an instance is old or new without firing a SELECT at the database - for example, when a class is mapped with a natural composite key and no version or timestamp property.
How does Hibernate detect which instances are old and which are new? A range of options is available. Hibernate assumes that an instance is an unsaved transient instance if:
The identifier property is null.
The version or timestamp property (if it exists) is null.
A new instance of the same persistent class, created by Hibernate internally, has the same database identifier values as the given instance.
You supply an unsaved-value in the mapping document for the class, and the value of the identifier property matches. The unsaved-value attribute is also available for version and timestamp mapping elements.
Entity data with the same identifier value isn't in the second-level cache.
You supply an implementation or org.hibernate.Interceptor and return Boolean.TRUE from Interceptor.isUnsaved() after checking the instance in your code.

As stated here, saveOrUpdate either saves a transient instance by generating a new identifier or updates/reattaches the detached instances associated with its current identifier. More specifically it does:
if the object is already persistent in this session, do nothing
if another object associated with the session has the same identifier, throw an exception
if the object has no identifier property, save() it
if the object's identifier has the value assigned to a newly instantiated object, save() it
if the object is versioned by a <version> or <timestamp>, and the version property value is
the same value assigned to a newly instantiated object, save() it
otherwise update() the object

This is done based on the value of the primary key. If the primary key is undefined, it's value will default to 0 for numeric surrogate keys and save will be performed. If the primary key is filled out, it will invoke an update.

If someone not really understood in theory then there is a code
MyModel sent = myDao.myDaoImpl(id);
if(sent == null){
sent = **new MyModel();** // new Object
sent.setXX(id);
sent.setYY("Yes");
sent.setDate(new Date());
myDao.saveOrUpdate(sent); // Insert will be called
} else if(! "Yes".equalsIgnoreCase(sent.getFlag())) {
sent.setXX("Yes");
sent.setDate(new Date());
myDao.saveOrUpdate(sent); // Update will be called
}

Related

Hibernate #SelectBeforeUpdate(false) not working if without a hibernate generated value of #Id

I found that if the entity mapping without a hibernate generated primary key value, the SelectBeforeUpdate(false) will not working.
#Entity
#SelectBeforeUpdate(false)
class X {
protected X(){}
#Id
#GeneratedValue
UUID id;
int x;
}
If comment out #GeneratedValue, assign id value in X ctor, the #SelectBeforeUpdate(false) will lose its functionality.
Due to hibernate will check a detached object is transient or not by ForeignKeys.isTransient in org\hibernate\engine\internal\ForeignKeys.java which finally call IdentifierValue.isUnsaved in org\hibernate\engine\spi\IdentifierValue.java. Without #GeneratedValue, hibernate use a static UNDEFINED instance of class IdentifierValue which isUnsaved method always return null. That will cause ForeignKeys.isTransient return null so that hibernate will build the snapshot at last which will trigger a selection SQL.
However, the problem is that #GeneratedValue split the object construction to 2 phase: first new it then pass it into session.save. I don't like this, is there a workaround to make #SelectBeforeUpdate(false) works without let hibernate generated the #Id value? I use UUID to be the pk so it is unnecessary let hibernate to do this.
I use hibernate 5
Finally I figure out this mechanism. For hibernate, the #Id field are processed in an uniformed procedure whatever the type is: if you want hibernate generate the id value for you, you must call session.save, even the id value can be determined at the stage of object construction.
This is problematic: if user use UUID to be the primary key, that means user want to determine the value ahead of communicating with database. For some complex logic, after the object A create, the object A need to do some additional things, after that, the object A need along with another object B in an transaction to commit to database. If the object A's id is null till database commit, it is very inconvenient due to object is not a complete object yet.
Hibernate use the uniform id generation procedure as an assumption which used to determine the object is transient or not. If #GeneratedValue is add, hibernate consider object is transient only if the id value is null(This is quite simple, if the id value is not null, then session.save must be called). If without a generator, hibernate can not know the fact, the only way to find out is select it out from database. So it have to omit the #SelectBeforeUpdate(false)
The only way to alter the procedure is self define a Interceptor which override the Interceptor.isTransient, however doing this require user implement the transient checking. If always return false in it, the save and update must be called precisely.

Automatically assigning value to transient attribute

I am using #Entity from javax.persistence.Entity to create my entities and in this particular case I had to create a transient attribute (private Boolean assigned;) and I am using PagingAndSortingRepository from org.springframework.data.repository to create interfaces to manage my database entities.
I need to assign this value (Boolean assigned) when a List (OneToMany) attribute is empty, so I am doing it programatically using an if clause, the problem is I have to write that if clause everywhere and I would like to know if it is possible to auto-assign that variable somehow.
Thanks in advance.
If you need to set the value of this flag before being persisted in the database, I would recommend use entity listener #PrePersist, basicallu give you the possibility to execute certain logic before you persist the object, in the method you could check if the Array is empty and set the value to false.
There are other events such as:
#PostLoad Executed after an entity has been loaded into the current
persistence context or an entity has been refreshed.
#PostLoad
public void setAssigned() {
//Your logic for set to true or false the transient variable.
}
I think this is one option to do the logic in only one place at be managed by the provider. Read more about it here

is it necessary to return a different object from the getter method than the object passed by Hibernate to the setter?

I recently came accross the following statement on Java persistence with Hibernate book.I was able to understand everything else except the highlighted one.
Another issue to consider is dirty checking. Hibernate automatically detects
object state changes in order to synchronize the updated state with the database.
It’s usually safe to return a different object from the getter method than the
object passed by Hibernate to the setter. Hibernate compares the objects by
value—not by object identity—to determine whether the property’s persistent
state needs to be updated. For example, the following getter method doesn’t
result in unnecessary SQL UPDATEs:
public String getFirstname() {
return new String(firstname);
}
Query: My concern here is we are creating new instance. Is that really necessary? kindly correct me if i'm wrong here..
If you will return different object from getter this means you are trying to create a defensive copy.
From hibernate perspective if you return different object from getter that object will have no history with hibernate session, and if you will call save on that object and that object already exist in database you will have ConstraintViolationException, you have to call saveOrUpdate instead. Call to saveOrUpdate will cause hibernate to issue select statement to database before committing.
if some object was already in session and you call commit after performing some changes Hibernate will issue update query

java hibernate eclipse xml

I got the answers from u all regarding the previous topic that was use of hibernate with table not having any primary key, but i have one question that can we use transient keyword prefixed to ID variable in the entity class so that not to persist that value into database???????
No. You must have a persistent ID in all your Hibernate entities. The transient keyword is used to avoid serializing it when transferring the object to another JVM (or to a file). The #Transient annotation is used to mark a field not persistent, but it can't be used for the ID, which is absolutely necessary in order to use Hibernate.
just add transient keyword in declaration
For Example
private transient Image thumbnailImage;
Same answer again.
If the ID is not stored in database, then how it will identify an unique row?
In your case, the prmiary key will be null or empty. If a primary key can be null?
I think you should take any auto increment ID as primary key. It should not affect your database design.

Refreshing entity instance after using merge in hibernate?

am using hibernate merge method, to deal with detached instance from entity, and i thought that the return of this method will be a new fetched instance from database as hibernate saveOrUpdate method, but that wasn't the case, and i think it's logic as it's a detached instance, so is there a better way to return the new instance rather than using findById,
regards,
The merge method copies the state of the passed object to a persistent entity with the same identifier (that is either already associated with the session or will be loaded) and then return a reference to that persistent entity. The object passed as parameter is not attached to the session.
So unless I didn't understand the question, I think you should do something like this:
Foo mergedFoo = session.merge(foo);

Categories

Resources