I'm facing a very similar problem to this: Yet another LazyInitializationException even with OpenSessionInViewFilter
I use Hibernate 4.2.7.Final. I have an entity which is mapped like this:
#Entity
#Table(...)
public class A {
...
#OneToMany(fetch=FetchType.LAZY, mappedBy="b")
private Set<B> bSet;
...
}
It loads a big amount of data, that's why I need to load it when it is required. So I load a page with this contoller request mapping:
#RequestMapping("/getDetails")
public ModelAndView showView(Model model) {
...
for(B b : myService.getBSet()) {...}
...
}
And the service is in transaction:
#Service
#Scope(value="session")
#Transactional("ora11transactionManager")
public class MyServiceImpl implements MyService {
private A a;
...
public Set<B> getBSet() {
return a.getBSet();
}
}
Transaction manager in the hibernate.cgf.xml:
<bean id="ora11sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="ora11source"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">${debug}</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.connection.characterEncoding">UTF-8</prop>
<prop key="hibernate.jdbc.use_get_generated_keys">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>mypackage</value>
</list>
</property>
</bean>
<bean id="ora11transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="ora11sessionFactory" />
</bean>
When I want to load the getDetails view, it throws the exception referencing that row in the service:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: <my-package>.A.bSet, could not initialize proxy - no Session
This is not the only lazy fetched collection I use, but anywhere else it works. The lazy loading must be in transaction, and it is in transaction (as you can see my service implementation)! I even added org.springframework.orm.hibernate4.support.OpenSessionInViewFilter in web.xml.
I can't find any solution for this, please advise!
UPDATE (The exact use of my entities):
I have a large set of As, and every A has a set of B. There is a view where I can show all As, they are in a list and showed in a datatable. At the end of every rows there is a button, which calls and action. In this action I save the selected A (in myService there is a setter for selected A). This action is in controller1. And when I want to show the Bs of an A, I set which is selected and redirect to an other view. This view is managed by an other controller, that's why I save the selected A to a service (session or singleton scoped).
#Controller
#Scope("session")
public class Controller1 {
...
public void setSelectedA(A selectedA) {
myService.setSelectedA(selectedA);
}
}
I tried to reach the set of B even in this method, but doesn't work (the whole service is transactional, I tried to set transactional annotation only to the setselectedA() and getBSet() method, but no success).
Your service is session scoped (#Scope(value="session")) but it does not make it automatically threadsafe. For example if you have a cart object(it's the same servlet session) the user may refresh his page and the page will be processed on the server from a different thread and it will be another Hibernate session but the same cart(and same Servlet session).
The problem is that entities which you cache in MySessionImpl require a live Hibernate session to trigger loading of B set - the session is closed after the first controller has finished processing.
Hibernate sessions are also not guaranteed to work properly when used from different threads so you can't extend their lives to provide lazy loading in controller B because it's processed in another thread.
So please avoid caching detached uninitialized object in your service class. So when you callreturn a.getBSet(); you are accessing session to which a was attached which does not exist in current thread.
I would refactor that code that all action is done in a threadsafe service with scope singleton(this scope is default in Spring) and it's methods should be coarse-grained - i.e. create a service that does as much as possible in single method call and that method is annotated as #Transactional.
If you need to keep a list of selected objects (for example the article IDs in a web store cart) you need only to store their identifiers(not the entities) in session scoped (per user) bean and then load them by IDs when needed in another controller/thread. To avoid extra database round trip for A entities you can enable second level cache in Hibernate.
Related
I want to be able to read the properties from certain bean definitions that are configured in my Spring configuration file (i.e. the one set by contextConfigLocation in my web.xml) at run time. I want to do this so that I can log property values so that I when I receive the log files for diagnosis I can see how the system integrators have set up the application.
Looking at the Spring debug logs I can see that they are read from the config file by the class XmlBeanDefinitionReader. I'm guessing there is a way that Spring provides for accessing the resulting bean definitions, but I can't find it.
By way of example these are the sort of bean definitions that for which I would like to read configurations.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost:3306/an_example_db</value>
</property>
<property name="user">
<value>exampleuser</value>
</property>
<property name="password">
<value>examplepwd</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="packagesToScan" value="com.my.example.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.query.substitutions">true 1, false 0, yes 'Y', no 'N'</prop>
</props>
</property>
</bean>
Please note that these are examples only. I'd like to be able to read properties for any bean definition if that is possible.
You can create a bean that plugs in the post-construction phase of all beans created at which point you can perform you logging. This can be done by a bean that implements BeanPostProcessor.
Example:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
#Component
public class BeanPostProcessorAuditChecker implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " initialized with props: " + bean);
return bean;
}
}
Things to note:
This bean can be defined using annotations (as depicted above) or XML config as usual.
The interface is intercepting all bean constructions.
The interface method implementations are expecting you to return the bean object.
In my example I just System.out.println but you really want to SLF4J or otherwise log. Also I'm trusting that the toString() method is implemented properly exposing the properties you are interested in.
If you want to filter the logging to happen only on a subset of beans and not for all beans you'll have to do that yourself in the body of the method with reflection
Snippet:
if(bean instanceof DataSource ||
bean instanceof SessionFactory) { log.debug("{}",bean); }
Since your business domain is not known some alternatives provided:
If you can pointcut your domain you want to plugin AOP is another alternative proxy approach.
You could expose JMX on the beans and consume externally (decoupled approach)
Another decoupled approach is publishing Events using ApplicationEventPublisher. This is fyi since not advisable in your case - you are interested in construction only state properties.
Before Edit
The beans you are demonstrating are 1) singletons (by virtue of Spring default scope) and 2) properties are not likely to be changing after bean setup (via set method calls).
Under these assumptions why aren't you amassing these properties values in a props file
database.driverClass=com.mysql.jdbc.Driver
database.user=exampleuser
...
inject them in the context definition
...
<property name="user" value="#{database.user}"/>
...
and use the PropertyPlaceholderConfigurer to check the values.
Note: See my own answer to this question for an example of how I solved this issue.
I am getting the following exception in my Spring MVC 4 + Hibernate 4 project:
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: com.mysite.Company.acknowledgements, could not
initialize proxy - no Session
After reading a lot of other questions about this issue, I understand why this exception occurs, but I am not sure how to fix it in a good way. I am doing the following:
My Spring MVC controller calls a method in a service
The service method invokes a method in a DAO class
The method in the DAO class fetches an entity object through Hibernate and returns it to the calling service
The service returns the fetched object to the controller
The controller passes the object to the view (JSP)
The view tries to iterate on a many-to-many association that is lazily loaded (and is thus a proxy object)
The exception is thrown because the session is closed at this point, and the proxy cannot load the associated data
I have previously worked with PHP and doctrine2, and this way of doing things caused no problems. I am trying to figure out the best way to solve this, because the solutions that I found so far don't seem so great:
Load the association eagerly, potentially loading lots of unnecessary data
Call Hibernate.initialize(myObject.getAssociation()); - this means that I have to loop through associations to initialize them (I guess), and just the fact that I have to do this, kind of makes the lazy loading less neat
Using a Spring filter to leave the session open in the views, but I doubt that this is a good thing to do?
I tried to use #Transactional in my service, but without luck. This makes sense because I am trying to access data that has not yet been loaded after my service method returns. Ideally I would like to be able to access any association from within my view. I guess the drawback of initializing the associations within my service is that I have to explicitly define which data I need - but this depends on the context (controller) in which the service is used. I am not sure if I can do this within my controller instead without losing the abstraction that the DBAL layer provides. I hope that makes sense. Either way, it would be great if I didn't have to always explicitly define which data I want to be available to my view, but just let the view do its thing. If that is not possible, then I am just looking for the most elegant solution to the problem.
Below is my code.
View
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1><c:out value="${company.name}" /> (ID: <c:out value="${company.id}" />)</h1>
<c:forEach var="acknowledgement" items="${company.acknowledgements}">
<p><c:out value="${acknowledgement.name}" /></p>
</c:forEach>
Controller
#Controller
public class ProfileController {
#Autowired
private CompanyService companyService;
#RequestMapping("/profile/view/{id}")
public String view(Model model, #PathVariable int id) {
Company company = this.companyService.get(id);
model.addAttribute("company", company);
return "viewCompanyProfile";
}
}
Service
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyDao companyDao;
#Override
public Company get(int id) {
return this.companyDao.get(id);
}
}
DAO
#Repository
#Transactional
public class CompanyDaoImpl implements CompanyDao {
#Autowired
private SessionFactory sessionFactory;
#Override
public Company get(int id) {
return (Company) this.sessionFactory.getCurrentSession().get(Company.class, id);
}
}
Company entity
#Entity
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// Other fields here
#ManyToMany
#JoinTable(name = "company_acknowledgement", joinColumns = #JoinColumn(name = "company_id"), inverseJoinColumns = #JoinColumn(name = "acknowledgement_id"))
private Set<Acknowledgement> acknowledgements;
public Set<Acknowledgement> getAcknowledgements() {
return acknowledgements;
}
public void setAcknowledgements(Set<Acknowledgement> acknowledgements) {
this.acknowledgements = acknowledgements;
}
// Other getters and setters here
}
Acknowledgement entity
#Entity
public class Acknowledgement {
#Id
private int id;
// Other fields + getters and setters here
}
mvc-dispatcher-servlet.xml (a part of it)
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.mysite.company.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
Thanks in advance!
As #Predrag Maric said writing service methods with different initialization for entity assotiations might be an option.
OpenSessionInView is a quit discussable pattern, which can be a solution in your case, though.
Another option is to set
<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
property. It is designed to solve org.hibernate.LazyInitializationException problem and is available since hibernate 4.1.6.
So what does this property do? It simply signals Hibernate that it
should open a new session if session which is set inside current
un-initialised proxy is closed. You should be aware that if you have
any other open session which is also used to manage current
transaction, this newly opened session will be different and it may
not participate into the current transaction unless it is JTA. Because
of this TX side effect you should be careful against possible side
effects in your system.
Find more information here: http://blog.harezmi.com.tr/hibernates-new-feature-for-overcoming-frustrating-lazyinitializationexceptions and
Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans
The simplest and most transparent solution is the OSIV pattern. As I'm sure you aware, there is plenty of discussion about this (anti)pattern and the alternatives both on this site and elsewhere so no need to go over that again. For example:
Why is Hibernate Open Session in View considered a bad practice?
However, in my opinion, not all criticisms of OSIV are entirely accurate (e.g. the transaction will not commit till your view is rendered? Really? If you are using the Spring implementation then https://stackoverflow.com/a/10664815/1356423)
Also, be aware that JPA 2.1 introduced the concept of Fetch Graphs which give you more control over what is loaded. I haven't tried it yet but maybe this if finally a solution for this long standing problem!
http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html
For others who are looking for a solution, here is how I solved the problem.
As Alan Hay pointed out, JPA 2.1+ supports entity graphs, which ended up solving my problem. To make use of it, I change my project to use the javax.persistence.EntityManager class instead of the SessionFactory, which would then hand me the current session. Here is how I configured it in my dispatcher servlet configuration (some things excluded):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:/comp/env/jdbc/postgres" expected-type="javax.sql.DataSource"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="dk.better.company.entity, dk.better.user.entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
</beans>
Below is an example of a DAO class.
#Transactional
public class CompanyDaoImpl implements CompanyDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Company get(int id) {
EntityGraph<Company> entityGraph = this.entityManager.createEntityGraph(Company.class);
entityGraph.addAttributeNodes("acknowledgements");
Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", entityGraph);
return this.entityManager.find(Company.class, id, hints);
}
}
I found that if I did not use #PersistenceContext but Autowired instead, then the database session was not closed when rendering the view, and data updates were not reflected in subsequent queries.
For more information on entity graphs, I encourage you to read the following articles:
http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html
http://www.thoughts-on-java.org/2014/04/jpa-21-entity-graph-part-2-define.html
My vote goes to Hibernate.initialize(myObject.getAssociation()) in service layer (which also means #Transactional should be moved from DAO to service methods)
IMHO, service methods should return all data that is required by the caller. If the caller wants to display some association on the view, it is service's responsibility to supply that data. This means you could have several methods that apparently do the same thing (return Company instance), but depending on the caller, different associations could be fetched.
On one project we had kind of configuration class, which contained information on what associations should be fetched, and we had a single service method which also accepted that class as a parameter. This approach meant we have only one method which is flexible enough to support all callers.
#Override
public Company get(int id, FetchConfig fc) {
Company result = this.companyDao.get(id);
if (fc.isFetchAssociation1()) {
Hibernate.initialize(result.getAssociation1());
}
...
return result;
}
Trigger in the service layer requires lazy loading of the Set's size method.
Tools:
public class HibernateUtil {
/**
* Lazy = true when the trigger size method is equal to lazy = false (load all attached)
*/
public static void triggerSize(Collection collection) {
if (collection != null) {
collection.size();
}
}
}
in your service method:
Apple apple = appleDao.findById('xxx');
HibernateUtil.triggerSize(apple.getColorSet());
return apple;
then use apple in controller, everything is ok!
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional.
So I want to use AspectJ, but I can not get session by sessionFactory.getCurrentSession(), which leads to following Exception.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I have added VM arguments at runtime.
-javaagent:/Users/xx/workStation/workspace/SpringHibernate/src/lib/spring-instrument-3.2.8.RELEASE.jar
Here is configuration file
aop.xml
<aspectj>
<weaver options="-Xset:weaveJavaxPackages=true -showWeaveInfo">
</weaver>
beans.xml
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref = "dataSource"/>
<property name="mappingResources">
<list>
<value>config/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
hibernate.format_sql=false
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref = "sessionFactory"/>
<property name="nestedTransactionAllowed" value = "true"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="txManager" />
<context:load-time-weaver />
Thank you for your answers.
If I removed aop.xml I got a warning:
warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified.
else I got following info
[AppClassLoader#546b97fd] weaveinfo Join point 'method-execution(java.util.List com.yonghui.sh.service.impl.UserServiceBean.getUsers())' in Type 'com.yonghui.sh.service.impl.UserServiceBean' (UserServiceBean.java:106) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:59)
[AppClassLoader#546b97fd] weaveinfo Join point 'method-execution(void com.yonghui.sh.service.txtest.TxTestB.txC())' in Type 'com.yonghui.sh.service.txtest.TxTestB' (TxTestB.java:32) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:59)
.
I think classes are weaved. I just use JUnit to run some tests not in web context.
It seems that the problem is "static".
static UserService userService;
static TxTest txTest;
static TxTestX txTestx;
#BeforeClass
public static void setUpBeforeClass() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
userService = (UserService)ac.getBean("UserService");
txTest = (TxTest)ac.getBean("txTestA");
txTestx = (TxTestX)ac.getBean("txTestX");
}
If I don't use JUnit it works well.
public class TxTestMain {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
TxTest txTestA = (TxTest)ac.getBean("txTestA");
txTestA.txA();
}
}
How comes??
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional.
I dont know if it is a problem here But a bean can invoke its own method through proxy. Just implement in your bean interfaces like ApplicationContextAware and BeanNameAware and having this two you can ask the context about the bean where you actually are and cal any method through proxy (in transaction). This of course work only when the scope is SINGLETON if it is PROTOTYPE there are also some ways to do that through API.
The amount of grey hair has dramatically increased in last couple of days while trying to resolve the following problem. I'm using Spring Data JPA repositories in custom event listeners that utilises simple Spring 3.2 eventing mechanism. The problem I'm having is that if ListenerA creates an entity and calls assetRepository.save(entity) or assetRepository.saveAndFlash(entity) the subsequent calls to retrieve this same entity from another listener fails. The cause seems to be that the ListenerB can not find the original entity in the database, it seem to be still in Hibernate's cache.
The trigger for ListenerB to lock up the entity is an event fired as a result of a runnable task execution from a thread pool.
Here is my configuration:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="spring-jpa" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="#{appProps.database}" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.hbm2ddl.auto">#{appProps['hibernate.hbm2ddl.auto']}</prop>
<prop key="hibernate.show_sql">#{appProps['hibernate.show_sql']}</prop>
<prop key="hibernate.format_sql">#{appProps['hibernate.format_sql']}</prop>
<prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.impl.FSDirectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">#{appProps.indexLocation}</prop>
<prop key="hibernate.search.lucene_version">#{appProps['hibernate.search.lucene_version']}</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
I'm omitting the dataSource configuration which is an instance of ComboPooledDataSource that defines connection to Oracle database. As a side note, component scanning is used and the project is Spring MVC.
Now Java classes.
ListenerA
#Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {
#Autowired
private AssetRepository assetRepository;
#Autowired
private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor
#Override
#Transactional
public void onApplicationEvent(FileUploadedEvent event) {
Asset target = event.getTarget();
Job job = new Job(target);
assetRepository.save(job);
executor.execute(job);
}
ListenerB
#Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {
#Autowired
private AssetRepository assetRepository;
#Override
#Transactional
public void onApplicationEvent(JobStartedEvent event) {
String id = event.getJobId();
Job job = assetRepository.findOne(id); // at this point we can not find the job, returns null
job.setStartTime(new DateTime());
job.setStatus(Status.PROCESSING);
assetRepository.save(job);
}
JobStartedEvent is fired from a runnable task within TaskExecutor.
What I'm doing wrong here? I have tried to use custom event publisher that is transaction aware, but that doesn't seem to solve the problem. I have also tried to wire appropriate service instead of data repository and remove #Transactional annotations from listeners, which also have failed. Any reasonable suggestions of how to solve the problem would be welcome.
I have managed to resolve the problem thanks to the hint from #Kresimir Nesek. So the solution was to replace Spring Data repositories with appropriate services.
Here are modified classes.
Listener A
#Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {
#Autowired
private JobService service;
#Autowired
private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor
#Override
public void onApplicationEvent(FileUploadedEvent event) {
Job job = service.initJobForExecution(event.getTarget());
executor.execute(job);
}
}
In the JobService method initJobForExecution(Asset target) had to be annotated with #Transactional(propagation=Propagation.REQUIRES_NEW) for everything to work.
Listener B
#Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {
#Autowired
private JobService service;
#Override
public void onApplicationEvent(JobStartedEvent event) {
service.updateStatus(event.getJobId(), Status.PROCESSING);
}
}
While this is slightly old, I ran in to this same problem but now with Spring 4.1.1.RELEASE, Spring Data JPA 1.7.0 and Hibernate 4.3.5.Final
My scenario occurred during testing with some tests failing. During testing, our problems were caused by H2 in single connection mode, broadcasting asynchronous events and event transactionality.
Solutions
First problem was due to transaction timeout and was solved by adding MVCC=true to the H2 URL string. See: https://stackoverflow.com/a/6357183/941187
Asynchronous Events were causing issues during tests since they executed on different threads. In the event configuration, a task executor and thread pool were used. To fix, just provided an overridden configuration bean using the SyncTaskExecutor as the task executor. This will cause all events to occur synchronously.
The event transactionality was tricky. In our framework, event get broadcast from within a transaction (#Transactional). The event was then handled on another thread outside of the transaction context. This introduced a race condition since the handler often depended on the object from the transaction to have had been committed. We didn't notice the problem on our Windows development machines but it became apparent when deployed to production on Linux. The solution uses TransactionSynchronizationManager.registerSynchronization() with an implementation of TransactionSynchronization.afterCommit() to broadcast the event after committing. See http://www.javacodegeeks.com/2013/04/synchronizing-transactions-with-asynchronous-events-in-spring.html for more info and examples.
Related to #3, we had to add #Transactional(propagation = REQUIRES_NEW) for some of the service methods called from some of the event handlers.
Hopefully this helps some late comers.
May I know by configuring the data source in Spring like this:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/dev"/>
<property name="lookupOnStartup" value="false"/>
<property name="cache" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
...
</bean>
And configuring my BOC and DAO object in Spring like this:
<bean id="Dao" class="com.dao.impl.DaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="Bo" class="com.bo.impl.BoImpl">
<property name="theDao">
<ref local="Dao"/>
</property>
</bean>
Currently I am testing it with 3 users, 1 successfully insert data into DB, 1 is hung, and 1 is missing in action, meaning there is no response, no log was capture in Websphere Application Server. With 3 users concurrently using the app has failed the test case, may I know how could I ensure all these are thread safe when come to a situation when there are 1000 users using the app concurrently?
UPDATE
In response to #Adrian Shum query:
Regarding the BO thing, I'm not sure what pattern is this. But I'm BOC is stand for Business Object Controller, the purpose of having this unit class is to separate the business logic from DAO object. Eventually this will end up the XHTML/JSP is the front-end, BO is the business controller, and DAO is concern about hibernate and query construction.
In order to retrieve the session factory, every DAO object must extends the HibernateDaoSupport, this is how Spring-Hibernate Integration work according to this tutorial. Here is some code snippet:
class DAO extends HibernateDaoSupport implements IDao {
public void save( Pojo pojo ) {
getHibernateTemplate().save(pojo);
}
public void update( Pojo pojo ) {
getHibernateTemplate().update(pojo);
}
public void delete( Pojo pojo ) {
getHibernateTemplate().delete(pojo);
}
}
I know that Spring object are singleton by default. Does this means each thread will have only ONE object or the whole JVM instance will have only ONE object? What if I declare those BO and DAO object as session scope like this:
<bean id="Dao" class="com.dao.impl.DaoImpl" scope="session">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="Bo" class="com.bo.impl.BoImpl" scope="session">
<property name="theDao">
<ref local="Dao"/>
</property>
</bean>
Regarding the data update or retrieval, this could happen as the 3 users that we are testing on is actaully targeting on the same record. There might be a lock as I notice that there is a function doing this code:
Query queryA = session.createQuery("Delete From TableA where fieldA = :theID");
queryA.setParameter("theID", "XX");
queryA.executeUpdate();
Query queryB = session.createQuery("Delete From TableB where fieldB = :theID");
queryB.setParameter("theID", "YY");
queryB.executeUpdate();
// update tableB object
session.save(tableBObj);
// update each tableA object
for(TableAObj obj : TableAObjList) {
session.save(obj);
session.flush();
session.evict(obj);
}
The TableA(slave) and TableB(master) has relationship in each other. I know there is a database design between TableA and TableB but this is beyond of this question. I'm just curious whether this function could cause the concurrent issue even though I made this class as singleton?
From your problem, it is obviously that the thread-safeness is nothing to do with Spring.
There can be a lot of place that can go wrong, for example: (I don't really know what your BO means, as it seems not a well know pattern. I assume your "user" will invoke method in BO and BO will invoke DAO to do the data retrieval job)
How are you using the session factory? I wish you are not getting one session and keep on using that. It will be great to show some code snippet on how you use it.
If your BO is a singleton, does it keep any state for individual "user session"? Is any shared object used in the processing not thread-safe?
for issue related to DAO which is data retrieval and update, have you did your work to avoid dead lock? for example, function A will update table X and then table Y, while function B update Y then X. Have you done your work to make sure that, for 2 users updating the same record, the latter update won't silently overwrite the former one (in case the update is not idempotent).
There can be tons of reason causing your problem, but I believe 99.999% of them have nothing to do with Spring (or Hibernate).
I have the problem resolved. It is due to the DB2 failed to handle concurrency issues by adding a new column into the table, and make it as a primary key.