I configured Spring Aop + Transaction + Mybatis. However, Transaction not work - spring

I configured Spring Aop and Transaction.
When I run the Project Test. the Transaction does not work and i got the log output as follows: "was not registered for synchronization because synchronization is not active"
When I run LoginServiceImpl.findMemberInfo() method, I got the Transaction registered Successfully. However, I run LoginServiceImpl.insertMember, the registration fails
root-context.xml as shown below:
<context:component-scan base-package:"com.quapenus">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- transactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- txAdvice -->
<tx:annotation-driven mode="aspectj" />
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="find*" />
<tx:method name="insert*" />
</tx:attributes>
</tx:advice>
<!-- Aspect -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.quapenus.*.service.*Impl.*(..))" />
<aop:advisor pointcut-ref="pointcut" advice-ref="txAdvice" />
</aop:config>
servlet-context as alown below:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="jsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
<bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
<property name="contentType" value="application/json;charset=UTF-8" />
<property name="objectMapper" ref="jsonMapper" />
</bean>
LoginServiceImpl as shwon below:
#Service
public class LoginServiceImpl implements LoginService {
Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);
#Autowired
private LoginDao loginDao;
#Override
public Member findMemberInfo(String username) throws UsernameNotFoundException {
Member member = loginDao.findMemberInfo(username);
if(member == null){
throw new UsernameNotFoundException("아이디가 존재하지 않습니다.");
}
return member;
}
#Override
public List<GrantedAuthority> findRoles(String username) throws NotFoundException {
List<GrantedAuthority> roles = loginDao.findRoles(username);
if(roles.size() == 0){
throw new NotFoundException("해당 역할이 없습니다.");
}
return roles;
}
#Override
public void insertMember(Member member) throws RollbackException {
int result = loginDao.insertMember(member);
if(result == 1){
throw new RollbackException("Test Tx");
}
}
}
LoginDao as shown below:
#Repository
public class LoginDao {
#Autowired
private SqlSession sqlSession;
public Member findMemberInfo(String username) {
return sqlSession.selectOne("com.quapenus.common.sql.LoginMapper.findMemberInfo", username);
}
public List<GrantedAuthority> findRoles(String username) {
return sqlSession.selectList("com.quapenus.common.sql.LoginMapper.findRoles", username);
}
public int insertMember(Member member) {
return sqlSession.insert("com.quapenus.common.sql.LoginMapper.insertMember", member);
}
}
LoginServiceImpl.insertMember() Method log as shown below:
org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession#77b976a0] was not registered for synchronization because synchronization is not active
17:13:01.100 [http-nio-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
LoginServiceImpl.findMemberInfo() Method log as shown below:
org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
17:58:07.052 [http-nio-8080-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession
github repository: https://github.com/Sujin1135/Quapenus

Related

Spring Data lazyload does not LazyInitializationException

#Component
#Transactional
public class TestClass extends AbstractClass
{
#Autowire
ClassARepo classARepo;
#Override
public void test() {
ClassA classA = classARepo.findOne(1);
List<ClassB> list = classA.getClassBs();
list.size();
}
}
ClassB is mapped as onetomany and lazily loaded.
In the above code
classARepo.findOne(1);
Executes correctly. but
List<ClassB> list = classA.getClassBs();
list.size();
Fails with LazyInitializationException.
public interface ClassARepo extends CrudRepository<ClassA, Integer> {
}
Instance for TestA is created like the one below
#PersistJobDataAfterExecution
#DisallowConcurrentExecution
#Transactional
#Component
public class TestClassJOB extends AbstractJob
{
#Autowired
TestClass indexer;
}
Context:
<!-- JPA mapping configuration -->
<bean id="persistenceXmlLocation" class="java.lang.String">
<constructor-arg value="classpath:/persistence.xml"></constructor-arg>
</bean>
<!-- entity manager -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistenceUnitName="jpaData"
p:persistenceXmlLocation-ref="persistenceXmlLocation">
<property name="packagesToScan" value="com..persist.entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<!-- transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" lazy-init="true" p:dataSource-ref="dataSource" />
<!-- JPA repositories -->
<jpa:repositories base-package="com..persist.repo"
entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
I tried many resources and could not solve the issue. The following error message is displayed "could not initialize proxy - no Session".
What could be the cause of the issue?
When the session is available while classARepo.findOne(1) is called, why is not available during lazy fetch(list.size())?
The issue was the instance for TestClassJOB was created by Quartz. So the transnational proxy was not applied to the class which was the reason for the issue.
I fixed the issue by declaring a transaction template
#Autowired
TransactionTemplate transactionTemplate;
and then wrapping the code within
transactionTemplate.execute(new TransactionCallbackWithoutResult()
{
#Override
protected void doInTransactionWithoutResult(TransactionStatus status)
{
<code here>
}
}

JPA entityMangerFactory not found through Persistence.createEntityManagerFactory("XYZ")

Any help will be greatly appreciated.
We are working on a web application. Which uses a JAR file (a java maven project) and has been added as a maven dependency in the web application.
Combination of this JAR file and web application itself creating problem.
Both web application and JAR are using Hibernate JPA to interact with database. But both are using 2 different ways for creating/initializing entityManagerFactory.
Web Application uses Spring xml based configuration to initialize entityManagerFactory.
CODE:
persistence.xml code:
<persistence-unit name="org.jbpm.persistence.jpa.local"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<mapping-file>META-INF/JBPMorm-JPA2.xml</mapping-file>
<class>org.drools.persistence.info.SessionInfo</class>
<class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.info.WorkItemInfo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<property name="hibernate.max_fetch_depth" value="3" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
Spring configuration:
<context:component-scan base-package="com.company.rd.core" />
<context:component-scan base-package="com.company.rd.services" />
<jee:jndi-lookup id="testDataSource" jndi-name="java:comp/env/jdbc/SybaseDB" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="testDataSource"/>
<property name="defaultTimeout" value="120"></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="org.jbpm.persistence.jpa.local" />
<property name="dataSource" ref="testDataSource" />
<property name="jpaDialect" ref="jpaDialect" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect" ref = "jpaDialect"></property>
<property name="defaultTimeout" value="120"></property>
</bean>
<jee:jndi-lookup id="logDataSource" jndi-name="java:comp/env/jdbc/DRMLOG" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
And Here is the code to initializing entitymanagerFactory in JAR file.
persistence.xml
<persistence-unit name="codeAuthorization" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/comp/env/jdbc/SybaseDB</non-jta-data-source>
<class>com.company.auth.entity.AuthorizationCode</class>
<class>com.company.auth.entity.UserInvalidAttempt</class>
<class>com.company.auth.entity.AuthorizationProperty</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
And a java file which is injected into Base DAO through spring.
#Service
public class AuthorizationEntityMangerService {
#PersistenceUnit(name = "codeAuthorization")
private EntityManagerFactory entityManagerFactory;
public AuthorizationEntityMangerService() {
entityManagerFactory = Persistence
.createEntityManagerFactory("org.jbpm.persistence.jpa.local");
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public EntityManager getEntityManager() {
return this.entityManagerFactory.createEntityManager();
}
public void closeEntityManager(EntityManager entityManager) {
if (entityManager != null && entityManager.isOpen()) {
entityManager.close();
}
}
public EntityTransaction getTransaction(EntityManager entityManager) {
return entityManager.getTransaction();
}
public void rollBackTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
}
public void commitTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.commit();
}
}
}
Calling code from Base DAO.
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
EntityTransaction transaction = null;
EntityManager entityManager = null;
try {
entityManager = this.entityMangerService.getEntityManager();
transaction = entityMangerService.getTransaction(entityManager);
transaction.begin();
final Query query = entityManager.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
final Object result = query.getSingleResult();
entityMangerService.commitTransaction(transaction);
return result;
} catch (final NoResultException e) {
entityMangerService.rollBackTransaction(transaction);
logger.error("Error" : " + e.getMessage());
return null;
} finally {
entityMangerService.closeEntityManager(entityManager);
}
}
Now Here is the problem when ever line entityManager.createQuery(queryString); execute it throws the exception.
2015-06-05 17:39:46,363 WARN DefaultExceptionHandler:94 - Unhandled exception caught by the Stripes default
exception handler.
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: AuthorizationProperty is
not mapped [SELECT pe.value FROM AuthorizationProperty pe WHERE pe.name=:propertyName AND pe.deleted=0]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke
(ExtendedEntityManagerCreator.java:334)
at com.sun.proxy.$Proxy53.createQuery(Unknown Source)
at com.company.authentication.dao.AuthorizationBaseDAO.getSingleResult(AuthorizationBaseDAO.java:40)
at com.company.authentication.dao.PropertyDAOImlp.getPropertyValue(PropertyDAOImlp.java:22)
at com.company.authentication.services.AuthorizationPropertyService.getPropertyValueByName
(AuthorizationPropertyService.java:19)
at com.company.rd.servlet.JspAuthorizationRestFilter.hasAuthorizationCode
(JspAuthorizationRestFilter.java:105)
at com.company.rd.servlet.AbstractAuthorizationRestFilter.isRequestAuthenticated
(AbstractAuthorizationRestFilter.java:120)
at com.company.rd.servlet.JspAuthorizationRestFilter.doFilter(JspAuthorizationRestFilter.java:84)
I have debugged the code and found entityManagerFactory for persistenceUnit "codeAuthorization" is not initialized. Only "org.jbpm.persistence.jpa.local" is available (verified through eclipse debugger) inside this method.
Note: This JAR is working fine in some other application where web application and JAR using same way to initialize entityMangerFactory [through Persistence.createEntityManagerFactory("")].
Please let me know How can I get "codeAuthorization" entiryManagerFactory
You are using Spring then use Spring, currently you are doing a lot of work to work around Spring and dependency injection and managed transaction. Don't use Persistence.createEntityManagerFactory(""). Just inject the EntityManager where you need it using an EntityManager field annotated with #PersistenceContext and specify the name of the one you want.
Also don't manage the transactions, entity manager yourself, spring does that for you. For this use the right PlatformTransactionManager the JpaTransactionManager and not the DatasourceTransactionManager as that won't work in a JPA environment. (At least not to manage your JPA transactions).
Doing this will really simplify your code and your life.
So basically ditch the service that is doing those nasty things and simple do things like this in your dao.
#Repository
public class YourDao {
#PersistenceContext(name="codeAuthorization")
private EntityManager em;
#Transactional
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
final Query query = em.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
return query.getSingleResult();
}
}
In your configuration replace the DatasourceTransactionManager with the JpaTransactionManager and add <tx:annotation-driven />. Then clean your code.
Note: The JpaTransactionManager is perfectly capable of managing plain JDBC transactions if you still need those, ideally you would have a single transaction manager.

spring unable to manage two entity manager

Hi all i ave an application with two different databases.
The first db is for our business app, the second belong to a framework we are using.
I have a manager in witch i need to access both dbs to perform some operations.
I configured my app with two entityManagerFactory, one for each datasource, and two transaction managers, one for each entityManagerFactory...
Here my configuration
my persistence.xml
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="ENPIM_BD_PU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>jdbc/businessDataSource</non-jta-data-source>
<class>it.aicof.projects.enpim.persistence.entities.AccountingRecords</class>
.....
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="ENPIM_SERV_PU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>it.aicof.projects.enpim.persistence.entities.Authusers</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
</persistence>
my app-context.xml
<context:annotation-config/>
<context:component-scan base-package="it.aicof.projects.*"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="dataSourceBusiness" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${profile.database.driverClassName}" />
<property name="jdbcUrl" value="jdbc:postgresql://${profile.database.hostname}:${profile.database.port}/${profile.database.business}" />
<property name="user" value="${profile.database.username}" />
<property name="password" value="${profile.database.password}" />
<!-- these are C3P0 properties -->
<property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
<property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
<property name="minPoolSize" value="${c3p0.minPoolSize}" />
<property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
<property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
</bean>
<bean id="dataSourceServ" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${profile.database.driverClassName}" />
<property name="jdbcUrl" value="jdbc:postgresql://${profile.database.hostname}:${profile.database.port}/${profile.database.serv}" />
<property name="user" value="${profile.database.username}" />
<property name="password" value="${profile.database.password}" />
<!-- these are C3P0 properties -->
<property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
<property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
<property name="minPoolSize" value="${c3p0.minPoolSize}" />
<property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
<property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
</bean>
<bean id="transactionManagerBusiness" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory">
<qualifier value="business" />
</bean>
<bean id="transactionManagerServ" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactoryServ">
<qualifier value="serv" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSourceBusiness"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<property name="persistenceUnitName" value="ENPIM_BD_PU"></property>
</bean>
<bean id="entityManagerFactoryServ"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSourceServ"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<property name="persistenceUnitName" value="ENPIM_SERV_PU"></property>
</bean>
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="POSTGRESQL" p:showSql="false"/>
Here is the code i'm using
my dao
#PersistenceContext(unitName = "ENPIM_BD_PU")
protected EntityManager em;
#PersistenceContext(unitName = "ENPIM_SERV_PU")
protected EntityManager emServ;
....
public void edit(AddressBook addressBook) throws NonexistentEntityException, Exception {
try {
addressBook = em.merge(addressBook);
} catch (Exception ex) {
Logger.getLogger(this.getClass().getName()).error("edit", ex);
String msg = ex.getLocalizedMessage();
if (msg == null || msg.length() == 0) {
Integer id = addressBook.getId();
if (findAddressBook(id) == null) {
throw new NonexistentEntityException("The addressBook with id " + id + " no longer exists.");
}
}
throw ex;
}
}
public void createBeneficiaryLoginAccount(String username, String password) throws Exception{
try {
Query q = emServ.createNativeQuery("INSERT INTO authusers(username, passwd, registrationdate, lastaccess, lastpasswordchange, active)" +
" VALUES (?, ?, ?, ?, ?, ?)");
q.setParameter(1, username);
q.setParameter(2, password);
q.setParameter(3, new Date());
q.setParameter(4, new Date());
q.setParameter(5, new Date());
q.setParameter(6, 1);
q.executeUpdate();
}catch (Exception ex) {
Logger.getLogger(this.getClass().getName()).error("createBeneficiaryAccount", ex);
throw ex;
}
}
my manager
#Override
public void createBeneficiaryAccount(AddressBook addressBook, String password) {
String username = addressBook.getUsername();
try {
AddressBook ab = this.addressBookJpaDAO.findAddressBook(addressBook.getId());
ab.setUsername(username);
//creo l'utente sul db serv e gli assegno il ruolo di beneficiario
this.createBeneficiaryLoginAccount(username, password);
this.createBeneficiaryUsername(ab, password);
} catch (Throwable t) {
Logger.getLogger(this.getClass().getName()).error("", t);
}
}
#Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class, value = "serv")
private void createBeneficiaryLoginAccount(String username, String password) throws Throwable {
try {
//creo l'utente sul db serv e gli assegno il ruolo di beneficiario
this.addressBookJpaDAO.createBeneficiaryLoginAccount(username, password);
} catch (Throwable t) {
Logger.getLogger(this.getClass().getName()).error("createLoginAccount", t);
throw t;
}
}
#Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class, value = "business")
private void createBeneficiaryUsername(AddressBook addressBook, String password) throws Throwable {
try {
this.addressBookJpaDAO.edit(addressBook);
} catch (Throwable t) {
Logger.getLogger(this.getClass().getName()).error("createBeneficiaryUsername", t);
throw t;
}
}
when the method that uses the second entityManager is called i get the exception
2014-11-26 11:10:35,844 ERROR controllers.AddressBookJpaController - createBeneficiaryAccount
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
.....
and debugging the application i found that when we try to use the second entityManager we have this error
Exception occurred in target VM: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:198)
at com.sun.proxy.$Proxy145.getTransaction(Unknown Source)
at it.aicof.projects.enpim.persistence.controllers.AddressBookJpaController.createBeneficiaryLoginAccount(AddressBookJpaController.java:854)
i take a look on the web for some example but all have the same configuration as mine.
So.. why it's not working?
what's wrong?
thanks in advance
andrea
You're annotating #Transactional at a private method. That doesn't work with the Spring Aop Proxy Beans.
The quickest way is to set both methods to public
#Transactional("serv")
public void createBeneficiaryLoginAccount(String username, String password) throws Throwable {
try {
//creo l'utente sul db serv e gli assegno il ruolo di beneficiario
this.addressBookJpaDAO.createBeneficiaryLoginAccount(username, password);
} catch (Throwable t) {
Logger.getLogger(this.getClass().getName()).error("createLoginAccount", t);
throw t;
}
}
#Transactional("business")
public void createBeneficiaryUsername(AddressBook addressBook, String password) throws Throwable {
try {
this.addressBookJpaDAO.edit(addressBook);
} catch (Throwable t) {
Logger.getLogger(this.getClass().getName()).error("createBeneficiaryUsername", t);
throw t;
}
}
Or you might want to use AspectJ mode for AOP, but that is another layer of headaches IMHO.
See: Does Spring #Transactional attribute work on a private method?

Specify a custom web.xml to an embedded tomcat

Is there a way to specify a different web.xml from the standard WEB-INF/web.xml when using an embedded tomcat instance?
I would like to put a web.xml in my src/test/resources (or some other area) and refer to that web.xml when starting the embedded tomcat.
Here is my existing code to start the tomcat instance
tomcat = new Tomcat();
String baseDir = ".";
tomcat.setPort(8080);
tomcat.setBaseDir(baseDir);
tomcat.getHost().setAppBase(baseDir);
tomcat.getHost().setAutoDeploy(true);
tomcat.enableNaming();
Context ctx = tomcat.addWebApp(tomcat.getHost(), "/sandbox-web", "src\\main\\webapp");
File configFile = new File("src\\main\\webapp\\META-INF\\context.xml");
ctx.setConfigFile(configFile.toURI().toURL());
tomcat.start();
I am starting this server from a tomcat instance and I would like to do the following when running unit tests
turn off the contextConfigLocation
specify a custom ContextLoaderListener that sets the parent ApplicationContext of the embedded tomcat.
This file might be specified like so:
File webXmlFile = new File("src\\test\\resources\\embedded-web.xml");
Edit
After much frustration I realized that no matter what I do, I cannot persuade tomcat from looking in WEB-INF for web.xml. It appears that I must ignore the web.xml altogether and set the items in the web.xml programmatically.
I ended up with this configuration:
cucumber.xml for configuring tests
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="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">
<bean id="applicationContextProvider" class="ca.statcan.icos.sandbox.ApplicationContextProvider"/>
<bean id="sandBoxDataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:testdb;shutdown=true;" />
<property name="username" value="SA" />
<property name="password" value="" />
</bean>
<!-- Support for JPA related annotation support (#PersistenceUnit and #PersistenceContext) -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- JTA Configuration -->
<bean id="jtaTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown"><value>true</value></property>
</bean>
<bean id="jtaUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
<bean id="springTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jtaTransactionManager" />
<property name="userTransaction" ref="jtaUserTransaction" />
</bean>
<!-- JPA Entity Manager configuration -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
lazy-init="true">
<property name="persistenceUnitName" value="sandBox" />
<property name="dataSource" ref="sandBoxDataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="SQL_SERVER" />
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
<property name="jpaPropertyMap">
<props>
<prop key="hibernate.archive.autodetection">class</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<!-- Second Level Cache : EHCache in dev
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop> -->
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<import resource="classpath:META-INF/applicationContext-core.xml" />
<import resource="classpath:META-INF/applicationContext-web.xml" />
</beans>
applicationContext-core.xml - where the services are configured
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd"
default-autowire="byName">
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath*:META-INF/fms-local.properties" />
<property name="systemPropertiesModeName">
<value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
</property>
</bean>
<!--
Classpath scanning to load all the service classes
-->
<context:component-scan base-package="ca.statcan"
use-default-filters="false">
<context:include-filter type="regex" expression="ca\.statcan\.icos.*\.service\..*Service" />
<context:include-filter type="regex" expression="ca\.statcan\.icos.*\.builders\..*Builder" />
</context:component-scan>
<!--
Spring TransactionManager
-->
<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
<!-- other methods use the default transaction settings -->
<tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!--
AOP Weaving for all Service methods
-->
<aop:config proxy-target-class="true">
<aop:pointcut id="icosServiceMethods" expression="execution(* ca.statcan.icos..*.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="icosServiceMethods" />
</aop:config>
</beans>
Custom ContextLoaderListener
public class EmbeddedContextLoaderListener extends ContextLoaderListener {
#Override
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
GenericWebApplicationContext context = new GenericWebApplicationContext(sc);
context.setParent(ApplicationContextProvider.getApplicationContext());
return context;
}
#Override
protected ApplicationContext loadParentContext(ServletContext servletContext) {
return ApplicationContextProvider.getApplicationContext();
}
}
Modified Embedded Tomcat Wrapper
public class EmbeddedTomcat {
/** Log4j logger for this class. */
#SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(EmbeddedTomcat.class);
private Tomcat tomcat;
public void start() {
try {
tomcat = new Tomcat();
String baseDir = ".";
tomcat.setPort(8080);
tomcat.setBaseDir(baseDir);
tomcat.getHost().setAppBase(baseDir);
tomcat.getHost().setDeployOnStartup(true);
tomcat.getHost().setAutoDeploy(true);
tomcat.enableNaming();
Context context = tomcat.addContext("/sandbox-web", "src\\main\\webapp");
Tomcat.initWebappDefaults(context);
configureSimulatedWebXml(context);
LOG.info("Starting tomcat in: " + new File(tomcat.getHost().getAppBase()).getAbsolutePath());
tomcat.start();
} catch (LifecycleException e) {
throw new RuntimeException(e);
}
}
public void stop() {
try {
tomcat.stop();
tomcat.destroy();
FileUtils.deleteDirectory(new File("work"));
FileUtils.deleteDirectory(new File("tomcat.8080"));
} catch (LifecycleException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void deploy(String appName) {
tomcat.addWebapp(tomcat.getHost(), "/" + appName, "src\\main\\webapp");
}
public String getApplicationUrl(String appName) {
return String.format("http://%s:%d/%s", tomcat.getHost().getName(),
tomcat.getConnector().getLocalPort(), appName);
}
public boolean isRunning() {
return tomcat != null;
}
private void configureSimulatedWebXml(final Context context) {
// Programmatically configure the web.xml here
context.setDisplayName("Sandbox Web Application");
context.addParameter("org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG", "/WEB-INF/tiles-defs.xml,/WEB-INF/tiles-sandbox.xml");
final FilterDef struts2Filter = new FilterDef();
struts2Filter.setFilterName("struts2");
struts2Filter.setFilterClass("org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter");
struts2Filter.addInitParameter("actionPackages", "ca.statcan.icos.sandbox.web");
context.addFilterDef(struts2Filter);
final FilterMap struts2FilterMapping = new FilterMap();
struts2FilterMapping.setFilterName("struts2");
struts2FilterMapping.addURLPattern("/*");
context.addFilterMap(struts2FilterMapping);
context.addApplicationListener("org.apache.tiles.web.startup.TilesListener");
context.addApplicationListener("ca.statcan.icos.sandbox.EmbeddedContextLoaderListener");
context.addWelcomeFile("index.jsp");
}
}
Step definitions
public class StepDefs {
#Autowired
protected EmployeeEntityService employeeEntityService;
#Given("^the following divisions exist$")
public void the_following_divisions_exist(DataTable arg1) throws Throwable {
final Employee employee = new Employee(3, "Third", "John", null, "613-222-2223");
employeeEntityService.persistEmployee(employee);
}
#Given("^there are no existing surveys$")
public void there_are_no_existing_surveys() throws Throwable {
}
#When("^I register a new survey with the following information$")
public void I_register_a_new_survey_with_the_following_information(DataTable arg1) throws Throwable {
Capabilities capabilities = DesiredCapabilities.htmlUnit();
final HtmlUnitDriver driver = new HtmlUnitDriver(capabilities);
driver.get("http://localhost:8080/sandbox-web/myFirst");
}
#Then("^the surveys are created$")
public void the_surveys_are_created() throws Throwable {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
#Then("^a confirmation message is displayed saying: \"([^\"]*)\"$")
public void a_confirmation_message_is_displayed_saying(String arg1) throws Throwable {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
}
Action class
#Results({ #Result(name = "success", location = "myFirst.page", type = "tiles") })
#ParentPackage("default")
#Breadcrumb(labelKey = "ca.statcan.icos.sandbox.firstAction")
#SuppressWarnings("serial")
public class MyFirstAction extends HappyfActionSupport {
private List<Employee> employees;
#Autowired
private EmployeeEntityService employeeEntityService;
#Override
public String execute() {
employees = employeeEntityService.getAllEmployee();
if (employees.size() == 0) {
// persist data in memory
final Employee employee1 = new Employee(1, "First", "John", null, "613-222-2222");
employeeEntityService.persistEmployee(employee1);
final Employee employee2 = new Employee(2, "Second", "John", null, "613-222-2223");
employeeEntityService.persistEmployee(employee2);
employees = employeeEntityService.getAllEmployee();
}
return SUCCESS;
}
public List<Employee> getEmployees() {
return employees;
}
}
With this, the embedded tomcat starts correctly and all seems to go well until I try to navigate to a web page. In the StepDefs class, the EmployeeEntityService is injected correctly. However, in the Action class, EmployeeEntityservice is not injected (it remains null).
According to my knowledge, I am setting the parent ApplicationContext for the embedded Tomcat correctly. So why isn't the server using the parent context to get the EmployeeEntityService?
I was stuck with a similar problem and the solution for using an alternative web.xml is simpler than one would dare to think:
2 lines:
Context webContext = tomcat.addWebapp("/yourContextPath", "/web/app/docroot/");
webContext.getServletContext().setAttribute(Globals.ALT_DD_ATTR, "/path/to/custom/web.xml");
Voila! The magic happens in org.apache.catalina.startup.ContextConfig#getWebXmlSource
Disclaimer: Tested on Tomcat 7.0.42

"No Session found for current thread" when adding #Autowire

I've seen several similar questions, but none of the suggested solutions helped me.
Summary: when I create and inject the beans on the .xml, it works; but when I use #Autowire or #Resource, it doesn't.
Environment: Spring3, Hibernate4, Tomcat7.
Details: the following setup DOES work:
web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/spring/security-context.xml
/WEB-INF/spring/users-context.xml
</param-value>
</context-param>
root-context.xml:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/venus" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.airbus.genesis.marte.dal" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
users-context.xml:
<bean id="usersDAO" class="com.airbus.genesis.marte.dal.users.UsersDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
BL object:
#Service("usersManager")
#Transactional(readOnly = true)
public class UsersManager implements IUsersManager {
#Autowired
#Qualifier("usersDAO")
private IUsersDAO usersDAO;
#Override
public List<User> getUsers() {
return usersDAO.getUsers();
}
}
DAO object (notice that #Repository and #Resource are commented):
//#Repository("usersDAO")
#Transactional(readOnly = true)
public class UsersDAO implements IUsersDAO {
// #Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
#Override
public List<User> getUsers() {
#SuppressWarnings("unchecked")
List<User> res = (List<User>) getSessionFactory().getCurrentSession()
.createQuery("from User").list();
return res;
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
But the following one DOES NOT work:
users-context.xml:
<!--
<bean id="usersDAO" class="com.airbus.genesis.marte.dal.users.UsersDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
-->
DAO object (notice that #Repository and #Resource are uncommented now):
#Repository("usersDAO")
#Transactional(readOnly = true)
public class UsersDAO implements IUsersDAO {
#Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
#Override
public List<User> getUsers() {
#SuppressWarnings("unchecked")
List<User> res = (List<User>) getSessionFactory().getCurrentSession()
.createQuery("from User").list();
return res;
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
org.hibernate.HibernateException: No Session found for current thread is raised:
org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:941)
com.airbus.genesis.marte.dal.users.UsersDAO.getUsers(UsersDAO.java:23)
com.airbus.genesis.marte.bl.users.UsersManager.getUsers(UsersManager.java:22)
[...]
The same happens if I use #Autowire instead of #Resource.
I guess it is some kind of misunderstanding on my side, but cannot find where. Any idea?
The problem is likely that #Repository and #Service annotations are being picked up in the dispatcher-servlet.xml configuration (do you use context:component-scan?), so these beans are created in the dispatcher servlet context instead of the root web app context.
A good practice is to put your service layer objects to the dedicated packages and use the specific package name as <context:component-scan/> base-package qualifier (like 'com.myproject.services'). You can also use filter expressions to include and exclude elements see examples here : #Service are constructed twice
and 4.10.3 section of the Spring documentation
See also Difference between applicationContext.xml and spring-servlet.xml in Spring Framework

Resources