I have the below code which tries to save the entity outside the transaction:
Session session = HibernateUtil.buildSessionFactory().openSession();
Teacher t= new Teacher();
t.setName("jonathan");
session.save(t);
session.flush();
According to the definition of save() if used outside the transaction we need to do flush() to save the entity in the db.However the above code does not save it. I have to create a transaction and commit it in order to add a row in db. Why is this so?
I am using HIbernate 4.3.6
EDIT: I just noticed that the session.save() returns the id but only after transaction.commit() the row is added.The other question does not answer my question.Since this basically means that save() in order to add the row in db has to be in a transaction only
Related
I'm trying to do insert via a native query with JPA, but I don't want to create a transaction:
Query query = em.createNativeQuery("INSERT INTO person (id, firstname, lastname) VALUES ('1','Ronnie','Dio')");
int count = query.executeUpdate();
this ends up with the TransactionRequiredException:
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:99)
However, if I unwrap the Hibernate session and execute the same query - it works:
Session session = em.unwrap(Session.class);
Query query = session.createSQLQuery("INSERT INTO person (id, firstname, lastname) VALUES ('1','Ronnie','Dio')");
int count = query.executeUpdate();
So my question is - what am I doing wrong in the first sample? Is transaction really required to execute insert/update/delete queries in JPA?
If it is, is there any link to documentation that specifies it clearly? If it's not - what am I doing wrong?
It seems you are building an application that does not run inside a container supporting JTA managed transactions. In such an environment, you have to handle/manage transactions for yourself, i.e., you have to control when transactions are opened, committed or rolled back. This scenario is referred to as resource-local entity manager.
In section 7.5.2 Resource-local EntityManagers of the official JPA 2.2 specification (p. 345) we find:
An entity manager whose transactions are controlled by the application through the EntityTransaction
API is a resource-local entity manager. A resource-local entity manager transaction is mapped
to a resource transaction over the resource by the persistence provider. Resource-local entity managers
may use server or local resources to connect to the database and are unaware of the presence of JTA
transactions that may or may not be active
Further down in the spec document the EntityTransaction interface is given. It allows you to call
begin() to "Start a resource transaction"
commit() to "Commit the current resource transaction, writing any
unflushed changes to the database."
rollback() to "Roll back the current resource transaction." in case something went wrong on the database side while committing changes.
That's for the theory part.
For your code example, you might want to change it as follows:
EntityTransaction tx = null;
try {
tx = em.getTransaction();
// start a new transaction, i.e. gather changes from here on...
tx.begin();
// do your changes here
Query query = em.createNativeQuery("INSERT INTO person (id, firstname, lastname) VALUES ('1','Ronnie','Dio')");
int count = query.executeUpdate();
// write changes to database via commit
tx.commit();
} catch(RuntimeException re) {
if(tx != null && tx.isActive()) {
// ensure no semi-correct changes are pending => cleanup
tx.rollback();
}
// handle exception, log it somewhere...
}
This should avoid the TransactionRequiredException you encounter. Moreover, you should avoid the use createNativeQuery, as you are mis-using a fundamental concept of an Object-Relational-Mapper (ORM), i.e. the mapper will transform objects into tuples and vice versa for you. This - in general - should ease the pain of writing insert/update queries for a large amount of domain entities.
Have a look into section 3.1.1 EntityManager Interface (p. 65) of the spec document linked above and make use of the methods
persist(..) - "Make an instance managed and persistent." or
merge(..) - "Merge the state of the given entity into the current persistence context."
For more infos on the difference of both approaches see the posts here and here.
Hope it helps.
Instead of creating a native query , I would recommend to create a JPA entity for the person and with JPARepository you can use a save method for the person to insert any record.
I have a Transactional method, inside it an entity is instantiated and inserted in the Hibernate context using persist method. Then some properties of the entity are changed (so it will be reflected in the database). What will happen if detach method is called on the entity and then some properties of the entity are changed. When the method finishes (and transaction commits), will Hibernate insert the entity and update the attributes to the point before the detach call?
For example:
#Transactional
public void transactionalMethod(){
MyEntity entity = new MyEntity();
getEntityManager().persist(entity);
entity.setAttribute1(data1);
entity.setAttribute2(data2);
getEntityManager().detach(entity);
entity.setAttribute3(data3);
}
Well, I'm answearing my own question for people having a similar question.
When using Spring Transactional method, all the inserts will be commited at the end of the transaction. If you use detach on an entity, all the updates (before or after detach) will be discarted, so no commits of updates will be done when transaction finishes.
If you want to reattach an entity to Persistence Context, you can do a merge or update method on the entity, then all changes to the entity will be commited when transaction finishes.
Hope this helps.
I have a method to delete record by hibernate. I add a query after deletion. And the record deleted is still in the result list. It should not be. Here is the code.
#Transactional
public void deleteFoo(long fooId){
Foo foo = fooDao.find(fooId);
fooDao.delete(foor);
List<Foo> brothers = fooDao.findByParentId(foo.getParentId());
// I think the brothers does not contains foo.
// Unfortunately, foo is still in the list.
// I do not know why and how to make sure the brothers does not contains foo.
...
}
The record is deleted from database where the transaction committed.
You should flush the hibernate session.
When your work with database via Hibernate you are using Hibernate session.
Hibernate sessions flushed to the database by following three situations.
commit()- When you commit a transaction
Before you run a query
When you call session.flush()
or you can flush it manually by
session.flush()
In most of the hibernate applications i see the explicit update statement to update any hibernate entity in db
Session session1 = factory.openSession();
Transaction tx1 = session1.beginTransaction();
College college= (College)session1.get(College.class, 1);
college.setCollegeName("College_update");
session1.saveOrUpdate(college); // at line 1
tx1.commit();
session1.close();
But even i miss statement 1 , entity is updated in DB. My question is it a good practice to mention explicit update statement
when it is not required even ?
No, the saveOrUpdate statement is not required. The College entity is attached to your Hibernate session because is was instantiated by Hibernate. Any mutations to the College entity will be UPDATEd to the database when that session is flushed.
Use saveOrUpdate when you need to attach an existing (detached) entity to a hibernate session. Any subsequent mutations to that entity made in the scope of that session will be persisted.
Update is required to take data from one session or transaction and save it into the database in another transaction.
A a = session1.load(......);
// Commit transaction
//Start New session ......
//Start transaction
session2.update (a);
If you read an entity change its properties within the same session then the results will get saved when you flush the session.
I am using hibernate. i am deleting record using delete method as below.
Session session = sessionFactory.getCurrentSession();
session.delete(pojotobedeleted);
my requirement is after deleting the record, it should return the same deleted record. How can i get the deleted record?
Thanks!
I'd say
Session session = sessionFactory.getCurrentSession();
pojotobedeleted = session.load(Pojo.class, id); /* not necessary if loaded before */
session.delete(pojotobedeleted);
return pojotobedeleted;
Sure there is an extra select statement for doing so, but that is the same with native SQL. The SQL delete does not return the deleted rows, and hibernate can't be better that native SQL.
(If this is not what you wants then I don't understand your question: What is the goal, why not like in my example?)