Set timeout based on query - java

How to set up a timeout for some specific queries using Java or Hibernate. The problem I am facing is that some queries where I have to fetch large amount of data some times do not respond for longer periods resulting in an unresponsive application.

Hibernate query has setTimeout() method
Query query = session.createQuery(someQueryString);
query.setTimeout(theTimeOut);

JPA standard property javax.persistence.query.timeout can also be provided as a "hint" to each query.
query.setHint("javax.persistence.query.timeout", someTimeout);
Hence avoiding tying your code to one JPA provider or another.

Hibernate supports Transaction time outs:
//set transaction timeout to 5 seconds at session
Session sess = factory.openSession();
sess.getTransaction().setTimeout(5);
You can also setup at query level.
Query q = sess.createQuery("from....")
.setTimeOut(3);

Related

JPA auto flush before any query

From JPA documentation I can see that the AUTO is the default flush mode, flush should happen before any query execution. I have tried this on spring boot jpa and I can see the flush won't happen on queries from different entities , is that expected ? even though different entity may have relation to it ( Department <--> Person here )
The flush should trigger before any query according to this article :
https://vladmihalcea.com/how-do-jpa-and-hibernate-define-the-auto-flush-mode/
// this triggers flush //
Person person = personRepository.findById(5L).get();
person.setName("hello test");
Person person1 = (Person) entityManager.createQuery("select person from Person
person where person.id=11").getSingleResult(); // flush before query
// this doesn't trigger flush even if the department has the person //
Person person = personRepository.findById(5L).get();
person.setName("hello test");
Department department= (Department) entityManager.createQuery("select
department from Department
department where department.id=1").getSingleResult();
Update:
I noticed the flush happens for JPQL queries on the same table only that has the DML , while for native sql queries it will always flush before any query if there is DML before. even though no flush happens the JPQL return the managed entity with modification not the one in DB. can anyone please explain if this follow JPA standard or not ?
As JPA is a specification this question is simple to answer. Check out the spec :-)
3.10.8 Queries and Flush Mode
The flush mode setting affects the result of a query as follows.
When queries are executed within a transaction, if FlushModeType.AUTO is set on the Query, TypedQuery, or StoredProcedureQuery object, or if the flush mode setting for the persistence context is AUTO (the default) and a flush mode setting has not been specified for the query object, the persistence provider is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the
query. The persistence provider implementation may achieve this by flushing those entities to the database or by some other means. If FlushModeType.COMMIT is set, the effect of updates made to entities in the persistence context upon queries is unspecified.
If the persistence context has not been joined to the current transaction, the persistence provider must
not flush to the database regardless of the flush mode setting.
package javax.persistence;
public enum FlushModeType {
COMMIT,
AUTO
}
If there is no transaction active, the persistence provider must not flush to the database
https://download.oracle.com/otn-pub/jcp/persistence-2_1-fr-eval-spec/JavaPersistence.pdf?AuthParam=1561799350_4cc62583442da694a6a033af82faf986
Then there is the Hibernate Doc:
6.1. AUTO flush
By default, Hibernate uses the AUTO flush mode which triggers a flush in the following circumstances:
prior to committing a Transaction
prior to executing a JPQL/HQL query that overlaps with the queued entity actions
before executing any native SQL query that has no registered synchronization
https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#flushing

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.

hibernate returning null for auto-generated timestamps from mysql

i have a table in mysql which has a data type of timestamp as one of the columns, which gets a default value of CURRENT_TIME upon insertion. and i have another timestamp column that has a default value of CURRENT_TIME upon update. i have these so that timestamp columns will get updated automatically on insertion and update (which works fine).
now i am using cxf, hibernate/jpa, mysql, jackson to build a web service.
i am simply creating a new record and retrieving it right away as below code shows.
Session session = getSession(); // sessionFactory.getCurrentSession();
String accountId = (String)session.save(account);
Account newAccount = (Account)session.load(Account.class, accountId);
logger.info("created timestamp=" + newAccount.getCreatedTimestamp());
after above code is ran, i can see that new record is created in mysql with correct timestamps for createdTimestamp. however, logger.info() line above throws an exception because newAccount.getCreatedTimestamp() returns null. if i remove logger.info() line, i can see that newAccount object is populated with correct values except for createdTimestamp which is null.
what's more odd is that after above code is ran (which is a part of HTTP POST operation), i call a HTTP GET service which just fetches a record that i just inserted by doing
session.get(Account.class, accountId);
and it correctly shows timestamps!
i tried to sleep before session.load() or session.get() thinking that there might be a delay in inserting timestamp, but that didn't do much. is there something special about hibernate session management that does not retrieve columns that mysql generates? what am i missing here? please help.
Your actual save isn't being committed until the session is flushed. Hibernate doesn't actually commit anything to the database until the session is flushed or closed so that if an exception is thrown, a rollback doesn't actually have to touch the physical database, the changes are just not sent. However if Hibernate detects that a query is going to receive stale data, it will automatically flush before running that query.
For example, you add a record to the database and immediately call a SELECT COUNT(*) query. Hibernate will flush the session (committing the record in the process) and then perform the SELECT COUNT(*) query on the now clean session ensuring that you get correct data. Hibernate didn't do this in your case because it saw that you were requesting the same object that you were trying to insert (in the same session) so it just returned you that reference.
If you are letting hibernate manage its sessions (using a session factory or similar) I don't think that you have to explicitly close sessions. I know that I don't, but I'm using Hibernate with Spring, and using the #Transactional annotation which manages the actual Hibernate session. If you want an immediate insert, make your call to save() the last call in the method. Usually, once the method exits, a commit() will be called automatically.
All the load() will be doing is giving you the same instance of Account that you passed into session.save(). Either close or flush the session, then try the load() again, and your value should be set.

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.

Why does MyBatis close sessions after executing every statement?

I'm using MyBatis on Spring 3. Now I'm trying to execute two following queries consequently,
SELECT SQL_CALC_FOUND_ROWS() *
FROM media m, contract_url_${contract_id} c
WHERE m.media_id = c.media_id AND
m.media_id = ${media_id}
LIMIT ${offset}, ${limit}
SELECT FOUND_ROWS()
so that I can retrieve the total rows of the first query without executing count(*) additionally.
However, the second one always returns 1, so I opened the log, and found out that the SqlSessionDaoSupport class opens a connection for the first query, and closes it (stupidly), and opens a new connection for the second.
How can I fix this?
I am not sure my answer will be 100% accurate since I have no experience with MyBatis but it sounds like your problem is not exactly related to this framework.
In general, if you don't specify transaction boundaries somehow, each call to spring ORM or JDBC api will execute in a connection retrieved for this call from dataSource/connectionPool.
You can either use transactions to make sure you stay with the same connection or manage connection manually. I recommend the former which is how spring db apis are meant to be used.
hope this helps
#Resource
public void setSqlSessionFactory(DefaultSqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
SqlSession sqlSession = sqlSessionFactory.openSession();
YourMapper ym = sqlSession.getMapper(YourMapper.class);
ym.getSqlCalcFoundRows();
Integer count = pm.getFoundRows();
sqlSession.commit();
sqlSession.close();

Categories