Correct use of the EntityManager in an #Async call - java

I am trying to use the #Async capabilities of the Spring framework to perform a simple indexing task.
The problem I'm facing is that I feel that the EntityManager used in my Async function is somehow reused from previous calls so my data is not up to date and sometimes uses old data.
Here is the code I wrote as an example. The goal is to update a product's data and index it asynchronously after I publish an event using Spring's ApplicationEventPublisher:
ProductService
#Service
class ProductService {
private final EntityManager entityManager;
private final ApplicationEventPublisher eventPublisher;
#Autowired
public ProductService(EntityManager entityManager, ApplicationEventPublisher eventPublisher) {
this.entityManager = entityManager;
this.eventPublisher = eventPublisher;
}
#Transactional
public void patchProduct (String id, ProductDto productDto) {
Product product = this.entityManager.find(Product.class, id);
product.setLabel(productDto.getLabel());
this.entityManager.flush();
this.eventPublisher.publishEvent(new ProductEvent(product, ProductEvent.EVENT_TYPE.UPDATED));
}
}
EventListener
#Component
public class ProductEventListener {
private final AsyncProcesses asyncProcesses;
#Autowired
public ProductEventListener (
AsyncProcesses asyncProcesses
) {
this.asyncProcesses = asyncProcesses;
}
#EventListener
public void indexProduct (ProductEvent productEvent) {
this.asyncProcesses.indexProduct(productEvent.getProduct().getPok());
}
}
AsyncProcesses
#Service
public class AsyncProcesses {
private final SlowProcesses slowProcesses;
#Autowired
public AsyncProcesses(SlowProcesses slowProcesses) {
this.slowProcesses = slowProcesses;
}
#Async
public void indexProduct (String id) {
this.slowProcesses.indexProduct(id);
}
}
SlowProcesses
#Service
public class SlowProcesses {
private EntityManager entityManager;
private ProductSearchService productSearchService;
#Autowired
public SlowProcesses(EntityManager entityManager, NewProductSearchService newProductSearchService) {
this.entityManager = entityManager;
this.newProductSearchService = newProductSearchService;
}
#Transactional(readonly = true)
public void indexProduct (String pok) {
Product product = this.entityManager.find(Product.class, pok);
// this.entityManager.refresh(product); -> If I uncomment this line, everything works as expected
this.productSearchService.indexProduct(product);
}
}
As you can see on the SlowProcesses file, if I refresh the product object in the entityManager, I get the correct and up to date data. If I do not, I might get old data from previous calls.
What is the correct way to use the EntityManager in an Asynchronous call? Do I really have to refresh all my objects in order to make everything work? Am I doing something else wrong?
Thank you for reading through

Since instances of EntityManager are not thread-safe as pointed out by Jordie, you may want to try this instead:
Instead of injecting an EntityManager, inject an EntityManagerFactory. Then from the EntityManagerFactory retrieve a new EntityManager instance that is used only for the duration of the method in question.

Related

JUnit5 test case calls #Transactional function

Given a TestClass with TestMethod for an integration test (spawning MySQL Testcontainer behind the scenes)
#SpringBootTest
public class InsertAnythingIntegrationTest {
private DoAnythingHandler handler;
#Autowired
private AnythingRepository anythingRepository;
#BeforeEach
void beforeEach() {
handler = new DoAnythingHandler(anythingRepository);
}
#Test
void handle_shouldAddEntry_givenValidValue() {
handler.insertSomething(new Entity(x,y,z));
assertThat(anythingRepository.findAll()).isEqualTo(1);
}
}
and the Handler implementation annotated with #Transactional
#Component
public class DoAnythingHandler() {
#Autowired
private AnythingRepository anythingRepository;
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Transactional
public void insertOrUpdateSomething(Entity entity) {
var existingEntity = anythingRepository.findById(entity.getId());
if (existingEntity != null) {
existingEntity.valueX = entity.x;
existingEntity.valueY = entity.Y;
existingEntity.valueZ = entity.Z;
} else {
anythingRepository.save(entity);
}
applicationEventPublisher.publishEvent(new AnyFurtherEventUpdatingDb(entity.X, entity.Y));
}
}
If I run this test, the transaction is never opened since it needs to be called from Bean to Bean to apply the #Transactional annotation.
How can I mock a Bean calling this method in my test case.
I don't want my test case to be #Transactional since I am not able to assert anything. This handler is my UnitOfWork and I want no further abstraction layer whatever in place.
How to approach this?
#Autowired
private DoAnythingHandler handler;
did the trick for me.
I thought I tried this before, but maybe I did something wrong before.
Thanks to everyone!

JPA Interface extending JpaRepository only, with use of EntityManager in default methods

First of all, for those who wanted to know how to use an EntityManager in an interface extending a JpaRepository.
Here is a potential solution.
But !!! I'm aware this is maybe a baaaad practice, and experienced people in Spring and JPA will maybe try to kill me ;) apologizes...
So, after this BIG warning, here is my question :
I would like to use only interfaces without a Custom implementation and this is what I did :
#Repository
#Transactional
public interface BookingDao extends JpaRepository<Booking, Long> {
Booking findByCodeId(String codeId);
void deleteByCodeId(String codeId);
default Integer findCountOfBookingOldDay() {
EntityManager entityManager = ApplicationContextProvider.getEntityManager();
Query query = entityManager.createNativeQuery("XXXX");
Number result = (Number) query.getSingleResult();
return result.intValue();
}
}
#Component(value = "applicationContextProvider")
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
public static EntityManager getEntityManager() {
return EntityManagerFactoryUtils.getTransactionalEntityManager(context.getBean(LocalContainerEntityManagerFactoryBean.class).getObject());
return context.getBean(EntityManager.class);
}
#Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
context = ac;
}
}
I'm aware this is maybe not the best practice, and the best way would be to use #PersistenceContext (takes care to create a unique EntityManager for every thread) in a class... But what would be in my case the best alternative :
this :
return EntityManagerFactoryUtils.getTransactionalEntityManager(context.getBean(LocalContainerEntityManagerFactoryBean.class).getObject());
or this :
return context.getBean(EntityManager.class)
or maybe another better way, still by using only interface.
Thank you for your remarks/advices.
PS: "EntityManagerFactoryUtils" is not doing the same as PersistenceContext ?
For what you want to do I'd annotate a method in the interface by #Query("<your native select counting what you need>", native=true).
When I need any fancy logic, I usually create a service, which is using the repository like this:
#Service
#Transactional
public class MyService {
private final MyRepository repository;
#Autowired
public MyService (MyRepository repository) {
this.repository = repository;
}
public int myMethod(){
//code using repository
}
}

Service class not updating database when called within Service Class

I am working on Java Spring MVC project. When the Controller calls the method in ServiceImpl class (updateAttempt()) which in turn calls DAOImpl class, the update happens and I see the updated data in DB.
But when the loadUserByUserName (which is present in ServiceImpl class) calls updateAttempt() method in same ServiceImpl class, it doesn't throw any error or exception, but data never gets updated in DB.
PersonController.java
#Controller
#SessionAttributes({ "mob_Number"})
public class PersonController implements Observer, InitializingBean{
private static final Logger logger = LoggerFactory.getLogger(PersonController.class);
private PersonService personService;
#Autowired(required=true)
#Qualifier(value="personService")
public void setPersonService(PersonService ps){
this.personService = ps;
}
public PersonController(PersonService personService){
this.personService = personService;
}
public PersonController(){
}
#RequestMapping(value="/submitVerificationCode",method = RequestMethod.POST, headers = "Accept=application/json")
#ResponseBody
public String submitVerificationCode(#RequestBody String json){
......
this.personService.update_User_Verification_AttemptCount(userVer.getMobile_Number(), no_Attempts);
//this call updates the data in DB
}
}
PersonServiceImpl.java
#Service
public class PersonServiceImpl implements PersonService, UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(PersonServiceImpl.class);
private PersonDAO personDAO;
private PersonService personService;
public void setPersonDAO(PersonDAO personDAO) {
this.personDAO = personDAO;
}
#Autowired
private Observer observe;
#Override
#Transactional
public void update_User_Verification_AttemptCount(String mobile_number, int count){
this.personDAO.update_User_Verification_AttemptCount(mobile_number, count);
}
#Override
#Transactional
public UserDetails loadUserByUsername(String mobile_Number)
throws UsernameNotFoundException {
this.update_User_Verification_AttemptCount(mobile_Number, no_Attempts); //but this call doesn't update the data in DB
this.getUserDetails() //but this call returns data from DB
}
PersonDAOImpl.java
#Repository
public class PersonDAOImpl implements PersonDAO {
private static final Logger logger = LoggerFactory.getLogger(PersonDAOImpl.class);
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public void update_User_Verification_VerCode(String mob_number, String verCode, Timestamp currentTimestamp){
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("update UserVerification set ver_Code=:verCode, sent_Time=:currentTimestamp where mobile_Number=:mob_Number");
query.setParameter("verCode", verCode);
query.setParameter("currentTimestamp", currentTimestamp);
query.setParameter("mob_Number", mob_number);
query.executeUpdate();
session.flush();
}
}
NOTE: the get methods residing in ServiceImpl(which does select) also return values properly when the get methods called from loadUserByUsername.
That is beacause your transaction does not commit when you call the methods inside the same service.
The problem there is that Spring enriches the bean with transaction behaviour by wrapping the bean inside the proxy and adding the behaviour to it. The proxy however is always created around the interface, so calling a method with this keyword will not propagate the desired behaviour.
a proper solution would be to repeat the dao call so to avoid the call of the same service method
#Transactional
public UserDetails loadUserByUsername(String mobile_Number)
throws UsernameNotFoundException {
this.personDAO.update_User_Verification_AttemptCount(mobile_number, count);
this.getUserDetails() //but this call returns data from DB
}
One other (hacky thing) that you can do is, since you already have a personService inside PersonServiceImpl, is to, first make sure that its injected, so add #Autowired
#Autowired private PersonService personService;
and than make a call through interface e.g.
personService.update_User_Verification_AttemptCount(mobile_Number, no_Attempts);
personService.getUserDetails()

Transactions with Spring and JPA

I am learning to use JPA. And I'm a little confused.
According JPA EntityManager manages transactions. But a design pattern is to inject the EntityManager in DAOs. So how is possible that are different EntityManager to the same transaction?
This is the case I want to solve
I have the DAOs defined
#Repository
JPARepository1 {
#PersistenceContext
protected EntityManager em;
....
.
#Repository
JPARepository2 {
#PersistenceContext
protected EntityManager em;
....
I have a Service
#Service
public class ServiceImpl1 {
#Autowired
private JPARepository1 repo1;
#Autowired
private JPARepository2 repo2;
public void mainMethod(){
Object o= transactionalMethod1();
try{
transactionalMethod2(o);
}catch (Exception e){
transactionalMethod3(o);
}
}
private Object transactionalMethod1(){
....
}
private void transactionalMethod2(Object o){
....
}
private void transactionalMethod3(Object o){
....
}
Then from #Controller I will invoke mainMethod().
What would be the right way to do transactional to transactionalMethod1, transactionalMethod2 and transactionalMethod3,within the same Service and using the same Repository's.
I would like it if there is an exeption in transactionalMethod2, this abort the transaction, but keep the transactions of transactionalMethod1 and transactionalMethod3
Thanks, sorry for my English
Usually you configure one EntityManager, so the wired manager is always the same, the one you configured. The instance of this manager though, is different in every wiring.
So, every transaction in your service uses a different instance of the EntityManager and thus every transaction invoked is seperated from each other.
As so, an exception in transactionalMethod2 doesn't necessarily affects the transactionalMethod1 and transactionalMethod3
What would be the right way to do transactional to transactionalMethod1, transactionalMethod2 and transactionalMethod3,within the same Service and using the same Repository's.
Now, you have two options to do service methods transactions
1) You could annotate your whole #Service like that:
#Service
#Transactional
public class ServiceImpl1 {
....
so every method declared here is also a transaction.
2) You could annotate each method as #Transactional:
#Transactional
private Object transactionalMethod1(){
....
}
#Transactional
private void transactionalMethod2(Object o){
....
}
#Transactional
private void transactionalMethod3(Object o){
....
}
If you want to use a single repository just #Autowired a single one and use it in your #Transactional method. E.g:
#Service
#Transactional
public class ServiceImpl1 {
#Autowired
private JPARepository1 repo1;
public void mainMethod(){
Object o= transactionalMethod1();
try{
transactionalMethod2(o);
}catch (Exception e){
transactionalMethod3(o);
}
}
private Object transactionalMethod1(){
return repo1.findOne();
}
private void transactionalMethod2(Object o){
repo1.create(o);
}
private void transactionalMethod3(Object o){
repo1.delete(o)
}

Understanding of Transactional services - spring

I have a service implementation with a particular method like so:
public class ExampleServiceImpl implements ExampleService {
#AutoWired
#Resource
private RecordRepository recordRepository;
private void processRecord() {
// some code here
}
#Transactional(readOnly=false)
public void processRecord(Record a) {
Record original = getOriginal(a);
recordRepository.saveChanges(a,original);
}
}
Where the Record class is the root object of an object graph. RecordRepository looks something like the following with sub repositories to save various children of the objects in the graph.
public class RecordRepository extends BaseRepository<Record> {
#AutoWired
#Resource
private IDao databaseDao;
#AutoWired
#Resource
private SubRecordRepository subRecordRepository;
public void saveChanges(Record a, Record b) {
//Perform some processing on a, b
for(SubRecord subA : a.getSubRecords()) {
subRecordRepository.saveChanges(subA);
}
databaseDao.updateRecord(a);
}
}
public class DatabaseDao extends NamedParameterJdbcDaoSupport implements IDao {
#Autowired
public DatabaseDao(#Qualifier("org.somewhere.Datasource") DataSource ds) {
super();
this.setDataSource(ds);
}
public void updateRecord(Record inRecord) {
String query = (String) sql.get("updateRecord");
SqlParameterSource parms = new BeanPropertySqlParameterSource(inRecord);
getNamedParameterJdbcTemplate().update(query, parms);
}
public void insertSubRecord(SubRecord inSubRecord) {
String query = (String) sql.get("insertSubRecord");
SqlParameterSource parms = new BeanPropertySqlParameterSource(inSubRecord);
getNamedParameterJdbcTemplate().insert(query, parms);
}
// other update and insert methods
}
Will the transaction be applied across all involved inserts\updates from the processRecord call? In other words, if an insert or update fails, will all previously called inserts and updates from ExampleServiceImpl.processRecord get rolled back?
Yes. The transactional aspect makes sure that a transaction is started before the annotated method is called, and that the transaction (if started by this method) is committed or rollbacked once the method returns.
The transactional interceptor doesn't know (and doesn't care) about which other methods are called inside the annotated method. Every read and write to the DataSource handled by the Spring transaction manager will be included in the same transaction.

Categories