JPA auto flush before any query - java

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

Related

Spring JPA always caches data [duplicate]

This question already has answers here:
Spring Data JPA Update #Query not updating?
(5 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Let's suppose to have this situation:
We have Spring Data configured in the standard way, there is a Respository object, an Entity object and all works well.
Now for some complex motivations I have to use EntityManager (or JdbcTemplate, whatever is at a lower level than Spring Data) directly to update the table associated to my Entity, with a native SQL query. So, I'm not using Entity object, but simply doing a database update manually on the table I use as entity (it's more correct to say the table from which I get values, see next rows).
The reason is that I had to bind my spring-data Entity to a MySQL view that makes UNION of multiple tables, not directly to the table I need to update.
What happens is:
In a functional test, I call the "manual" update method (on table from which the MySQL view is created) as previously described (through entity-manager) and if I make a simple Respository.findOne(objectId), I get the old object (not updated one). I have to call Entitymanager.refresh(object) to get the updated object.
Why?
Is there a way to "synchronize" (out of the box) objects (or force some refresh) in spring-data? Or am I asking for a miracle?
I'm not ironical, but maybe I'm not so expert, maybe (or probably) is my ignorance. If so please explain me why and (if you want) share some advanced knowledge about this amazing framework.
If I make a simple Respository.findOne(objectId) I get old object (not
updated one). I've to call Entitymanager.refresh(object) to get
updated object.
Why?
The first-level cache is active for the duration of a session. Any object entity previously retrieved in the context of a session will be retrieved from the first-level cache unless there is reason to go back to the database.
Is there a reason to go back to the database after your SQL update? Well, as the book Pro JPA 2 notes (p199) regarding bulk update statements (either via JPQL or SQL):
The first issue for developers to consider when using these [bulk update] statements
is that the persistence context is not updated to reflect the results
of the operation. Bulk operations are issued as SQL against the
database, bypassing the in-memory structures of the persistence
context.
which is what you are seeing. That is why you need to call refresh to force the entity to be reloaded from the database as the persistence context is not aware of any potential modifications.
The book also notes the following about using Native SQL statements (rather than JPQL bulk update):
■ CAUTION Native SQL update and delete operations should not be
executed on tables mapped by an entity. The JP QL operations tell the
provider what cached entity state must be invalidated in order to
remain consistent with the database. Native SQL operations bypass such
checks and can quickly lead to situations where the inmemory cache is
out of date with respect to the database.
Essentially then, should you have a 2nd level cache configured then updating any entity currently in the cache via a native SQL statement is likely to result in stale data in the cache.
In Spring Boot JpaRepository:
If our modifying query changes entities contained in the persistence context, then this context becomes outdated.
In order to fetch the entities from the database with latest record.
Use #Modifying(clearAutomatically = true)
#Modifying annotation has clearAutomatically attribute which defines whether it should clear the underlying persistence context after executing the modifying query.
Example:
#Modifying(clearAutomatically = true)
#Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
int expireNetwork(#Param("network_id") Integer network_id, #Param("network_status") String network_status);
Based on the way you described your usage, fetching from the repo should retrieve the updated object without the need to refresh the object as long as the method which used the entity manager to merge has #transactional
here's a sample test
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfig.class)
#EnableJpaRepositories(basePackages = "com.foo")
public class SampleSegmentTest {
#Resource
SampleJpaRepository segmentJpaRepository;
#PersistenceContext
private EntityManager entityManager;
#Transactional
#Test
public void test() {
Segment segment = new Segment();
ReflectionTestUtils.setField(segment, "value", "foo");
ReflectionTestUtils.setField(segment, "description", "bar");
segmentJpaRepository.save(segment);
assertNotNull(segment.getId());
assertEquals("foo", segment.getValue());
assertEquals("bar",segment.getDescription());
ReflectionTestUtils.setField(segment, "value", "foo2");
entityManager.merge(segment);
Segment updatedSegment = segmentJpaRepository.findOne(segment.getId());
assertEquals("foo2", updatedSegment.getValue());
}
}

How to update an entity with a condition(where clause) and get the updated entity in method response in spring data jpa

I am working on concurrent update handling stuff, my scenario is- an employee can be marked inactive from UI, but if once marked inactive, it should not be allowed to marked inactive again. I want to achieve it by making the db update call with a condition update entity where status=active. I need the updated entity in return.
How to update an entity with a condition(where clause) and get the updated entity in method response in spring data jpa
employeRepository.save(employee); // is it possible to do something like this with a condition at the same time update method returns updated entity.
You are working against the grain of JPA here.
I suggest you do it the JPA way:
load the entity
If the status property is not inactive set it to active.
When ever the transaction ends this will be written to the database.
If you insist on using an update statement
You can use the following process:
If the entity is part of the current session:
flush all changes to the database.
evict the entity from the session.
perform an update either using SQL or JPQL with a where clause like this: ... WHERE id = :id and status <> 'inactive'
use the id to load the entity again.
You can use JPQL.
uery query = entityManager.createQuery(
"UPDATE employee SET status = \"inactive\"" +
"WHERE status=\"active\"");
query.executeUpdate();
or just spring data
#Modifying
#Query("update employee u status = `inactive` where status=`active` and ...")
void disableUsers(...);
however, to deal with concurrent queries a good way could be Optimistic locking and #Version annotation.

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.

EntityManager unwanted background commit

I have a strange problem on one of my glassfish server. Have a look on this piece of code:
userTransaction.begin();
MyEntity entity = new MyEntity(12345);
//setting values..
entityManager.persist(entity);
MyEntity persistedEntity = entityManager.createQuery("SELECT p FROM MyEntity p WHERE p.idpk=12345").getSingleResult();
//...
userTransaction.commit(); //OK => the tuple is in the DB
Now there is a business problem and the transaction needs to be rollbacked.
userTransaction.begin();
MyEntity entity = new MyEntity(12345);
//setting values..
entityManager.persist(entity);
MyEntity persistedEntity = entityManager.createQuery("SELECT p FROM MyEntity p WHERE p.idpk=12345").getSingleResult();
//...
//Business problem => rollback
userTransaction.rollback(); //ERROR => the tuple 12345 is in the DB !
Even if the rollback seems to work (no exception raised or strange log output), the tuple has been committed in the database...
To search where the problem is, I tried the following code:
userTransaction.begin();
MyEntity entity = new MyEntity(12345);
//setting values..
entityManager.persist(entity);
//Business problem => rollback
userTransaction.rollback(); //OK => the tuple 12345 is NOT in the DB !
With this code (the entity is not retrieved), the tuple is not committed to the database, which is the correct behaviour. Let's go further:
userTransaction.begin();
MyEntity entity = new MyEntity(12345);
//setting values..
entityManager.persist(entity);
MyEntity persisted = entityManager.find(MyEntity.class, 12345);
//...
//Business problem => rollback
userTransaction.rollback(); //OK => the tuple 12345 is NOT in DB
In this last case, the result is still correct and there is no tuple committed to the database. It seems the EntityManager makes a magic [unwanted] commit when retrieving the entity with a query...
I also tried to make a query on another table and that doesn't cause the wrong commit (the rollback works). Moreover, I tried to reproduce the problem on my own server, and there is no problem: all the rollbacks work correctly. Hence, it should really be a server configuration problem.
For information, it is a glassfish v2.1.1 running on linux. The EntityManager is in FlushModeType.AUTO.
Does anyone have an idea ?
Thanks & best regards !
Found some discussion which seems to explain some of what you're seeing:
Via P-331 of EJB3 In Action Book:
By default, the database flush mode is set to AUTO. This means that
the Entity-Manager performs a flush operation automatically as needed.
In general, this occurs at the end of a transaction for
transaction-scoped EntityManagers and when the persistence context is
closed for application-managed or extendedscope EntityManagers. In
addition, if entities with pending changes are used in a query, the
persistence provider will flush changes to the database before
executing the query. If the flush mode is set to COMMIT, the
persistence provider will only synchronize with the database when the
transaction commits. However, you should be careful with this, as it
will be your responsibility to synchronize entity state with the
database before executing a query. If you don’t do this and an
EntityManager query returns stale entities from the database, the
application can wind up in an inconsistent state.
P-353 EJB3 In Action Book states:
If the Query is set to FlushModeType.COMMIT, the effect of updates
made to entities in the persistence context is not defined by the
specification, and the actual behavior is implementation specific.
Perhaps try toggling the flush mode on the query returned by
entityManager.createQuery("SELECT p FROM MyEntity p WHERE p.idpk=12345").getSingleResult();
or on the entity manager itself and see if that changes anything?
Some further discussion here:
http://www.coderanch.com/t/475041/ORM/databases/EntityManager-setFlushMode-COMMIT-Vs-Query
Why not do this:
MyEntity persisted = entityManager.persist(entity);
I finally found where was the problem: in the JDBC Connection Pool, there is a section "Transaction" where the option "Non Transactional Connections" was enabled... Simply removing this option made the whole thing work :)

Hibernate/Spring HibernateTemplate.findByCriteria(Deatched Criteria dc) executes a sql update on view

I am trying to search a view based on given criteria. This view has a few fields for multiple different entities in my application that a user may want to search for.
When I enter the name of an entity I want to search for, I add a restriction for the name field to the detached criteria before calling .findByCriteria(). This causes .findByCriteria() to retrieve a list of results with the name I am looking for.
Also, when I look through my log, I can see hibernate calling a select statment.
I have now added another entity to my view, with a few searchable fields. When I try to search for a field related to this new entity, I get an exception in my log.
When I look through my log with the exception, I can see hibernate calling a select statment with an update statement right after the select (I am not trying to update a record, just retrieve it in a list).
So why is hibernate calling an update when I am calling .findByCriteria() for my new entity?
org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:90)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
SQL that is executed:
Hibernate:
select
*
from
( select
this_.SEARCH_ID as SEARCH1_35_0_,
this_.ST_NM as ST24_35_0_
from
SEARCH_RESULT this_
where
this_.LOAN_TYPE=? )
where
rownum <= ?
DEBUG 2012-03-21 11:37:19,332 142195 (http-8181-3:org.springframework.orm.hibernate3.HibernateTemplate):
[org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:389)]
Eagerly flushing Hibernate session
DEBUG 2012-03-21 11:37:19,384 142247 (http-8181-3:org.hibernate.SQL):
[org.hibernate.jdbc.util.SQLStatementLogger.logStatement(SQLStatementLogger.java:111)]
update
SEARCH_RESULT
set
ADDR_LINE1=?,
ASSGND_REGION=?,
BASE_DEAL_ID=?,
ST_NM=?
where
SEARCH_ID=?
There is probably an update happening because Hibernate is set up to do an autoflush before executing the queries, so if the persistence context thinks it has dirty data, it will try to update it. Without seeing the code I can't be sure, but I'd guess that even though search_result is a view, your corresponding Java object is annotated on the getters and the object has matching setters. Hibernate doesn't make a distinction between tables and views, and if you call a setter, Hibernate will assume that it has data changes to update.
You can tweak how you build your Java objects for views by adding the #Immutable annotation (or hibernate.#Entity(mutable = false) depending on which version you're using. This should be enough to indicate to Hibernate to not flush changes. You can also annotate the fields directly and get rid of your setters so that consumers of the SearchResult object know that it's read only.

Categories