I have an entity Customer and Spring data interface CustomerRepository shown below:
public interface CustomerRepository extends JpaRepository<Customer,Long> {
Customer findCustomerByName(String name);
}
I save Customer object in the database and then update one field like this:
customerRepository.save(new Customer("John", "Smith"));
Customer john = customerRepository.findCustomerByName("John");
john.setSurname("Barton");
customerRepository.flush();
customerRepository.findAll().forEach(System.out::println);
I don't understand why it prints: Customer(id=1, name=John, surname=Smith).
As far as I know, Hibernate uses dirty checking mechanism to update entities in persistent state. So changed surname should be propagated to the database during end of transaction (but it does not - even if I separate this code into two #Transactional methods). Am I doing something wrong? Do I really need to save object manually after each change? Why surname field is not updated in the database?
#RunWith(SpringRunner.class)
#SpringBootTest
public class CustomerRepoTest {
#Autowired
private CustomerRepository customerRepository;
#Test
//NOTE: No #Transactional
public void testSaveFails() throws Exception {
customerRepository.save(new Customer("John", "Smith"));
Customer john = customerRepository.findCustomerByName("John");
john.setSurname("Barton");
customerRepository.flush();
customerRepository.findAll().forEach(System.out::println);
}
#Test
#Transactional
public void testSaveWorks() throws Exception {
customerRepository.save(new Customer("John", "Smith"));
Customer john = customerRepository.findCustomerByName("John");
john.setSurname("Barton");
//customerRepository.flush(); Flush is not necessary
customerRepository.findAll().forEach(System.out::println);
}
}
To explain:
Hibernate keeps a cache of objects it loaded during a transaction.
When a find method is executed, the id of a newly loaded object is compared to the id of the object in this cache, if found, the cached version is used.
This is why the version with #Transactional works.
Also, it explains why flush is not necessary - it only forces the values to be written before the transaction ends.
If you miss the #Transactional (assuming auto-commit on the underlying transaction, which is most likely the case):
You save the entity in one transaction
Reload it with findCustomerByName, but it immediately gets detached
you modify the detached entity - no save
you reload the entries in another transaction, and you don't see your update
Related
I have the following case in a web app:
#Stateless
#LocalBean
public class AccountBean {
#PersistenceContext(unitName = "foreign-context")
private EntityManager fem;
#PersistenceContext(unitName = "own-context")
private EntityManager oem;
public void doCreate() {
Account account = createAccount();
SubAccount subAccount = createSubAccount(account);
}
private Account createAccount() {
Account account = new Account("This is a sample");
oem.persist(account);
oem.flush();
return oem;
}
private SubAccount createSubAccount(Account account) {
SubAccount subAccount = new SubAccount(account.getId()); // This field is only set after Account entity is persisted
fem.persist(subAccount);
fem.flush();
return subAccount;
}
}
The problem as I see it is that account.getId() returns the default value 0 (as is int) causing SQL exception when attempting to save SubAccount due to table constraints. The ID field on account is supposed to be set after the Account is persisted however I suspect that due to the uncommitted transactions the result is not persisted, therefore the ID field is not updated in code. I have tested both of the methods individually and they seem to work fine but when combined the issue arises.
I have to use two separate EntityManager(s) due to business requirements having the model objects in different project dependencies each with it's own descriptor.
I have tried creating a new container managed transaction using #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) on createAccount() but to no avail.
The code runs on a Wildfly Server using Hibernate 5.2.4 if that's relevant. No other framework is used (except JPA)
How can I alleviate this issue?
Do not completely understand your code, method persist should return you object with id and you can use it.
I have an List which contains say 4 DTOs. I am performing some processes on each of the DTOs present in my list. If suppose for one the DTO, any exception comes then all the transactions are rolled back (even if the process is success for other 3 DTOs).
My code looks like this :
#Transactional
public void processEvent(List<MyObject> myList){
myList.forEach(dto -> process(dto));
}
public void process(MyObject dto){
//some code which calls another class marked as #Transactional
// and save the data processed to database
}
I want to perform these processes for each DTO on a sepearte thread such that exception encountered in one thread does not rollbacks transaction for all the DTOs.
Also is there a way to process these DTOs one by one on different threads so that data consistency is maintained ?
Simply move the transactional to the method called with the dto, plus I am not sure if it is needed a transaction for dto. This looks as a controller which should not have any transactional annotaions. In the service once you change the dto to entity and are ready to save it you may put the anotation. Furthermore if you are simply calling the repository's save method you do not need to be in transaction as save method has the annotation in the repository.
public void processEvent(List<MyObject> myList){
myList.forEach(dto -> process(dto));
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void process(MyObject dto){
//some code which calls another class marked as #Transactional
// and save the data processed to database
}
And one last advice do not put #Transactional on classes, except if they have the readOnly parameter set to true. Then you can put #Transactional on the methods that perform any CRUD operations.
consider entity as user, it having some fields. here i am using jpa callback functions to update user information of last update information. in test class I want to write junit method to evaluate whether these call back methods are working or not/ not only for functionality testing and code coverage purpose also. but if I follow below approach i am getting same time everytime, can anyone help on this.
#Entity
public class User {
// user setter and getter methods
#preUpdate
public void preUpdateFunction() {
this.lastUpdateDate = new Date();
}
#prePersist
public void prePersistFunction() {
// setting some user properties
preUpdateFunction();
}
}
// please ignore this configuration and annotations setup, I tested my class spring configuration working perfectly there is no issue with spring configuration.
#SpringConfiguration
#JpaTransactional
public class TestClass {
#Autowired
UserDao userDao; // userDao implements JPA Repository
// I am worrying about this functionality only
#Test
public void saveUpdateTest() {
User user = userDao.save(new User(constructor arguments));
user = userDao.findOne(user.getId());
user.setName("Hello"); // here updating user object with existing property
User updatedUser = userDao.save(user);
assertEquals(user.getLastUpdateDate().getTime(), updatedUser.getLastUpdateDate().getTime());
// assertion is failing, everytime i am getting same Time for two values. even I added //Thread.sleep(1000) between save and update operations, still i am getting same values.
}
}
Short answer
You need to call saveAndFlush
User updatedUser = userDao.saveAndFlush(user);
Long answer
From JPA spec (JSR 338 JPA 2.1):
The PrePersist and PreRemove callback methods are invoked for a given entity before the
respective EntityManager persist and remove operations for that entity are executed.
The PreUpdate and PostUpdate callbacks occur before and after the database update operations to entity data respectively. These database operations may occur at the time the entity state is updated or
they may occur at the time state is flushed to the database (which may be at the end of the transaction).
#PrePersist is invoked when entityManager persist operation is executed. Tricky part is the execution is usually delayed until next flush operation or transaction commit (hibernate default config). Generally, it depends on flush configuration and on jpa implementation.
Same also applies to #PreUpdate. In addition, JPA spec says it more specifically, it might occur when entity state is updated or when flushed to DB.
Related links
https://download.oracle.com/otn-pub/jcp/persistence-2_1-fr-eval-spec/JavaPersistence.pdf
https://thorben-janssen.com/spring-data-jpa-save-saveandflush-and-saveall
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.
Let's take into consideration the following code snippet:
public class EmployeeServiceImpl implements EmployeeService
{
#PersistenceContext(unitName="EmployeeService")
EntityManager em;
public void assignEmployeeToProject(int empId, int projectId)
{
Project project = em.find(Project.class, projectId);
Employee employee = em.find(Employee.class, empId);
project.getEmployees().add(employee);
employee.getProjects().add(project);
}
}
please note that this example refers to Transaction Scoped,container managed Entity Manager.
from javacodegeeks:
By the end of 2nd line in the method both project and employee
instance are managed. At the end of the method call, the transaction
is committed and the managed instances of person and employee get
persisted. Another thing to keep in mind is that when the transaction
is over, the Persistence Context goes away.
I really cannot understand how does the entity manager knows the method is closed and implicitly commits the transaction...
Am I missing something here ?
Should we commit the transaction explicitly ?
Yes you are missing something:
Your service isn't just an instance of EmployeeServiceImpl but of a proxy class which wraps EmployeeServiceImpl and every public method in it. And when your method exits the wrapping method takes over and commits the transaction. If you debug your application and set a breakpoint in assignEmployeeToProject() you can see very easily what is happening in the stacktrace.