how to keep jpa session in the thread - spring

Now I use JPA in my web application. And I have a class like this:
class A {
...
#OneToMany
private List<B> list;
...
}
When it is in a HTTP request, I can use a.getList() successful. But in a schedule thread, it throws the exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: A.list, could not initialize proxy - no Session
Can I keep the session in the schedule thread just like the http request thread?

Actually, when spring handle the http request, it start the transaction by the interceptor org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor or the filter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter. So if we want to keep the session, we can start and commit the transaction by our self.
Here is the code:
public class BackendThread {
#Autowired
private PlatformTransactionManager platformTransactionManager;
#Override
public void onMessage(Message message, byte[] pattern) {
new TransactionTemplate(platformTransactionManager).execute(new TransactionCallback<Void>() {
#Override
public Void doInTransaction(TransactionStatus transactionStatus) {
...
your code here
...
return null;
}
});
}
}

TransactionTemplate is helpful if you use JPA. I didn't use it and created a session/transactions on my own in background thread, so that each task had its own environment:
Inject entity manager or get it from Spring context or pass it as reference:
#PersistenceContext
private EntityManager entityManager;
Then create a new entity manager, to avoid using a shared one:
EntityManager em = entityManager.getEntityManagerFactory().createEntityManager();
Now you can start transaction and use Spring DAO, Repository, JPA, etc
private void save(EntityManager em) {
try
{
em.getTransaction().begin();
<your database changes>
em.getTransaction().commit();
}
catch(Throwable th) {
em.getTransaction().rollback();
throw th;
}
}

Related

Spring Data Neo4j OGM version and transaction isolation/propagation

I have a service method mergeAndUpdateAndCompliance() which uses Spring Data Neo4j OGM version:
#Transactional("neo4jTransactionManager")
#Override
public ComplianceMatrix mergeAndUpdateAndCompliance() {
mergeAndUpdate();
return compliance()
}
#Override
public void mergeAndUpdate() {
//do some update operations
}
#Override
#Transactional(readOnly = true, transactionManager = "neo4jTransactionManager")
public void compliance() {
//do some read operations
}
mergeAndUpdateAndCompliance() invokes two other service methods - mergeAndUpdate() and compliance(). compliance() reads the data updated by mergeAndUpdate(). Right now compliance() doesn't see the data updated in mergeAndUpdate() in the same transaction.
It works only if I add session.getTransaction().commit(); between them:
#Autowired
private Session session;
#Transactional("neo4jTransactionManager")
#Override
public ComplianceMatrix mergeAndUpdateAndCompliance() {
mergeAndUpdate();
session.getTransaction().commit();
return compliance()
}
Is it safe to place session.getTransaction().commit(); inside of Spring transaction ? What is the right way to solve this issue? Is it possible to use transaction propagation with SDN in order to configure mergeAndUpdate with REQUIRES_NEW ?
You have applied #Transactional on the mergeAndUpdateAndCompliance, function, within it is also applied to compliance method. You should try this way:
#Override
public ComplianceMatrix mergeAndUpdateAndCompliance() {
mergeAndUpdate();
return compliance()
}
#Override
#Transactional("neo4jTransactionManager")
public void mergeAndUpdate() {
//do some update operations
}
#Override
#Transactional(readOnly = true, transactionManager = "neo4jTransactionManager")
public void compliance() {
//do some read operations
}
Instead of applying it on the mergeAndUpdateAndCompliance, you should apply it on mergeAndUpdate and compliance functions separately. So that you don't have to manually commit the transaction.

How to involve a Collection on a spring transaction?

I currently have a spring application with hibernate and a PlataformTransactionManager running on Jboss/wildfly.
Some of the methods that manipulate the database also call a bean which contains a LinkedBlockingQueue. This queue stores logging messages that are periodically dispatched to someplace else on another thread (using simple spring #Scheduler).
Would it be possible to make my queue (inside a bean) transactional? ie. if the transaction rollback would I be able to "undo" any operations made on my Collection? What's the best strategy to implement this ?
So, in short something like:
#Service
#Transactional
public PersonService {
#Autowired
EntityManager EM;
#Autowired
LoggingBuffer logger;
public void addPerson(String name) {
EM.persist(new Person(.....));
logger.add("New person!");
// A rollback here via some thrown exception would not affect the queue
}
}
#Component
public class LoggingBuffer {
private Queue<String> q= new LinkedBlockingQueue<String>();
public add(String msg){
q.add(msg);
}
}
Try something like this
#Transactional
public void addPerson(String name) {
EM.persist(new Person(.....));
//logger.add("New person!");
// A rollback here via some thrown exception would not affect the queue
}
public void wrapAddPerson(String name){
List<String> localBuffer = new ArrayList<>();
try{
addPerson(name);
localBuffer.add(".....");
}catch(Exception e)
{
localBuffer.clear();
}
finally{
localBuffer.forEach(logger::add);
}
}

Wny am I getting "java.lang.IllegalStateException: No TransactionalEventListener annotation"?

I'm using Spring 4.3.8.RELEASE with Hibernate 5.1.5.Final. I want to have a method executed after another another transaction completes. That transaction is defined below
#Service("organizationService")
#Transactional
public class OrganizationServiceImpl implements OrganizationService, ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher;
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher)
{
this.publisher = publisher;
}
#Override
public void save(Organization organization)
{
...
// sync data with ThirdParty but only if something has definitelychanged on the SB
// side, in which case we want to send ThirdParty an update.
if (!hasSameAttributes)
{
publisher.publishEvent(new ThirdPartyOrganizationEvent(organization.getId()));
} // if
} // save
So here is the method that I want executed after the above transaction completes ...
#Service
public class ThirdPartyAPIServiceImpl implements ThirdPartyAPIService
{
#Override
#TransactionalEventListener
public boolean updateOrg(final ThirdPartyOrganizationEvent thirdPartyOrgEvent)
{
...
}
But when I load my application context I get this error
Caused by: java.lang.IllegalStateException: No TransactionalEventListener annotation found on method: public abstract boolean org.mainco.subco.myproject.service.ThirdPartyAPIService.updateOrg(org.mainco.subco.myproject.domain.ThirdPartyOrganizationEvent)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter.<init>(ApplicationListenerMethodTransactionalAdapter.java:55)
at org.springframework.transaction.event.TransactionalEventListenerFactory.createApplicationListener(TransactionalEventListenerFactory.java:55)
at org.springframework.context.event.EventListenerMethodProcessor.processBean(EventListenerMethodProcessor.java:159)
at org.springframework.context.event.EventListenerMethodProcessor.afterSingletonsInstantiated(EventListenerMethodProcessor.java:104)
... 34 more
Wbat do I need to do to get this configured properly?
Defining #TransactionalEventListener on interface method rather then on method implementing interface worked for me.

Spring transactioninterceptor is invoked when no class or method annotated with transactional

I am working on a gigaspace xap application which uses spring under the hood. The jini transaction manager provided by gigaspaces does not support serializable.
I have a class which uses spring-batch to process a file. Below is how it is invoking the job
public class FileProcessor implements BasicFileProcessor {
#Value("${feeddownload.basedir}")
private String baseDir;
#Autowired
private JobLauncher jobLauncher;
#Autowired
private Job cmJob;
#Autowired
private MapJobRepositoryFactoryBean repositoryFactoryBean;
#Override
public void process(RiskRunCompletion riskRunCompletion, VersionedSliceName versionedSliceName, GigaSpace gigaSpace) {
Transaction tx = gigaSpace.getCurrentTransaction();
try {
//Adding current time to the parameter, to enable multiple times calling job with same parameters
long currentTimeInMillis = System.currentTimeMillis();
JobParameter currentTimeInMillinsParam = new JobParameter(currentTimeInMillis);
Map parameterMap = new LinkedHashMap();
addDirectoryParams(valuationSliceRun, parameterMap);
parameterMap.put(CURRENT_TIME, currentTimeInMillinsParam);
JobParameters paramMap = new JobParameters(parameterMap);
JobExecution cmExecution = launchJobWithParameters(paramMap);
for (Throwable t : cmExecution.getAllFailureExceptions()) {
throw new RuntimeException(t);
}
} catch (Exception e) {
throw new RuntimeException("Exception during batch job", e);
} finally {
repositoryFactoryBean.clear();
}
}
private JobExecution launchJobWithParameters(JobParameters paramMap) throws Exception {
return jobLauncher.run(cmJob, paramMap);
}
}
The process method is invoked from a different class as below
public class FileBasedProcessingEventListener implements ApplicationContextAware {
#Value("${feeddownload.basedir}")
private String baseDir;
#Autowired
private BasicFileProcessor cmProcessor;
#Autowired
private FileBasedProcessingExceptionHandler fileBasedProcessingExceptionHandler;
public void handle(FileBasedProcessingEvent fileBasedProcessingEvent, GigaSpace gigaSpace) throws IOException {
LOGGER.info("Processing file based processing event : " + fileBasedProcessingEvent);
createLockFiles(fileBasedProcessingEvent);
handleEvent(fileBasedProcessingEvent, gigaSpace);
}
private void handleEvent(FileBasedProcessingEvent fileBasedProcessingEvent, GigaSpace gigaSpace) {
Transaction tx = gigaSpace.getCurrentTransaction();
cmProcessor.process(fileBasedProcessingEvent.getRiskRunCompletion(), versionedSliceName, gigaSpace);
}
}
Handle method is called from the framework. Now i am not sure why i am getting the exception as below
Caused by: org.springframework.transaction.InvalidIsolationLevelException: Jini Transaction Manager does not support serializable isolation level
at org.openspaces.core.transaction.manager.AbstractJiniTransactionManager.applyIsolationLevel(AbstractJiniTransactionManager.java:271)
at org.openspaces.core.transaction.manager.AbstractJiniTransactionManager.doJiniBegin(AbstractJiniTransactionManager.java:251)
at org.openspaces.core.transaction.manager.AbstractJiniTransactionManager.doBegin(AbstractJiniTransactionManager.java:207)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:255)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
None of the classes are marked as transactional, i am not sure why TransactionInterceptor is being invoked when i have not marked any class or any method as transaction it should not be of any concern. I also used Transaction tx = gigaSpace.getCurrentTransaction(); to check that transaction is not active it comes as null only
i am confused when none of the classes are marked as transactional why is spring trying to invoke this method under transaction
Looks like Gigaspaces transaction manager is based upon Spring transaction management infrastructure as can be inferred from the documentation here - so if you are using Gigaspaces transaction API then you are using Spring transaction management. Also worth looking is any transaction manager configuration inside xml configuration files which would point the exact transaction manager class.
Its indeed seems that transactions are in use, check if you define a transaction manger in your pu.xml, in particular check jobLauncher initialization.

Logging Exception into the DB - Java Spring

Hi I am trying to log exception into the DB
public void a(){
try{
String c = b();
}catch (Throwable ex){
com.log.Logger.log(ex);
throw new UserDefinedException(ex);
}
}
public String b(){
throw new NullPointerException("Transaction Logger")
}
I have a LoggerImpl class which logs the details of the exception into DB.
Only the UserDefinedException is getting logged where as the Null Pointer Exception is not. Could any one Plz help me.
LogEntry.java
private long id;
private String desc;
// getters and setters
Logger.java
public long log(Throwble ex){
LogEntry entry = new LogEntry();
entry.setDesc(ex.getMessage());
LoggerImpl log = new LoggerImpl();
log.insertLog(entry);
return entry.getId();
}
LoggerImpl.java
#Transactional(propogation = PROPOGATION.REQUIRES_NEW)
public void insertLog(LogEntry log){
insert.update(//fields);
}
Id is generated using a Sequence Incrementer
I am using JTA Transaction Manager.
You got such results because in your case LoggerImplementation class isn't managed by Spring container and container doesn't start new transaction for insertLog(..) method (as it supposed to be). To make this class managed you should inject it in your bean. I propose you to make such refactoring. This will work.
Instead of having Logger and LoggerImpl classes create Logger interface and LoggerImpl class that implements this interface.
public interface Logger {
long log(Throwable ex);
}
#Transactional
#Component
public final class LoggerImpl implements Logger {
#PersistenceContext
private EntityManager entityManager;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public long log(Throwable ex) {
LogEntry entry = new LogEntry();
entry.setDescription(ex.getMessage());
entityManager.persist(entry);
return entry.getId();
}
}
And then simply inject your exceptions Logger into necessary service class. For example:
#Transactional(rollbackFor=Exception.class)
#Service
public class TestService {
#Autowired
private Logger logger;
public void a() throws UserDefinedException {
try {
b();
} catch (Throwable ex) {
logger.log(ex);
throw new UserDefinedException(ex);
}
}
public String b() {
throw new NullPointerException("Transaction Logger");
}
}
Now outer transaction will be rolled back but inner new transaction will write data to DB.
Note rollbackFor attribute in #Transactional annotation for TestService class. This is necessary because by default Spring will not rollback transaction for chacked exceptions. This described behaviour that you got. In your case outer transaction was rollbacked only for runtime exceptions. That's why when you try to log NullPointerException the whole transaction is rolled back and your log record isn't added to the DB. But when you try to log UserDefinedException transaction is successfully commited despite the fact that error have been thrown and your log record is written to the DB.

Resources