Why JPA Repository always create a transaction? - spring

When I use P6Spy to capture SQL statements, I found there always a commit action takes about 200ms, which means there is always a transaction.
After more research, I found JPA always creates a transaction.
JPA always create a transaction even for findById method.
JPA always create a transaction even when explicitly annotate a method with "#Transactional(propagation = Propagation.NEVER)".
The root cause is SimpleJpaRepository class is annotated with #Transactional, and the
default propagation is REQUIRED.
#Repository
#Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID>
SimpleJpaRepository seems so obsessed with transaction...
Question:
Why SimpleJpaRepository add #Transactional at class level? The deleteById and delete method already annotated with #Transactional right?
How to avoid transaction for JPA query like findById?
When I hack Spring JPA source code like below, and recompiled as a home-made jar to replace the canonical jar, now JPA queries work like a charm, and for queries the commit is gone!
//Hack!
#Repository
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID>
P.S.
Related spring source codes here:
TransactionalRepositoryProxyPostProcessor.RepositoryAnnotationTransactionAttributeSource
Edit:
JPA Transaction here means JpaTransactionManager hold a physical connection with setAutoCommit(false) and follows with a commit action in the end.

Related

QueryException when using Spring Data Rest with EclipseLink on Multi-Tenant System

I am using Spring data rest and EclipseLink to create a multi-tenant single table application.
But I am not able to create an Repository where I can call on custom QueryParameters.
My Kid.class
#Entity
#Table(name="kid")
#Multitenant
public class Kid {
#Id
private Long id;
#Column(name = "tenant_id")
private String tenant_id;
#Column(name = "mother_id")
private Long motherId;
//more attributes, constructor, getter and setter
}
My KidRepository
#RepositoryRestResource
public interface KidRepository extends PagingAndSortingRepository<Kid, Long>, QuerydslPredicateExecutor<Kid> {}
When I call localhost/kids I get the following exception:
Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a):
org.eclipse.persistence.exceptions.QueryException\r\nException Description: No value was provided for the session property [eclipselink.tenant-id].
This exception is possible when using additional criteria or tenant discriminator columns without specifying the associated contextual property.
These properties must be set through EntityManager, EntityManagerFactory or persistence unit properties.
If using native EclipseLink, these properties should be set directly on the session.
When I remove the #Multitenant annotation on my entity, everything works fine. So it has definitively something to do with EclipseLink.
When I don't extend from the QuerydslPredicateExecutor it works too. But then I have to implement all findBy* by myself. And even doing so, it breaks again. Changing my KidsRepository to:
#RepositoryRestResource
public interface KidRepository extends PagingAndSortingRepository<Kid, Long> {
Collection<Kid> findByMotherId(#Param("motherId") Long motherId);
}
When I now call localhost/kids/search/findByMotherId?motherId=1 I get the same exception as above.
I used this tutorial to set up EcpliseLink with JPA: https://blog.marcnuri.com/spring-data-jpa-eclipselink-configuring-spring-boot-to-use-eclipselink-as-the-jpa-provider/, meaning the PlatformTransactionManager, the createJpaVendorAdapter and the getVendorProperties are overwritten.
The tenant-id comes with a jwt and everything works fine as long as I don't use QuerydslPredicateExecutor, which is mandatory for the use case.
Turns out, that the wrong JpaTransactionManager is used we I rely on the QuerydslPredicateExecutor. I couldn't find out, which one is created, but having multiple breakpoints inside the EclipseLink Framework code, non of them were hit. This is true for both, using the QuerydslPredicateExecutor or using the custom findby method.
I have googled a lot and tried to override some of the basic EclipseLink methods, but non of that worked. I am running out of options.
Does anyone has any idea how to fix or work around this?
I was looking for a solution for the same issue; what finally helped was adding the Spring's #Transactional annotation to either Repository or any place from where this custom query is called. (It even works with javax.transactional.) We had the #Transactional annotation on most of our services so the issue was not obvious and its occurrence seemed rather accidental.
More detailed explanation about using #Transactional on Repository is here: How to use #Transactional with Spring Data?.

is hibernate #Transactional(readOnly=true) on read query a bad practice?

I use Spring(Service Layer and Repository) to do CRUD operations on a mysql database.
MyServiceImpl :
#Service
#Transactional
public class MyServiceImpl implements MyService {
private final MyRepository myrepo;
....
#Transactional(readOnly = true)
public Optional<myObj> findOne(Long id) {
return myrepo.findById(id);
}
}
is the using of readonly=true for read operations a bad practice? what about performance?
This is a good optimization practice. You can find the examples in the Spring Data documentation. And you won't need to annotate your whole service with #Transactional annotation because "..CRUD methods of the Spring Data JPA repository implementation are already annotated with #Transactional"
Getting started with Spring Data JPA
To start with, since Spring doesn't do persistence itself, so readonly is only a hint to the provider for behaviour(e.g Hibernate)
As per Hibernate's behavior readonly=true will set FlushMode.NEVER in current session which will prevent committing the transaction.
If you don't explicitly set readOnly to true, you will have read/write transactions.
Now coming Back to your Question
Looking at your findOne method. Looks like you are doing a Read call from database.
So its good to mark it as readonly to let your Provider know you are reading only.
You can read more in Detail here
https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
Spring #Transactional read-only propagation

In Spring 3.2, should we use #Transactional annotation for db activities?

I use spring 3.2 and Hibernate 4 in my project. When i query table i get a "No Session found for current thread" message. I try to use #Transactional annotation(it get success) but i don't want to put #Transactional to every service implementation.
Is there an another way?
In other words "How can i do a simple "insert" operation without using #Transaction?"
Thx...
You should not have #Transactional on you DAO methods, in fact you should never be accessing your DAO methods directly, you should be using an #Service. A service will use zero or more DAO classes to perform operations, only after all operations are completed will the transaction be committed.
#Repository
public class CustomerDao() {
// dao methods here, they are not transactional but will be run within a sevice transaction
}
#Service
#Transactional
public class CustomerService() {
private final CustomerDao customerDao;
#Autowired
public CustomerService(CustomerDao customerDao) {
this.customerDao = customerDao;
}
//service methods here (they are all transactional because we have annotated the class)
}
#Transactional is used for making a java code call in transaction so that in case any exception occurred during the process then all database changes will be rolled back. In ideal scenario every service which you think should be independent should have #Transactional annotation. Hibernate also want each database calls in transaction thats why they have implemented in such a way that Transaction will be required for each database query to be successful. I am not sure why you wanted your service to be out of transaction still they would like to fire database calls.

Spring+hibernate+jpa how does it work?

In the new project that I have joined, they keep using the terms Hibernate and JPA interchangeably. So, I tried to dive down into the code and try to understand how this whole thing works (I am new to Spring, JPA and Hibernate world). I'll try to put the code here for better understanding:
1) There is a #Configuration class where they have the following:
#Resource
private HibernateJpaVendorAdapter hibernateOracleJpaVendorAdapter;
LocalContainerEntityManagerFactoryBean entityManager =
new LocalContainerEntityManagerFactoryBean();
entityManager.setJpaVendorAdapter(hibernateOracleJpaVendorAdapter);
entityManager.setPersistenceUnitName("abc");
.
.
So, in this configuration class, we are returning an EntityManagerFactory.
2) Then there is a persistor class marked #Persistor, where a method of repository is invoked (for example, for a save operation):
blahblahRepository.save(blahblahEntity, abcdef);
3) Finally there is a repository class which is annotated #Repository. Then again, they have this piece of code:
#PersistenceContext(unitName = "same as the name in persistence.xml")
protected EntityManager entityManager;
The "save" method wraps around the persist method of JPA:
getEntityManager().persist(entityObject);
My questions are as follows:
1) There is no word about Hibernate other than in the hibernateJpaVendorAdapter. I searched the entire workspace and it showed just 3 occurences of the word hibernate, all in the configuration file.
2) From whatever knowledge I have, one should use either an EntityManagerFactory or an EntityManager but we are doing both?
Hibernate is one of the implementations of the JPA spec. Since your project chose Hibernate as its JPA implementation, it uses the JPA API, which delegates the Hibernate. Just like when you use the JDBC API, which delegates to a specific Oracle or PostgreSQL driver.
EntityManagerFactory, as its name indicates, is a factory for EntityManager. I don't see why you wouldn't use both. EntityManager is the main interface of the JPA API, used to execute all database operations (find, persist, merge, query, etc.). EntityManagerFactory must be configured before asking it to create an EntityManager.

EntityManager and two DAO with PersistenceContextType.EXTENDED

I have a problem with my entity manager in my application. I have two DAO clasess like this:
#Repository
public abstract class DaoA
{
protected ClassA persistentClass;
#PersistenceContext(name="my.persistence", type=PersistenceContextType.EXTENDED)
protected EntityManager entityManager;
-------------- some typical action for DAO --------------
}
Second DAO is for ClassB and looks similar to DaoA. The rest of things are done for me by the Spring framework.
When I'm debugging the application I recognize that both DAO objects have different instances of EntityManager. In the result my two different DAOs are connected with different PersistenceContext.
Question is if this is correct behaviour or not? I would like to have the same PersistenceContext for all my DAO classes. Please give me a hint if this is possible and if I understood the JPA correctly?
Regards Hsd
It's a correct behaviour of EXTENDED persistence context, therefore you don't need it to be EXTENDED in this case.
In most cases you need a TRANSACTIONAL persistence context, that is the default mode when type is omitted:
#PersistenceContext(name="my.persistence")
protected EntityManager entityManager;
In this mode persistence context is associated with the transaction, so that all DAOs will share the same persistence context when working inside the same transaction. I guess it's what you need.

Resources