Hibernate second level cache - multiple objects of the same entity - java

I need to ensure that only one object of the same entity is created (as it is done when loading data using first level hibernate cache, to simplify entity refreshing) and I want to use second level cache.
Im testing ehcache but cant make it working. Normally the entities in my app are being nested in other entities but this is just an simple example of usage:
a) example using first level cache which works fine:
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.getTransaction();
transaction.begin();
Person person=session.get(Person.class, 1L);
Person person2=session.get(Person.class, 1L);
transaction.commit();
session.close();
System.out.println(person2 == person);
and it returns true
b) using second level cache with Ehcache
//1. load person with id 1
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.getTransaction();
transaction.begin();
Person person=session.get(Person.class, 1L);
transaction.commit();
session.close();
//2. load the same person
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.getTransaction();
transaction.begin();
Person person2=session.get(Person.class, 1L);
transaction.commit();
session.close();
System.out.println(person2 == person);
and it returns false
Is it normal behaviour of second level cache to be like that or do i miss something?
Does any second level cache engine keep only one instance of the same entity (as first level cache do)?

This is not possible to keep one instance of the entity in RAM in Hibernate using second level cache as each time Hibernate find it in the cache, it just creates new instance based on the cached data.
For my purpose I have implemented AVL Tree based loaded entities and database synchronization engine that creates repositiories based on the loaded entities from hibernate and asynchronously search throught all the fields in entities and rewrites/merge all the same fields (so that if some field (pk) is the same entity like the one in repository, it replaces it)
In this way synchronization with database is easy as it comes to find the externally changed entity in the repository (so basically in the AVL Tree which is O(log n)) and rewrite its fields.

Related

JPA update query without transaction - is transaction mandatory?

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.

Is explicit update required in hibernate?

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.

Hibernate multiple object update in same session

Using Hibernate I need to update multiple objects by calling for eg. updateDetails() function shown below:
function updateDetails(){
Session session = this.getSessionFactory().openSession();
Employee emp=(Employee )session.load(Employee.class, empId);
emp.salary(2000);
Account acc=(Account)session.load(Account.class, accId);
account.setTotal(2000);
Transaction tx=session.beginTransaction();
session.update(emp);
session.update(acc);
tx.commit();
result=true;
session.close();
}
what is the best way of doing this ?
does Updating multiple objects of same or different type in same session will cause any problem in hibernate?
Why not. You can do that. Hibernate allows.
Since you are using Transaction management the answer is depends on your context. If you want to save both Objects regardless of saving another make them update in individual transactions.
If you want to save the whole info successfully and want to revert everything if any of the update fail keep them in the current format.

Configure query cache without second level cache

i want to configure query cache without second level cache for some learning purpose as i have read it can be done but somehow for me query cache works only with 2nd level cache.
Here is my configuration
<property name="cache.use_query_cache">true</property>
Entity class is
#Entity
public class Company {}
but when i do
session = factory.openSession();
Query getQuery=session.createQuery("from Company where companyId=1");
getQuery.setCacheable(true);
Object company2 = getQuery.uniqueResult();
session.close();
session = factory.openSession();
getQuery=session.createQuery("from Company where companyId=1");
getQuery.setCacheable(true);
company2 = getQuery.uniqueResult();
session.close();
Two separate queries are fired .
As soon as i do
#Entity
#Cacheable
#Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class Company {}
Query cache work but it enables the second level cache also. So how to configure query
cache without enabling second level cache?
Query cache needs the 2nd level cache to do its job, since the results of the queries are then retrieved from the 2nd level cache. The query cache just keeps track of a query and the set of ids that the query returned. These ids are then retrieved from 2nd level cache from further speed increase. Where did you read you could have query cache without 2nd level cache?

Querying Hibernate cache

I have the following code:
Person a = new Person();
a.setName("John");
Session session = openHibernateSession();
session.beginTransaction();
session.saveOrUpdate(a);
Criteria critera = session.createCriteria(Person.class);
critera.add(Restrictions.eq("name","John"));
Person personFromCache = (Person) criteria.uniqueResult();
...
session.commit();
What I want is to have the ability to search objects from both the database and Hibernate's cache. The following example returns null upon calling uniqueResult. Is there any way to retrieve saved objects that have not yet been committed to the database?
If you are searching other than ID then Hibernate will not use first level cache. Hibernate get and load is related to first level cache by default but criteria query is not. In your case there are two solution from my side
By flushing session = Just flush your session like this session.flush(); while doing so data from session will be synchronize to database hence Id will ge generated and as result Criteria query will find the result in database and will result list to you.
Enable hibernate second level cache = You can enable second level cache by hibernate cache providers like ehCache and apply the trick.
You can use the StatelessSession but be warned:
those entitys are not bound to any session and you will get Exceptions if you like to resolve relations or lazy fields!
session.beginTransaction();
session.saveOrUpdate(a);
session.flush();
Criteria critera = session.createCriteria(Person.class);
critera.add(Restrictions.eq("name","John"));
Person personFromCache = (Person) criteria.uniqueResult();
We do some similar things except using TestNg test framework. Several of the answers discuss the session.flush() method call. This is correct. The call to flush tells Hibernate to do several things, including making sure that all database calls currently waiting in the queue get executed and cleared from the queue.
It returns data even if you are selecting on the basis of username. It is not returning null.

Categories