spring data jpa transaction and state - spring-data-jpa

I use spring boot with jpa.
#Transaction
public void processXXX(Billing billing){
Party party = getOldParty(billing);
delete(party);
createNewParty(billing);
}
#Transaction
public void delete(Party party){
repository.delete(party);
}
#Transaction
public void createNewParty(Billing billing){
...
repository.save(billing);
}
is there a way to be sure delete operation are done before createNewParty is running?

Yes, do a flush between delete and save

Related

SpringBoot: Create document in mongodb on startup if not exists

I have a small service on SpringBoot and Mongodb as a DB.
I need to be able create a small collection with one document ( very basic: id, name, status) on startup. An analog of sql create table if not exists, but for mongo. How do I do that?
I tried to initialize values in the document attributes, but it didn't help.
Currently, collection and the document appear only if I use API to add it.
You may want to use something like ApplicationRunner or CommandLineRunner which can be defined as a bean.
Example:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication .class, args);
}
#Bean
public CommandLineRunner initialize(MyRepository myRepository) {
return args -> {
// Insert elements into myRepository
};
}
}
Both CommandLineRunner and ApplicationRunner are functional interfaces, so we can use a lambda for them. Spring Boot will execute them at the startup of the application.
You can leverage the spring internal event mechanism.
When your application is ready, spring triggers the event ApplicationReadyEvent
You can listen to this event and init your collection:
#Component
public class DataInit implements ApplicationListener<ApplicationReadyEvent> {
private final MyRepository myRepository;
public DataInit(MyRepository myRepository) {
this.myRepository = myRepository;
}
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// init data
}
}

XA transaction in spring batch

I am trying to commit jms and database transaction in spring batch job. I was under assumption that spring batch transaction are xa transactions. But in my item writer even when jms transaction errored out database transaction is committing. Can any one pls help me if I am missing something. Do I need to third party libraries for XA in spring batch?
I am actually throwing exception intentionally to test transaction roll back. Now even without any jms transaction just a database transaction is committing even with exception thrown from the item writer.Below is the method in writer which is saving into DB. compEvent object is jpa repository injected into this class
private void writeCEs(Map<TrueEvent, List<Event>> agentMap)
throws FailedCompensationException, Exception {
for (Entry<TrueEvent, List<Event>> agent : agentMap.entrySet()) {
agent.getValue().stream().forEach((ce) -> {
compEvent.save(ce);
});
updateRecordFileStatus(agent.getKey());
//postToAccounting(agent.getKey(), agent.getValue());
}
throw new Exception("Testing XA roolback.... ");
}
Below is my batch configuration
#EnableBatchProcessing
#EnableTransactionManagement
#Configuration
#ComponentScan({ "com.pm.*" })
public class TrueBatchConfig extends DefaultBatchConfigurer {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
EventReader reader;
#Autowired
private EventProcessor processor;
#Autowired
private EventWriter writer;
#Bean
protected Step step1(ThreadPoolTaskExecutor executor) {
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
attribute.setPropagationBehavior(Propagation.REQUIRED.value());
attribute.setIsolationLevel(Isolation.DEFAULT.value());
attribute.setTimeout(30);
return steps.get("step1").<List<TrueEvent>, Map<TrueUpEvent, List<Event>>>chunk(10).reader(reader)
.processor(processor).writer(writer).transactionAttribute(attribute).build();
}
#Bean(name = "firstBatchJob")
public Job job(#Qualifier("step1") Step step1) {
return jobs.get("firstBatchJob").start(step1).build();
}
}

Hazelcast MapStore and JPA Repository

Since many days, I try to make an Hazelcast MapStore works with a JPARepository but I cannot make it working because I cannot create a transaction:
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
I know that the MapStore does not participate to the Spring transaction as stated in the documentation but I would like to explicitly create another one if needed. I tried with TransactionTemplate or PlateformTransactioManager but it seems that it has no effect:
#Autowired
private UuidSpringDataJpaRepository uuidSpringDataJpaRepository;
#Autowired
private PlatformTransactionManager platformTransactionManager;
...
#Override
public void storeAll(Map<String, V> map) {
LOGGER.info("[{}, {}] storeAll: {}", tenant, cacheType, map);
ClientDatabaseContextHolder.setTenantName(tenant);
try {
TransactionTemplate txTemplate = new TransactionTemplate(platformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
for (Entry<String, V> entry : map.entrySet()) {
storeUuidEntity(entry.getKey(), entry.getValue());
}
uuidSpringDataJpaRepository.flush();
}
});
} finally {
ClientDatabaseContextHolder.removeTenantName();
}
}
I saw already some exemples like this so this might be feasible I guess ?
Thanks for your help.
#Jerome, you can actually let MapStore participate into Spring Transaction. Use #Transactional annotation on UuidSpringDataJpaRepository or another Service and/or any related method and call that method.

How can I customize the AuditingEntityListener injected by Spring-Data when using Auditing?

I try to add #PreRemove to my AuditingEntityListener to audit my data when use soft delete data in Spring , but i found it dosn't work, dose any guy have any suggest, Many thanks
public class MyAuditingEntityListener extends AuditingEntityListener{
#PreRemove
public void touchForRemove(Object target) {
this.touchForUpdate(target);
}
}

How do I create a separate entity manager for bulk operations in a JTA environment?

In JPA, when doing bulk operations such as this
update LogEntry e set e.customer = null where e.customer.id = :cid
It is recommended to use a separate entity manager to avoid breaking synchronization, according to this: UPDATE SET Queries in JPA/JPQL
For example, the EntityManager may not be aware that a cached entity object in its persistence context has been modified by an UPDATE query. Therefore, it is a good practice to use a separate EntityManager for UPDATE queries.
How do I create a separate entity manager in a JTA environment such as Wildfly using hibernate? Do I need to create a separate persistence unit for bulk operations?
EDIT: Given I dont need a separate PU for bulk operations, is this a sufficient way of solving it using a new transaction?
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
#Inject
private EntityManager em;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
...
}
Where LogEntry.updateCustomerToNull is the bulk query.
Answer: This does not work because the interceptor is not invoked when called from inside the same class.
EDIT2: Following the suggestions from Andrei, this should work:
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
public static class BulkUpdater {
#Inject
private EntityManager em;
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
}
#Inject
private EntityManager em;
#Inject
private BulkUpdater bulkUpdater;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
bulkUpdater.updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
...
}
Testing confirms that the interceptor gets called twice.
The recommendation is valid only if you also do other stuff with the EntityManager (when there is a risk of manipulating/reading the same entities as the BULK UPDATE). The easiest solution: make sure that this BULK UPDATE is executed in a separate service, within a new transaction. No need to create a separate PU (persistence unit) for bulk operations.