Why is Hibernate Save updating the records? - java

In hibernate, session.save() is supposed to save the records.It generates "insert" queries. However, I have written below simple program to check this. I am observing the save() can also be used to update the records. It is generating "update" query. Isn't save() doing the same job as saveorupdate() in the below program ?
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
Transaction trans = session.beginTransaction();
Vehicle veh = new Vehicle();
veh.setId(1);
veh.setModel("Veh_mod");
veh.setName("Veh_Name");
Serializable obj = session.save(veh);
veh.setModel("Veh_mod_change");
obj = session.save(veh);
session.flush();
trans.commit();
session.close();
------------------------- in the console--------------------------------
Hibernate:
/* insert com.anvesh.test.Vehicle
*/ insert
into
VEHICLE
(NAME, MODEL, ID)
values
(?, ?, ?)
Hibernate:
/* update
com.anvesh.test.Vehicle */ update
VEHICLE
set
NAME=?,
MODEL=?
where
ID=?

After your first call to save(), object veh becomes an attached object (aka. persistent object state). Subsequently mutating that object with setModel() and committing the transaction would cause hibernate to fire an update even without calling save() a second time.
Here's an example for reference: http://www.dineshonjava.com/p/transient-persistent-and-detached.html#.VEfGCme8G7E
Or perhaps a short video tutorial: http://javabrains.koushik.org/tutorials/hibernate_run/Hibernate-Tutorial-22---Transient,-Persistent-and-Detached-Objects.html

save() can do an update, if id is set on the object it saves. Check out this thread for differences between various saving methods. To quote from an accepted answer
save Persists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity.

When you first called session.save(veh), your object becomes associated with the session.
Hibernate will then know that it needs to use an "UPDATE" query when you save the object again.
Try:
Vehicle veh1 = new Vehicle();
veh1.setId(1);
veh1.setModel("Veh_mod");
veh1.setName("Veh_Name");
Vehicle veh2 = new Vehicle();
veh2.setId(1);
veh2.setModel("Veh_mod");
veh2.setName("Veh_Name");
session1.save(veh1);
session2.save(veh2); // try changing this to session2.saveOrUpdate()
then you'll see the difference between session.save() and session.saveOrUpdate()

Related

Get just added Object with EntityManagerFactory

I'm developing a java app with MySql database, JPA objects and EntityManagerFactory with EclipseLink to manage the database. Everything works Ok but I have an issue.
One of my JPA objects is like this
public class JPAObject1{
#Id
#GeneratedValue
private int id;
#OneToMany(//things here)
List<JPAObject2> list1;
...
}
So the id field will be autogenerated by the EntityManagerFactory when I store it in the database. Asumming em type EntityManager and object type JPAObject1:
em.getTransaction().begin();
em.persist(object);
em.getTransaction().commit();
//house work closing things
The JPAObject1 is added correctly, I can see all fields in my database. As field id is the key to do the find operation, my question is:
Is there a way to get the last added object on the EntityManager on just the moment it is added?
Because I have others objects that use the JPAObject1 id field as a foreign key and I need that field when just the object is added to the database to link the others, but the only way I know to get it is getting all the JPAObjects and getting the last one in the Collection. So, with a few Objects it won't be a problem but if one process insert on database and another do the same before process 1 does the findAll to get the last added, there will be a coherence error....
I think I've explained it well.
Thanks a lot!
you can use this code
Obejct en = new Obejct ();
en.setxxx("My name");
em.persist(en);
em.flush();
System.out.println(en.getId());
the id genrated after flush
Note that the datas saved to database is a set, not list. So they don't have the order or anything like that, and you can't get the last one you've added. If you want to, pls add a column like date, time..., and the query will be like:
" SELECT * FROM Table ORDER BY dateColumn DESC LIMIT 1"

How can I update a row based on id in hibernate

I am new in hibernate. I am using SesssionFactory to get the session of the transaction and one way which I found after searching is used for setting few fields using set parameter i.e
Query query = getCurrentSession().createSQLQuery(
"UPDATE table_name set field1=:f1 where ID=:id");
query.setParameter("f1", f1);
query.setParameter("id", id);
but I want to update the whole row. I have already set the values in the entity class but is there a way to pass the values of the whole entity class to the database based on the id the id is the primary key for the table which I want to update.
you already have all data present in the hibernate entity object? Then just call the session directly:
getCurrentSession().save(myEntity);
to create a new object, or
getCurrentSession().update(myEntity);
to update an existing row.
If not sure, you can use:
getCurrentSession().saveOrUpdate(myEntity);
Take a look at Session#update (or saveOrUpdate). This will allow you to persist a complete, mapped, object to the database.
To be as OO as you can, you can get entity by session.get(entityClass, id);
And then after modifying object by setters/getters, you can save it back to the DB using update method :session.update(entity);

Hibernate updating the record upon calling the setter methods of the bean class

I am new with this language. I have some rows in employee table and the bean class is Employee. I have fetched one record
Employee employee=this.employeeDaoImpl.getEmployeeObject(employeeId);
This is the CONTROLLER
#Transactional
#RequestMapping(value="/revise_payroll")
public String revise_payroll(HttpServletRequest req,HttpServletResponse resp, Model model,RedirectAttributes redirect){
System.out.println("in revise payroll");
String employeeId=req.getParameter("employeeId");
System.out.println("E_ID for revise:"+employeeId);
List<IncrementDecrementPayrollTemp> tempPayrollList=this.employeeDaoImpl.getTemporaryPayroll(employeeId);
//get employee object from session
List<Employee> empList=this.employeeDaoImpl.getCurrentCTC(employeeId);
System.out.println("empList has: "+empList.toString());
Employee employee=this.employeeDaoImpl.getCurrentCTCasObject(employeeId);
System.out.println(("in controller employee hashcode: "+employee.toString()));
int count=0;
// this will run for only one time
for(IncrementDecrementPayrollTemp tempPayroll:tempPayrollList){
employee.setCtc(tempPayroll.getCtct());
employee.setBasicMonthly(tempPayroll.getBasicMonthlyt());
employee.setBasicAnnual(tempPayroll.getBasicAnnualt());
employee.setDaMonthly(tempPayroll.getDaMonthlyt());
employee.setDaAnnual(tempPayroll.getDaAnnualt());
employee.setHouserentMonthly(tempPayroll.getHouserentMonthlyt());
employee.setHouserentAnnual(tempPayroll.getHouserentAnnualt());
employee.setConveyanceMonthly(tempPayroll.getConveyanceMonthlyt());
employee.setConveyanceAnnual(tempPayroll.getConveyanceAnnualt());
employee.setMedicalMonthly(tempPayroll.getMedicalMonthlyt());
employee.setMedicalAnnual(tempPayroll.getMedicalAnnualt());
employee.setSpecialMonthly(tempPayroll.getSpecialMonthlyt());
employee.setSpecialAnnual(tempPayroll.getSpecialAnnualt());
employee.setPfMonthly(tempPayroll.getPfMonthlyt());
employee.setPfAnnual(tempPayroll.getPfAnnualt());
employee.setEsiMonthly(tempPayroll.getEsiMonthlyt());
employee.setEsiAnnual(tempPayroll.getEsiAnnualt());
employee.setMonthlySalary(tempPayroll.getMonthlySalaryt());
}
return new ModelAndView ("IncrementDecrementStatus");
}
Now, when I am just calling the setter methods on employee object, its updating the sql records, in the controller itself. I am not yet in DAO layer using session.save or any update function.
This is DAO Layer
Session session=this.sessionFactory.getCurrentSession();
String p=employeeId.trim();
String hql="From Employee e where e.employeeId=?";
Query query=session.createQuery(hql);
query.setString(0, p);
List<Employee> employeeList=(List<Employee>)query.list();
System.out.println("dao list has "+employeeList.toString());
// to update the existing records
for(Employee emp:employeeList){
int id=emp.getId();
System.out.println("id got: "+id);
Employee empl=(Employee) session.get(Employee.class, id);
String version=empl.getVersion();
System.out.println("version is: "+version);
int intVersion=Integer.valueOf(version);
intVersion=intVersion+1;
version=String.valueOf(intVersion);
empl.setVersion(version);
System.out.println("version and ctc in empl is: "+empl.getVersion()+" , "+empl.getCtc());
System.out.println("hash code in loop: "+empl.toString());
session.update(empl);
}
// this is to save new record
Integer i=(Integer)session.save(sessionEmployee);
System.out.println("save returned: "+i.toString());
}
Things I want to achieve is, I want to update the existing records already in sql table and then save the employee object with some new set of values as a new record. Please suggest me where I am wrong. Thank you!
Let me tell you the lifecycle states of an entity which can make you more clear about this behaviour.
An entity can exist in three states - Transient, Persistent and Detached.
Transient - When you create an object but do not associate it with Hibernate session, then it is in Transient state. Any modifications to such object using setter methods doesn't reflect the change in the database.
Persistent - Here the object is attached to the Hibernate session. So now the Hibernate session manages this object. Any changes made to this object gets reflected in the database. Because Hibernate designed it in such way that, if any modifications is made to a Persistent object, it automatically gets updated in the database, when the session is flushed. (This is Hibernate's capability).
Detached - This state is similar to Transient. The only difference is that an object in detached state was previously in the session(i.e. in persistent state ). But now this is out of the session, because of either closing of the session or calling the evict(Object) method of session.
So coming to your case, once you have loaded the object from database, the object is associated with the session, and thus is in persistent state. As this object is in Persistent state, and you made changes to a Persistent object, the changes are reflected back to database.
Coming to your requirement, (Dividing the problem into parts)
You want to get an existing record from the table - Use Employee empl=(Employee) session.get(Employee.class, id);
Now you want to make changes to this object but not to the database. So use session.evict(empl); to bring the object to detached state. Then after this, you can make modifications to the detached empl object.
Now you want to save this set of new values as a new record. So make sure you change the "id" property of the empl object, as you can't violate unique constraint of the id value. You can't have two records with the same id value in the table.
Don't forget to commit the transaction.
That's normal behaviour. If you load an entity and modify it while it's still managed by the EntityManager, it will propagate all changes back to the database.
You can use evict(employee) to make the bean unmanaged.
Chang performed on any attached entity , hibernate automatically detect and commit to DB. either you can detached loaded entity by evict(entity) or create transient entity by clone of attached entity to use it further in you code.

Hibernate persist() vs save() method

The hibernate document says:
persist():
persist() makes a transient instance persistent. However, it does not guarantee that the identifier value will be assigned to the
persistent instance immediately, the assignment might happen at flush
time. persist() also guarantees that it will not execute an INSERT
statement if it is called outside of transaction boundaries. This is
useful in long-running conversations with an extended
Session/persistence context.
save():
save() does guarantee to return an identifier. If an INSERT has to be executed to get the identifier ( e.g. "identity" generator, not
"sequence"), this INSERT happens immediately, no matter if you are
inside or outside of a transaction. This is problematic in a
long-running conversation with an extended Session/persistence
context.
So I am trying with a small example on how this works. I created an entity called DomesticCat:
#Entity
public class DomesticCat {
#Id
#GeneratedValue
private long id;
private String name;
}
and a small program to test this, once using save() and another time with persist():
private static void saveData() {
Session session = getSession();
DomesticCat cat = new DomesticCat();
cat.setName("My Cat");
//session.save(cat);
session.persist(cat);
}
For this program, hibernate generated same queries for save & persist, in this case it is:
select hibernate_sequence.nextval from dual
Now I added an extra line to my code saying:
session.flush();
Now hibernate generated insert query for both cases i.e save & persist:
insert into CAT (name, id) values (?, ?)
Also when I do session.flush(), the id is getting assigned to my cat object when I use save() and also for persist()
Finally, when I use the transaction then the data is stored in the DB table.
So using this example I can see only single difference between persist vs save, that is save returns the identifier where as persist will not.
Then what exactly the document says, can someone please help me with some examples?
Update:
I am using Oracle as my database.
Now I modified my entity class Id generation strategy to increment as follows:
#Id
#GeneratedValue(generator="increment")
#GenericGenerator(name="increment", strategy = "increment")
private long id;
But even then I can see that calling session.persist() is hitting the DB to get the Id value. Here is my program and its output:
private static void saveData() {
Session session = getSession();
DomesticCat cat = new DomesticCat();
cat.setName("My Cat");
System.out.println("before id="+cat.getId());
session.persist(cat);
System.out.println("after id="+cat.getId());
session.flush();
System.out.println("after flush id="+cat.getId());
}
Output:
before id=0
Hibernate: select max(id) from CAT
after id=1
Hibernate: insert into CAT (name, id) values(?, ?)
after flush id=1
As per the output hibernate is hitting the DB to get the ID before I call session.flush() and the case is same for session.save() also. So there is no difference in output if I use Id generation strategy to increment.
All the information is in the documentation. save() flushes the entity to the database when you make the call. persist() actually just marks the entity to be persisted in the upcoming flush. There is a difference and with persist you have more control on when the actual write to the database takes place.
It's a simple logic problem.
it does not guarantee that the identifier value will be assigned to the persistent instance immediately
is not the same thing as
it guarantees that the identifier value will not be assigned to the persistent instance immediately
The ID generation strategy used for your database is to use a sequence. In that case, Hibernate asks the sequence for the next ID when calling persist(). If the ID generation strategy for your database was to use an auto-increment column, the ID would only be assigned to the entity only when the entity is inserted to the database, at flush time.

batch saveORupdate using hibernate

I have a batch operation where in i have to either insert or update a record.I want to inser larger number of records so i need to commit batch after batch
1)Insert if new
2)Update if existing.
I can typically do it using
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.saveOrUpdat(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
The problem is hibernate generates a select before each saveOrUpdate which seems to be a issue.
The primarykey of object is always populated before passing to hibernate.As it primaryKey is never generated by hibernate using sequencer or anything else.
How can i avoid this exta select for each saveOrupdate?
I dnt want to use stored procedure.
Following are the steps that it takes for Hibernate to decide whether to update or insert a record in to database.
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.
If in any case there is a conflict and hibernate is not able to decide on what operation to perform it does a select.
Coming to your question, try giving an hint to Hibernate like using the fields like timestamp or version
Credits - Jboss HIbernate Docs, StackOverFlow

Categories

Resources