Springboot 2.6 Transaction Management with Hibernate - spring-boot

I have a common setup with a rest controller calling method from a service which itself calls a repository(extends JpaRepository) method(simple select through #Query annotation).
As I do a select it's beeing recommended to annotate method in the service with #Transactional(readOnly = true).
After enabling two different logs I can see the following:
logging.level.org.springframework.transaction.interceptor=TRACE
o.s.t.i.TransactionInterceptor : Getting transaction
for [mypackage.MyService.MyMethod]
o.s.t.i.TransactionInterceptor : No need to create transaction for
[org.springframework.data.jpa.repository.support.SimpleJpaRepository.MyMethod]:
This method is not transactional. Hibernate: select {here goes my
select statement} o.s.t.i.TransactionInterceptor :
Completing transaction for [mypackage.MyService.MyMethod]
logging.level.org.springframework.orm=DEBUG
o.s.orm.jpa.JpaTransactionManager: Creating new transaction with name
[mypackage.MyService.MyMethod]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
o.s.orm.jpa.JpaTransactionManager: Opened new EntityManager
[SessionImpl(1445729846)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager: Exposing JPA transaction as JDBC
[o.s.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#ea0fe1c]
Hibernate: select {here goes my select statement}
o.s.orm.jpa.JpaTransactionManager: Initiating transaction commit
o.s.orm.jpa.JpaTransactionManager : Committing JPA
transaction on EntityManager [SessionImpl(1445729846)]
o.s.orm.jpa.JpaTransactionManager : Closing JPA
EntityManager [SessionImpl(1445729846)] after transaction
and if change my service method annotaion to #Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
In log I can see only one line:
o.s.orm.jpa.EntityManagerFactoryUtils: Opening JPA EntityManager
So quite misleading logs for the same method. Is there actually a transaction created?
It seems that we do not need one for such cases(simple selects) to not create a persistence context and save some dirty checking costs.
But when what be the isolation rules in such cases which data could be read from database?

Related

Why Update using #Query in Spring Data Jpa requires #Transactional

I am new to spring boot. I use spring data jpa to deal with database. I have a method to update a table in the database using #Query. But when I try to update I get an exception of invalidDataAccessApiUsageException. when I tried it with #Transactional it gets updated successfully. Aren't updates a single operation so wouldn't it get committed automatically.
There are 2 ways in which transaction execute in SQL
Implicit -> One that means database, while running the write query(UPDATE, INSERT ....), create an isolation and then execute. If an error occurs, the isolation is discarded and no change will be written.
Explicit -> In this you explicitly specify the isolation start using BEGIN , discarding by ROLLBACK and finally writing by COMMIT
Initial versions of postgres (<7.4) had a configuration to called AUTOCOMMIT which when set off, will DISABLE implicit transactions. But this was disabled in 2003 since the databases were smart enough to discard isolations and not create inconsistencies.
In a nutshell at any point running following queries
UPDATE table_name WHERE id IN (....)
or
BEGIN
UPDATE table_name WHERE id IN (....)
COMMIT
are EXACTLY the same.
In JPA autocommit is now just a runtime validation for write queries.

Spring Boot, get queries used to map db extract to POJO

I wanted to find out if there is a way to know the query Spring Boot creates to extract data from a db. In detail: given Spring Boot needs to map a POJO to several tables in a db, how does it create the joints to provide the resulting JSON? Is there a way to see the query it generates in a log?
You can add below configuration in application.properties to see the query generated by Spring Boot / Hibernate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.show_sql=true
hibernate.show_sql=true
hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.*=true
You can see the query in console. Here I am sharing a sample of it.
2019-07-10 11:46:38:823 [http-nio-8080-exec-7] DEBUG org.hibernate.SQL -
select
*
from
user
where
statusenable=true
Hibernate:
select
*
from
user
where
statusenable=true
If you have passed parameters to query you can also see it (last line)
2019-07-10 11:49:30:073 [http-nio-8080-exec-3] DEBUG org.hibernate.SQL -
select
*
from
user
where
id=?
and statusenable=true
Hibernate:
select
*
from
user
where
id=?
and statusenable=true
2019-07-10 11:49:30:089 [http-nio-8080-exec-3] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [NUMERIC] - [2]

Custom query within JPA server-managed session

I'm trying to execute an unrelated query (other_table) within a JTA session (server-managed) in order to do some validations, before persisting the bean EJB into DB with JPA.
Here is what I want to do (aprox):
#PersistenceUnit(unitName="DynamicDatabase")
EntityManagerFactory emf;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private long nextEntryid() {
em = emf.createEntityManager();
Query query = em.createQuery("select t from OTHER_TABLE t");
// do some validation and checking
MyTable bean = new MyTable();
em.persist(bean);
}
However I keep getting a server error, it does not allow to interact with other database items that are not persisted:
org.apache.openjpa.persistence.ArgumentException:
An error occurred while parsing the query filter (query): The name "OTHER_TABLE" is not a recognized entity (...) Perhaps you meant MyTable, which is a close match.
<persistence-unit name="DynamicDatabase" transaction-type="JTA">
<jta-data-source>jdbc/DB2DynamicConnection</jta-data-source>
<class>jorge.java.dynamicdatabase.db.MyTable</class>
</properties>
</persistence-unit>
The question is: what is the proper way to query/alter another table within the same JPA DB connection and JTA transaction?
Pretty newby about this, please be patient with me. Been working on this very long.
Edit: I don't think it's related but FYI I'm using WebSphere Liberty Profile 8.5.5.4, JSDK 8u31, EclipseEE Luna 4.4.2 for Web Dev. Going to add it to tags.
The problem is that you have a #NamedQuery that references the entity OTHER_TABLE, and that does not exist or not marked as being an Entity (try in both Java class and persistence.xml).
If the OtherTable Java class is not an entity and if it MUST remain so, then you could use Constructors in JPQL-queries:
SELECT NEW com.domain.reports.MyReport(u.firstName, u.lastName, u.birthday) FROM User u
If you need to write plain SQL queries, then you could do it using native queries, as they are known in the JPA specification.
Based on Andrei's suggestion, the JPQL (Java Persistence Query Language) operates on objects, not on tables. This means that em.createQuery() can't be used for universal database interaction.
To execute any SQL statement generally speaking (inside the container-managed transaction JTA), it is necessary to get the DB connection from the entity manager (it will return the persistence context unit JPA).
// Get container's objects
em = emf.createEntityManager();
java.sql.Connection conn = em.unwrap(java.sql.Connection.class);
// Run the query
Statement sql = conn.createStatement();
ResultSet rs = sql.executeQuery("select * from OTHER_TABLE");
rs.next()
(...)
//Container-managed connection shouldn't be closed.
rs.close;
sql.close;
Notice that unwrap call works for JPA but not for Hibernate (other questions already exist on this point). Notice the different language used for the SQL statement select * from than a named query JPQL select t from. Exception handling must be also controlled as usual with a } finally { clause.
That way would allow to execute a complex custom statement within server-managed transaction without using JAVA entities and I can finally sleep.

Synchronize access to JPA EntityManager

I am using Spring, Hibernate and JPA Entity Manager. When fetching different regions of the UI concurrently (via jquery/ajax) , I would like to synchronise access to JPA Entity Manager, as otherwise it results in shared references to collections exception. How do I achieve this ? I have a controller that calls service methods in the backend which are responsible for calling get/save etc on entitymanager.
You can apply lock on the object with specific mode.
Entity Manager : entityManager.lock(entity, LockModeType.OPTIMISTIC);
Query : query.setLockMode(LockModeType.OPTIMISTIC);
NamedQuery : lockMode element
Lock mode OPTIMISTIC is synonymous to READ & OPTIMISTIC_FORCE_INCREMENT to WRITE.
Edit : From Documentation
If transaction T1 calls for a lock of type LockModeType.OPTIMISTIC on
a versioned object, the entity manager must ensure that neither of the
following phenomena can occur:
P1 (Dirty read): Transaction T1 modifies a row. Another transaction T2 then reads that row and obtains the modified value, before T1 has
committed or rolled back. Transaction T2 eventually commits
successfully; it does not matter whether T1 commits or rolls back and
whether it does so before or after T2 commits.
P2 (Non-repeatable read): Transaction T1 reads a row. Another transaction T2 then modifies or deletes that row, before T1 has
committed. Both transactions eventually commit successfully.
Lock modes must always prevent the phenomena P1 and P2.

How to read changed values with native query during one transaction? (Spring and JPA)

We have container transaction with Spring and JPA (Hibernate). I need to make an update on a table to "flag" some rows via native statements. Then we insert some rows via EntityManager from JPATemplate to this table. After that, we need to calculate changes in the table via native statement (with Oracle's union and minus, complex groups...)
I see that changes from step 1 and 2 are not commited and thats why the statement from 3 fails. I already tried with transaction propagation REQUIRES_NEW, EntityManager.flush... Didn't work.
1) update SOMETABLE acolumn = somevalue (native)
2) persist some values into SOMETABLE (via entity manager)
3) select values from SOMETABLE
Is there a possibility to read the changes from step 1 and 2 in step 3?
I guess you are using the same DataSource in the JpaTransactionManager, JdbcTemplate and JpaTemplate and have enabled #Transactional with this:
<tx:annotation-driven />
Assuming it is not a configuration bug, my best guess is that you're calling on the #Transactional method from a method in the same class?
In that case, you need AspectJ to weave in the transaction logic or refactor the code so the #Transactional method is in another class than the calling method. (The refactor option is the easy and preferred one)
If this doesn't solve your problem, you should add the relevant classes to your question together with relevant log messages.

Resources