Spring Data JPA + Hibernate Marking methods as Transactional - java

I am using Spring data JPA's repository for my application. Currently I am using basic CRUD operations provided by default by spring data jpa repositories and for complex join queries, I am writing custom JPQL queries as follows:
public interface ContinentRepository extends JpaRepository<Continent, Long>
{
#Query(value = "SELECT u FROM Continent u JOIN FETCH ... WHERE u.id = ?1")
public List<Continent> findContinent(Long id);
}
In my Service class, I am autowiring this repository and performing DB operations.
#Service
public class MyService
{
#Autowired
public ContinentRepository cr;
public void read()
{
var result1 = cr.findContinent(1);
var result2 = cr.findContinent(2);
}
#Transactional
public void write()
{
var c = new Continent();
// setters
c = cr.save(c);
}
}
Currently I am just marking the write() as org.springframework.transaction.annotation.Transactional.
Should I also mark read() method with Transactional(readOnly = true)? As it only performs read operations.
Should I also mark the findContinent(Long id) as Transactional(readOnly = true)? I read that all the default methods as marked as Transactional https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
In the repository interface, should I mark the Transactional annotation at the method level or at the interface level. (Also, I suspect most of the custom methods will be read only)
Is it good to have #Transactional at both Service and Repository layer?

Should I also mark read() method with Transactional(readOnly = true)? As it only performs read operations.
Not really necessary, however it might create some optimisations regarding cache memory consumption accordingly to this blog https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/
Should I also mark the findContinent(Long id) as Transactional(readOnly = true)? I read that all the default methods as
marked as Transactional
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
The same answer like in the first case
In the repository interface, should I mark the Transactional annotation at the method level or at the interface level. (Also, I
suspect most of the custom methods will be read only)
In general I added to the method level, because I have more control over the parameters like rollback
Is it good to have #Transactional at both Service and Repository layer?
I recommend you to have at the Service level because, there, you can update many tables (so you can use many repositories), and you want your entire update to be transactional.

Should I also mark read() method with Transactional(readOnly = true)? As it only performs read operations.
Yes. It is good practice, you will know immediately that this method doesn't change anything in database, and what is more important, you will disable some unintentional changes in database using this service method. If this method tries to do any update on DB exception will be thrown and rollback will be called.
Should I also mark the findContinent(Long id) as Transactional(readOnly = true)? I read that all the default methods as
marked as Transactional
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
Depends how you are using it. If you are always calling this from #Sevice then no. I think that is common use case as fare as I know.
In the repository interface, should I mark the Transactional
annotation at the method level or at the interface level. (Also, I
suspect most of the custom methods will be read only)
Same as 2.
Is it good to have #Transactional at both Service and Repository
layer?
Same as 2.

Related

How to check special conditions before saving data with Hibernate

Sample Scenario
I have a limit that controls the total value of a column. If I make a save that exceeds this limit, I want it to throw an exception. For example;
Suppose I have already added the following data: LIMIT = 20
id
code
value
1
A
15
2
A
5
3
B
12
4
B
3
If I insert (A,2) it exceeds the limit and I want to get exception
If I insert (B,4) the transaction should be successful since it didn't exceed the limit
code and value are interrelated
What can I do
I can check this scenario with required queries. For example, I write a method for it and I can check it in the save method. That's it.
However, I'm looking for a more useful solution than this
For example, is there any annotation when designing Entity ?
Can I do this without calling the method that provides this control every time ?
What examples can I give ?
#UniqueConstraint checking if it adds the same values
Using transaction
The most common and long-accepted way is to simply abstract in a suitable form (in a class, a library, a service, ...) the business rules that govern the behavior you describe, within a transaction:
#Transactional(propagation = Propagation.REQUIRED)
public RetType operation(ReqType args) {
...
perform operations;
...
if(fail post conditions)
throw ...;
...
}
In this case, if when calling a method there is already an open transaction, that transaction will be used (and there will be no interlocks), if there is no transaction created, it will create a new one so that both the operations and the postconditions check are performed within the same transaction.
Note that with this strategy both operation and invariant check transactions can combine multiple transactional states managed by the TransactionManager (e.g. Redis, MySQL, MQS, ... simultaneously and in a coordinated manner).
Using only the database
It has not been used for a long time (in favor of the first way) but using TRIGGERS was the canonical option used some decades ago to check postconditions, but this solution is usually coupled to the specific database engine (e.g. in PostgreSQL or MySQL).
It could be useful in the case where the client making the modifications is unable or unwilling (not safe) to check postconditions (e.g. bash processes) within a transaction. But nowadays it is infrequent.
The use of TRIGGERS may also be preferable in certain scenarios where efficiency is required, as there are certain optimization options within the database scripts.
Neither Hibernate nor Spring Data JPA have anything built-in for this scenario. You have to program the transaction logic in your repository yourself:
#PersistenceContext
EntityManager em;
public addValue(String code, int value) {
var checkQuery = em.createQuery("SELECT SUM(value) FROM Entity WHERE code = :code", Integer.class);
checkQuery.setParameter("code", code);
if (checkQuery.getSingleResult() + value > 20) {
throw new LimitExceededException("attempted to exceed limit for " + code);
}
var newEntity = new Entity();
newEntity.setCode(code);
newEntity.setValue(value);
em.persist(newEntity);
}
Then (it's important!) you have to define SERIALIZABLE isolation level on the #Transactional annotations for the methods that work with this table.
Read more about serializable isolation level here, they have an oddly similar example.
Note that you have to consider retrying the failed transaction. No idea how to do this with Spring though.
You should use a singleton (javax/ejb/Singleton)
#Singleton
public class Register {
#Lock(LockType.WRITE)
public register(String code, int value) {
if(i_can_insert_modify(code, value)) {
//use entityManager or some dao
} else {
//do something
}
}
}

TransactionRequiredException in SpringBoot

my controller is as follow:
#GetMapping("/groupByCourse")
public Reply getStudentsCountByCourse(){
Reply reply=new Reply();
reply.setData(service.getStudentsCountByCourse());
return reply;
}
and Service is:-
public List getStudentsCountByCourse() {
List students=new ArrayList<>();
students=studentCourseRepo.findCountStudentByCourse();
getCourseCountByStudent();
return students;
}
#Transactional(Transactional.TxType.REQUIRES_NEW)
public List getCourseCountByStudent() {
List students=new ArrayList<>();
students=studentCourseRepo.findCountCourseByStudent();
return students;
}
and repository is :-
#Repository
public interface StudentCourseRepo extends CrudRepository<StudentCourseTbl, StudentCourseTblPK> {
#Query(value = "select sc.studentCourseTblPK.courseId,count(sc.studentCourseTblPK.studentId) from StudentCourseTbl sc group by sc.studentCourseTblPK.courseId")
List findCountStudentByCourse();
#Lock(LockModeType.PESSIMISTIC_WRITE)
#Query(value = "select s.id,s.name,count(sc.studentCourseTblPK.courseId) from StudentCourseTbl sc right join StudentsTbl s on sc.studentCourseTblPK.studentId=s.id group by s.id")
List findCountCourseByStudent();
}
I am already using #Transactional(Transactional.TxType.REQUIRES_NEW) in my service method that execute the query with Pessimistic_write lock but still i am getting TransactionRequiredException even after transaction mode is set to requires new.I know i can make the code work by using #Transactional annotation on my getStudentsCountByCourse method but i want to know the reason why it doesnot work currently.
I am using springboot version 2.1.7.Release with mysql as database
This is a known limitation of Spring default CGLIB proxies. Since the transactional behavior is based on proxies, a method of the target object calling another of its method won’t lead to transactional behavior even though the latter method is marked as transactional.
This can be avoided by weaving the transactional behavior inside the bytecode instead of using proxies i.e. using AspectJ.
You can read more about spring transaction management here Method visibility and #Transactional

OptimisticLockException in pessimistic locking

I am using Spring with Hibernate. I am running jUnit test like this:
String number = invoiceNumberService.nextInvoiceNumber();
and invoiceNumberService method is:
InvoiceNumber invoiceNumber = invoiceNumberRepository.findOne(1L);
it is using simple spring data repository method, and it's working well. But when I override this method to use locking:
#Lock(LockModeType.PESSIMISTIC_READ)
#Override
InvoiceNumber findOne(Long id);
I am getting "javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction"
I can't understand why its optimistic lock exception, while I am using pessimistic locking? And where is this part when another transaction is changing this entity?
I have already dig a lot similar questions and I am quite desperate about this. Thanks for any help
Solution:
The problem was in my init function in test class:
#Before
public void init() {
InvoiceNumber invoiceNumber = new InvoiceNumber(1);
em.persist(invoiceNumber);
em.flush();
}
There was lack of
em.flush();
Which saves the data into database, so findOne() can now retreive it
Question: Have you given the #transcational annotation in dao or service layer?
it happens due to the reason that two transaction is simultaneously trying to change the data of same table..So if you remove all the annotation from the dao layer and put in the service layer it should solve the problem i this..because i faced similar kind of problem.
Hope it helps.
Just for the sake of it I'll post the following, if any one disagrees please correct me. In general in java you are advised to use Spring/hibernate and JPA. Hibernate implements JPA so you will need dependencies for Spring and Hibernate.
Next let Spring/hibernate manage your transactions and the committing part. It is bad practice to flush/commit your data yourself.
For instance let assume the following method:
public void changeName(long id, String newName) {
CustomEntity entity = dao.find(id);
entity.setName(newName);
}
Nothing will happen after this method (you could call merge and commit). But if you annotate it with #Transactional, your entity will be managed and at the end of the #Transactional method Spring/hibernate will commit your changes. So this is enough:
#Transactional
public void changeName(long id, String newName) {
CustomEntity entity = dao.find(id);
entity.setName(newName);
}
No need to call flush, Spring/Hibernate will handle all the mess for you. Just don't forget that your tests have to call #Transactional methods, or should be #Transactional themselves.

How to enable LockModeType.PESSIMISTIC_WRITE when looking up entities with Spring Data JPA?

How can I achieve the equivalent of this code:
tx.begin();
Widget w = em.find(Widget.class, 1L, LockModeType.PESSIMISTIC_WRITE);
w.decrementBy(4);
em.flush();
tx.commit();
... but using Spring and Spring-Data-JPA annotations?
The basis of my existing code is:
#Service
#Transactional(readOnly = true)
public class WidgetServiceImpl implements WidgetService
{
/** The spring-data widget repository which extends CrudRepository<Widget, Long>. */
#Autowired
private WidgetRepository repo;
#Transactional(readOnly = false)
public void updateWidgetStock(Long id, int count)
{
Widget w = this.repo.findOne(id);
w.decrementBy(4);
this.repo.save(w);
}
}
But I don't know how to specify that everything in the updateWidgetStock method should be done with a pessimistic lock set.
There is a Spring Data JPA annotation org.springframework.data.jpa.repository.Lock which allows you to set a LockModeType, but I don't know if it's valid to put it on the updateWidgetStock method. It sounds more like an annotation on the WidgetRepository, because the Javadoc says:
org.springframework.data.jpa.repository
#Target(value=METHOD)
#Retention(value=RUNTIME)
#Documented
public #interface Lock
Annotation used to specify the LockModeType to be used when executing the query. It will be evaluated when using Query on a query method or if you derive the query from the method name.
... so that doesn't seem to be helpful.
How can I make my updateWidgetStock() method execute with LockModeType.PESSIMISTIC_WRITE set?
#Lock is supported on CRUD methods as of version 1.6 of Spring Data JPA (in fact, there's already a milestone available). See this ticket for more details.
With that version you simply declare the following:
interface WidgetRepository extends Repository<Widget, Long> {
#Lock(LockModeType.PESSIMISTIC_WRITE)
Widget findOne(Long id);
}
This will cause the CRUD implementation part of the backing repository proxy to apply the configured LockModeType to the find(…) call on the EntityManager.
If you don't want to override standard findOne() method, you can acquire a lock in your custom method by using select ... for update query just like this:
/**
* Repository for Wallet.
*/
public interface WalletRepository extends CrudRepository<Wallet, Long>, JpaSpecificationExecutor<Wallet> {
#Lock(LockModeType.PESSIMISTIC_WRITE)
#Query("select w from Wallet w where w.id = :id")
Wallet findOneForUpdate(#Param("id") Long id);
}
However, if you are using PostgreSQL, things can get a little complicated when you want to set lock timeout to avoid deadlocks. PostgreSQL ignores standard property javax.persistence.lock.timeout set in JPA properties or in #QueryHint annotation.
The only way I could get it working was to create a custom repository and set timeout manually before locking an entity. It's not nice but at least it's working:
public class WalletRepositoryImpl implements WalletRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public Wallet findOneForUpdate(Long id) {
// explicitly set lock timeout (necessary in PostgreSQL)
em.createNativeQuery("set local lock_timeout to '2s';").executeUpdate();
Wallet wallet = em.find(Wallet.class, id);
if (wallet != null) {
em.lock(wallet, LockModeType.PESSIMISTIC_WRITE);
}
return wallet;
}
}
If you are able to use Spring Data 1.6 or greater than ignore this answer and refer to Oliver's answer.
The Spring Data pessimistic #Lock annotations only apply (as you pointed out) to queries. There are not annotations I know of which can affect an entire transaction. You can either create a findByOnePessimistic method which calls findByOne with a pessimistic lock or you can change findByOne to always obtain a pessimistic lock.
If you wanted to implement your own solution you probably could. Under the hood the #Lock annotation is processed by LockModePopulatingMethodIntercceptor which does the following:
TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);
You could create some static lock manager which had a ThreadLocal<LockMode> member variable and then have an aspect wrapped around every method in every repository which called bindResource with the lock mode set in the ThreadLocal. This would allow you to set the lock mode on a per-thread basis. You could then create your own #MethodLockMode annotation which would wrap the method in an aspect which sets the thread-specific lock mode before running the method and clears it after running the method.

How to use #Transactional with Spring Data?

I just started working on a Spring-data, Hibernate, MySQL, JPA project. I switched to spring-data so that I wouldn't have to worry about creating queries by hand.
I noticed that the use of #Transactional isn't required when you're using spring-data since I also tried my queries without the annotation.
Is there a specific reason why I should/shouldn't be using the #Transactional annotation?
Works:
#Transactional
public List listStudentsBySchool(long id) {
return repository.findByClasses_School_Id(id);
}
Also works:
public List listStudentsBySchool(long id) {
return repository.findByClasses_School_Id(id);
}
What is your question actually about? The usage of the #Repository annotation or #Transactional.
#Repository is not needed at all as the interface you declare will be backed by a proxy the Spring Data infrastructure creates and activates exception translation for anyway. So using this annotation on a Spring Data repository interface does not have any effect at all.
#Transactional - for the JPA module we have this annotation on the implementation class backing the proxy (SimpleJpaRepository). This is for two reasons: first, persisting and deleting objects requires a transaction in JPA. Thus we need to make sure a transaction is running, which we do by having the method annotated with #Transactional.
Reading methods like findAll() and findOne(…) are using #Transactional(readOnly = true) which is not strictly necessary but triggers a few optimizations in the transaction infrastructure (setting the FlushMode to MANUAL to let persistence providers potentially skip dirty checks when closing the EntityManager). Beyond that the flag is set on the JDBC Connection as well which causes further optimizations on that level.
Depending on what database you use it can omit table locks or even reject write operations you might trigger accidentally. Thus we recommend using #Transactional(readOnly = true) for query methods as well which you can easily achieve adding that annotation to you repository interface. Make sure you add a plain #Transactional to the manipulating methods you might have declared or re-decorated in that interface.
In your examples it depends on if your repository has #Transactional or not.
If yes, then service, (as it is) in your case - should no use #Transactional (since there is no point using it). You may add #Transactional later if you plan to add more logic to your service that deals with another tables / repositories - then there will be a point having it.
If no - then your service should use #Transactional if you want to make sure you do not have issues with isolation, that you are not reading something that is not yet commuted for example.
--
If talking about repositories in general (as crud collection interface):
I would say: NO, you should not use #Transactional
Why not: if we believe that repository is outside of business context, and it should does not know about propagation or isolation (level of lock). It can not guess in which transaction context it could be involved into.
repositories are "business-less" (if you believe so)
say, you have a repository:
class MyRepository
void add(entity) {...}
void findByName(name) {...}
and there is a business logic, say MyService
class MyService() {
#Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE)
void doIt() {
var entity = myRepository.findByName("some-name");
if(record.field.equal("expected")) {
...
myRepository.add(newEntity)
}
}
}
I.e. in this case: MyService decides what it wants to involve repository into.
In this cases with propagation="Required" will make sure that BOTH repository methods -findByName() and add() will be involved in single transaction, and isolation="Serializable" would make sure that nobody can interfere with that. It will keep a lock for that table(s) where get() & add() is involved into.
But some other Service may want to use MyRepository differently, not involving into any transaction at all, say it uses findByName() method, not interested in any restriction to read whatever it can find a this moment.
I would say YES, if you treat your repository as one that returns always valid entity (no dirty reads) etc, (saving users from using it incorrectly). I.e. your repository should take care of isolation problem (concurrency & data consistency), like in example:
we want (repository) to make sure then when we add(newEntity) it would check first that there is entity with such the same name already, if so - insert, all in one locking unit of work. (same what we did on service level above, but not we move this responsibility to the repository)
Say, there could not be 2 tasks with the same name "in-progress" state (business rule)
class TaskRepository
#Transactional(propagation=Propagation.REQUIRED,
isolation=Isolation.SERIALIZABLE)
void add(entity) {
var name = entity.getName()
var found = this.findFirstByName(name);
if(found == null || found.getStatus().equal("in-progress"))
{
.. do insert
}
}
#Transactional
void findFirstByName(name) {...}
2nd is more like DDD style repository.
I guess there is more to cover if:
class Service {
#Transactional(isolation=.., propagation=...) // where .. are different from what is defined in taskRepository()
void doStuff() {
taskRepository.add(task);
}
}
You should use #Repository annotation
This is because #Repository is used for translating your unchecked SQL exception to Spring Excpetion and the only exception you should deal is DataAccessException
We also use #Transactional annotation to lock the record so that another thread/request would not change the read.
We use #Transactional annotation when we create/update one more entity at the same time. If the method which has #Transactional throws an exception, the annotation helps to roll back the previous inserts.

Categories