Given the following code:
public class OrderService {
#PersistanceContext
private EntityManager entityManager;
#Transactional
public void updateOrder(long orderId, OrderDTO updatedOrder) {
Order order = entityManager.find(Order.class, orderId);
if (order != null) {
order.setName(updated.getName());
} else {
throw new EntityNotFoundException(Order.class, orderId);
}
}
}
I was asked to point out all the queries that are executed when the updateOrder method is called including transactional sentences.
My answer was 1 query, the one that retrieves the order by calling entityManager.find(Order.class, orderId) however it seems that is not correct. How is that even possible? I do see the setName method is called on the order but there is not a call to save that order back to the database.
Is there any documentation that explains how this works or any way to see all the sentences executed in that transaction?
When you call find() method,your object becames in persistent state. Hibernate will detect any changes made to an object in persistent state and synchronize the state with the database when the unit of work completes. You can read about object states : https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html
The answer is it depends, the first one for sure is entityManager.find(...) which does a select. And if it finds a record, you are setting a new name(setName(...)) for which hibernated detects the object as dirty. So that it will flush the new data to db. Hence, as a second call save(...) will be triggered. Check here
Related
I am trying to understand the behavior of transaction propagation using SpringJTA - JPA - Hibernate.
Essentially I am trying to update an entity. To do so I have written a test method where I fetch an object using entity manager (em) find method ( so now this object is manged object). Update the attributes of the fetched object. And then optionally make a call to service layer(service layer propagation=required) which is calling em.merge
Now I have three variations here :
Test method has no transactional annotation. Update the attributes
of the fetched object and make no call to service layer.
1.1. Result level 1 cache doesn't gets updated and no update to DB.
Test method has no transactional annotation. Update the attributes of the fetched object. Call the service layer.
2.1. Result level 1 cache and DB gets updated.
Test method has Transnational annotation which could be any of the following. Please see the table below for Propagation value at the test method and the outcome of a service call.
(service layer propagation=required)
So to read the above table, the row 1 says if the Test method has transaction propagation= REQUIRED and if a service layer call is made then the result is update to Level 1 cache but not to the DB
Below is my test case
#Test
public void testUpdateCategory() {
//Get the object via entity manager
Category rootAChild1 = categoryService.find(TestCaseConstants.CategoryConstant.rootAChild1PK);
assertNotNull(rootAChild1);
rootAChild1.setName(TestCaseConstants.CategoryConstant.rootAChild1 + "_updated");
// OPTIONALLY call update
categoryService.update(rootAChild1);
//Get the object via entity manager. I believe this time object is fetched from L1 cache. As DB doesn't get updated but test case passes
Category rootAChild1Updated = categoryService.find(TestCaseConstants.CategoryConstant.rootAChild1PK);
assertNotNull(rootAChild1Updated);
assertEquals(TestCaseConstants.CategoryConstant.rootAChild1 + "_updated", rootAChild1Updated.getName());
List<Category> categories = rootAChild1Updated.getCategories();
assertNotNull(categories);
assertEquals(TestCaseConstants.CategoryConstant.rootAChild1_Child1,categories.get(0).getName());
}
Service Layer
#Service
public class CategoryServiceImpl implements CategoryService {
#Transactional
#Override
public void update(Category category) {
categoryDao.update(category);
}
}
DAO
#Repository
public class CategoryDaoImpl {
#Override
public void update(Category category) {
em.merge(category);
}
}
Question
Can someone please explain why does REQUIRED, REQUIRES_NEW, and NESTED doesn't lead to insertion in the DB?
And why absence of transaction annotation on Test case lead to insertion in the DB as presented in my three variations?
Thanks
The effect you're seeing for REQUIRED, NESTED, and REQUIRES_NEW is due to the fact that you're checking for updates too early
(I'm assuming here that you check for db changes at the same moment when the test method reaches the assertions, or that you roll the test method transaction back somehow after executing the test)
Simply enough, your assertions are still within the context created by the #Transactional annotation in the test method. Consequently, the implicit flush to the db has not been invoked yet.
In the other three cases, the #Transactional annotation on the test method does not start a transaction for the service method to join. As a result, the transaction only spans the execution of the service method, and the flush occurs before your assertions are tested.
For concurrency purpose, I have got a requirement to update the state of a column of the database to USED while selecting from AVAILABLE pool.
I was thinking to try #Modifying, and #Query(query to update the state based on the where clause)
It is all fine, but this is an update query and so it doesn't return the updated data.
So, is it possible in spring data, to update and return a row, so that whoever read the row first can use it exclusively.
My update query is something like UPDATE MyObject o SET o.state = 'USED' WHERE o.id = (select min(id) from MyObject a where a.state='AVAILABLE'), so basically the lowest available id will be marked used. There is a option of locking, but these requires exceptional handling and if exception occur for another thread, then try again, which is not approved in my scenario
You need to explicitly declare a transaction to avoid other transactions being able to read the values involved until it's commited. The level with best performance allowing it is READ_COMMITED, which doesn't allow dirty reads from other transactions (suits your case). So the code will look like this:
Repo:
#Repository
public interface MyObjectRepository extends JpaRepository<MyObject, Long> {
#Modifying
#Query("UPDATE MyObject o SET o.state = 'USED' WHERE o.id = :id")
void lockObject(#Param("id") long id);
#Query("select min(id) from MyObject a where a.state='AVAILABLE'")
Integer minId();
}
Service:
#Transactional(isolation=Isolation.READ_COMMITTED)
public MyObject findFirstAvailable(){
Integer minId;
if ((minId = repo.minId()) != null){
repo.lockObject(minId);
return repo.findOne(minId);
}
return null;
}
I suggest to use multiple transactions plus Optimistic Locking.
Make sure your entity has an attribute annotated with #Version.
In the first transaction load the entity, mark it as USED, close the transaction.
This will flush and commit the changes and make sure nobody else touched the entity in the mean time.
In the second transaction you can no do whatever you want to do with the entity.
For these small transactions I find it clumsy to move them to separate methods so I can use #Transactional. I therefore use the TransactionTemplate instead.
I have a Foo entity with fields Name, SecondaryName and Counter.
In the DB I have a unique constraint on (name, secondaryName, counter).
In the service layer I have the following method (where fooRepositry is a CrudRepository):
#Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW)
public void saveFoo(Foo foo) {
Optional<TestDto> fooWithHighestCounter= fooRepository.
findTopByNameAndSecondaryNameOrderByCounterDesc(foo.getName(), foo.getSecondaryName());
if (fooWithHighestCounter.isPresent()) {
foo.setCounter(fooWithHighestCounter.get().getCounter() + 1);
} else {
foo.setCounter(1);
}
Foo saved = fooRepository.save(foo);
}
With every call on saveFoo, a new record shall be created in the DB with already the existing highest counter + 1. Hence, the highest counter must be found, thus the #Transactional.
However, I constantly get ContraintViolationException when multiple threads call the saveFoo method as every thread finds the same highest counter value.
I assumed that every thread would create a new transaction and those transactions will run serially so no transaction would find the same counter value. (The #EnableTransactionManagement is put on the Application)
What else can I do to achieve the aforementioned behavior?
I think the fooRepository.save(foo) at last is saving the same values again and again in the database that is why it is giving ContrainViolationException. If you need to update the value to any existing Object just call the setCounter but dont call the .save() method instead call the update method of the repository (if you have any) else if it is a new entity which is not present in database yet then call the save method.
If it is done in hibernate refer the following link
Ref: http://www.objectdb.com/java/jpa/persistence/update
Basically I'm trying to get an entity that has a LAZY relation to another entity. Below are 2 things I tried. The first one works, the second one does not and I don't understand why. All I want is to get the entity from database. The reason why I put this in other method is I don't want the first one to be #Transactional because it can take some time to execute. Note that I'm not saving or even accessing the database again in the first method, I just need to read from db once.
Method 1 (Works as expected):
#Override
#Transactional
public void sendEmailToUser(Long exhibitorId) {
EmailExhibitorTO exhibitorTO = findExhibitorById(exhibitorId);
}
private EmailExhibitorTO findExhibitorById(Long id){
return converter.convert(exhibitorRepository.findById(id), EmailExhibitorTO.class);
}
Everything here is fine, I'm getting the entity and the lazy initialized entity as well.Method 2 (Doesn't work):
#Override
public void sendEmailToUser(Long exhibitorId) {
EmailExhibitorTO exhibitorTO = findExhibitorById(exhibitorId);
}
#Transactional
private EmailExhibitorTO findExhibitorById(Long id){
return converter.convert(exhibitorRepository.findById(id), EmailExhibitorTO.class);
This however does not work. Error:
There's a mapping exception but that's because lazy entity could not be fetched.I'm probably just being stupid but if theres something I don't understand, please explain. Thanks in advance.
The #Transactional in your private method has no effect because you are calling it from another method of the class, bypassing the Proxy that handles the transaction.
you cannot propagate transactions on private methods, you can see it here:
Does Spring #Transactional attribute work on a private method?
Using Spring (MVC, Batch, and persistence) I have the following piece of an interface defined:
public interface JobStatusService {
#Transactional(readOnly = false)
#Modifying
JobStatus save(JobStatus status);
#Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
Optional<JobStatus> get(ResultsIdentifier identifier);
}
Note the REQUIRES_NEW as it's on the broken method.
This interface is implemented in:
#Override
public JobStatus save(JobStatus status) {
if (status.getUserId() == null) {
status.setUserId(userService.getCurrentUser());
}
status.setLastUpdateTime(new Date());
return repository.save(status);
}
#Override
public Optional<JobStatus> get(ResultsIdentifier identifier) {
return repository.findByJobIdentifier(identifier);
}
Where repository is a JPA repository with the following:
public interface JobStatusRepository extends Repository<JobStatus, ResultsIdentifier> {
JobStatus save(JobStatus status);
Optional<JobStatus> findByJobIdentifier(ResultsIdentifier id);
}
ResultsIdentifier is a simple compound ID class of a string code and an ID number.
Elsewhere, I have a batch Writer finishing out by writing a JobStatus to the repository, and a web Controller class which runs a loop containing a query to the job status service. The save() operation seems to execute correctly, and the get() operation works right the first time.
However, if the first run of the loop calls get() and the JobStatus isn't "complete" then on all subsequent runs it still is not "complete". And in fact the same exact object comes back from Hibernate (which is my persistence tool under the hood). What's happening is that the session cache in SessionImpl/StatefulPersistenceContext is keeping a copy of it and always returning that instead of doing a real database query. So, I figured, propagation = Propagation.REQUIRES_NEW should always start a new transaction/session, right? But the same problem recurs whether I include that in the annotation or not.
Are the life-cycles of the session and transaction not coterminous here? If not, how can I tell Spring I want to start a new session? I'm trying to avoid changing my XML configs to push the Hibernate session factory into the JobStatusService implementation and manually clear() it, as no other part of the system sees Hibernate directly.
I'm also happy to hear any suggestions for debugging session-related stuff, given that in my code it's just a magic annotation.
I eventually "solved" this by setting fetch to EAGER on a particular entity definition. This doesn't seem like it should work, so I put in this question to inquire what might have happened.