Struts2; keeping a session open for StrutsSpringTestCase JUnit tests - spring

My project architecture is Struts2 with Spring integration and JPA/Hibernate. StrutsSpringTestCase base class is utilized for JUnit integration tests.
Under normal circumstances, the following configuration in web.xml keeps a single session open from start to finish of each request:
<filter>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
As a result, all lazy loading works fine in all services. For example:
#Override
public Person find(int id) {
Person person = personDao.find(id);
// Take care of lazy loading before detaching the object for
// the view layer...
person.getGender().getCode();
// Detach the object so that it can be used for data transfer
// (as a DTO) without causing JPA issues and errors...
getEntityManager().detach(person);
return person;
}
Now... issues arise when I try to run the integration tests, which are independent of the OpenEntityManagerInViewFilter configuration in web.xml.
What happens is that since there is no session being kept open from start to finish of each request, lazy loading statements like "person.getGender().getCode()" don't work any longer, and I get the "could not initialize proxy - no Session" errors.
One solution I'm aware of is to force the #Transactional annotation upon the service methods that are having the lazy-loading issues, which will result in a session being open from start to finish of the method call. I tested it and it fixed the problem:
#Transactional
#Override
public Person find(int id) {
Person person = personDao.find(id);
// Take care of lazy loading before detaching the object for
// the view layer...
person.getGender().getCode();
// Detach the object so that it can be used for data transfer
// (as a DTO) without causing JPA issues and errors...
getEntityManager().detach(person);
return person;
}
However, this could be overkill as the method doesn't need a transaction under normal circumstances. I'm wondering if there is another solution that doesn't require to compromise on the service side.
Is there something I can add to my test classes (which extend StrutsSpringTestCase) to keep the session open? Or is there perhaps an elegant configuration solution on the Spring or JUnit side?
Here is my Spring configuration file - applicationContext.xml:
<?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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
default-dependency-check="all"
default-lazy-init="false"
default-autowire="byName">
<!-- *************** MAIN CONFIGURATION SECTION *************** -->
<!-- Bean post-processor for JPA annotations. -->
<!-- Make the Spring container act as a JPA container and inject an EnitityManager from
the EntityManagerFactory. -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
autowire="no"
dependency-check="none" />
<!-- ** Data Source Configuration ** -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
autowire="no"
dependency-check="none">
<!-- Database configuration: -->
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost/**********" />
<property name="user" value="**********" />
<property name="password" value="**********" />
<!-- C3P0 pooling properties configuration: -->
<property name="acquireIncrement" value="4" />
<property name="initialPoolSize" value="4" />
<property name="minPoolSize" value="4" />
<property name="maxPoolSize" value="20" />
<property name="maxIdleTime" value="600" />
<property name="maxConnectionAge" value="1800" />
</bean>
<!-- ** JPA Vendor Selection ** -->
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
autowire="no"
dependency-check="none" />
<!-- ** JPA Vendor and Entity Manager Configuration ** -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
autowire="no"
dependency-check="none">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<!-- Have the JPA vendor manage the database schema: -->
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.max_fetch_depth">4</prop>
<prop key="hibernate.jdbc.batch_size">1000</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
</props>
</property>
</bean>
<!-- ** Transaction Manager Configuration ** -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
autowire="no"
dependency-check="none">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- ** Transaction Annotation Configuration; classes/functions with #Transactional will
get a framework transaction. ** -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- **** DETAILED SERVICE BEAN CONFIGURATION WAS TAKEN OUT TO SHORTEN THE FILE **** -->
</beans>
I would appreciate any pointers.
EDIT:
To make things a bit more visual, the following test generates an exception when the service method in question encounters lazy loading and the service method is not annotated with #Transactional, but works just fine when the service method is annotated with #Transactional.
public class ActionTest extends CustomActionTestBase {
public ActionTest() {
super("/web/someAction"); // the action to test
}
#Override
public void testHelperActionLoggedIn() throws Exception {
procApplyContinualSessionForAdmin(); // the numerous steps to get logged in
procExecuteAction(
helpGetPrimaryActionURI(), // use the action URI set by the constructor above
helpPrepareActionParams( ) // no parameters are passed to this action
);
procConfirmOutcome(ActionSupport.SUCCESS,0,0,0,false);
}
}
Note: CustomActionTestBase extends StrutsSpringTestCase (which in turn extends some JUnit stuff). I needed CustomActionTestBase due to some heavy test case customization/automation.
EDIT:
I also tried adding #Transactional to the "testHelperActionLoggedIn()" test method itself, which didn't change the outcome.
EDIT:
Additionally, I tried to make things more Spring-specific (as instructed by Aleksandr M) by annotating with #RunWith, #ContextConfiguration, and #Test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class ActionTest extends CustomActionTestBase {
public ActionTest() {
super("/web/someAction"); // the action to test
}
#Test
#Override
public void testHelperActionLoggedIn() throws Exception {
procApplyContinualSessionForAdmin(); // the numerous steps to get logged in
procExecuteAction(
helpGetPrimaryActionURI(), // use the action URI set by the constructor above
helpPrepareActionParams( ) // no parameters are passed to this action
);
procConfirmOutcome(ActionSupport.SUCCESS,0,0,0,false);
}
}
It resulted in an exception that showed up in the JUnit Failure Trace - there was no exception output in the console for whatever reason.
Exception details:
java.lang.NullPointerException
at org.apache.struts2.StrutsTestCase.getActionMapping(StrutsTestCase.java:196)
at org.apache.struts2.StrutsTestCase.getActionMapping(StrutsTestCase.java:206)
at com.mycompany.utils.test.CustomActionTestBase.examineActionMapping(CustomActionTestBase.java:402)
at com.mycompany.utils.test.CustomActionTestBase.procExecuteAction(CustomActionTestBase.java:158)
at com.mycompany.utils.test.CustomActionTestBase.execLoginActionForAdmin(CustomActionTestBase.java:505)
at com.mycompany.utils.test.CustomActionTestBase.procApplyContinualSessionForAdmin(CustomActionTestBase.java:106)
at com.mycompany.actions.web.ActionTest.testHelperActionLoggedIn(ActionTest.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
It looks like it's having trouble with getting the action mapping, which it didn't before.

You can put #Transactional annotation over test method and you need to run your tests with spring in order it could find #Transactional annotation. To use JUnit4 in Struts2 tests you need to extend StrutsSpringJUnit4TestCase. So your test class should look something like that:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class ActionTest extends StrutsSpringJUnit4TestCase {
#Transactional
#Test
public void testHelperActionLoggedIn() throws Exception {
// ...
}
}
Note: If you need to obtain ActionProxy you can get it by calling getActionProxy method. You probably need to create new session map for it and then you can call execute.
ActionProxy actionProxy = getActionProxy("/action");
Map<String, Object> sessionMap = new HashMap<String, Object>();
actionProxy.getInvocation().getInvocationContext().setSession(sessionMap);
actionProxy.execute();
BUT if you don't need reference to ActionProxy then you can use executeAction method to execute action in this way you don't need to create new session map.
executeAction("/action");

Related

java.lang.ClassCastException: com.sun.proxy.$Proxy62 cannot be cast to org.hibernate.engine.spi.SessionImplementor

I'm having an issue with Spring and Hibernate Search. Here is my code:
#Slf4j
#Repository
public class DefaultIndexBuilderDao implements IndexBuilderDao {
#PersistenceContext
#Getter
#Setter
private EntityManager entityManager;
#Override
public void rebuildIndex() {
try {
log.debug("Starting the reindex process...");
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(getEntityManager());
fullTextEntityManager.createIndexer().startAndWait();
log.debug("Reindex complete.");
} catch( InterruptedException e ) {
log.warn("Error rebuilding index: {}", e.getMessage(), e);
}
}
}
I'm getting:
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy62 cannot be cast to org.hibernate.engine.spi.SessionImplementor
at org.hibernate.search.impl.FullTextSessionImpl.<init>(FullTextSessionImpl.java:62)
at org.hibernate.search.impl.ImplementationFactory.createFullTextSession(ImplementationFactory.java:35)
at org.hibernate.search.Search.getFullTextSession(Search.java:45)
at com.domainwww.dao.DefaultIndexBuilderDao.rebuildIndex(DefaultIndexBuilderDao.java:38)
at com.domainwww.service.DefaultIndexBuilderService.rebuildIndex(DefaultIndexBuilderService.java:30)
at com.domainwww.beans.admin.IndexBean.reindex(IndexBean.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.el.parser.AstValue.invoke(AstValue.java:247)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:267)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:107)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
... 57 more
Here are the versions from my POM (properties) (in case this is a version conflict)
<spring.version>5.3.1</spring.version>
<hibernate.version>5.4.24.Final</hibernate.version>
<hibernate.search.version>5.11.7.Final</hibernate.search.version>
Here is by bean for entityManager:
<bean id="entityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="net.xxxx,com.xxxx.dbmanager3.settings" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.dialect.storage_engine">innodb</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.search.default.directory_provider">filesystem</prop>
<prop key="hibernate.search.default.indexBase">/opt/xxx/lucene/indexes</prop>
</props>
</property>
</bean>
So I get that Spring is injecting a Proxy, so how should I be obtaining the FullTextEntityManager, unwrapping seems to leave me with the same error? Thanks!
This is typically what happens when using Hibernate Search 5.11.5.Final and below with Spring boot 2.4 / Spring 5.3. See https://github.com/spring-projects/spring-framework/issues/26090
Given the ClassCastException occurs at FullTextSessionImpl.java:62, I suspect you are not actually using Hibernate Search 5.11.7.Final: in Hibernate Search 5.11.7.Final, this line is just an instanceof test. In 5.11.5.Final and below, this line actually is a cast.
I see you set property hibernate.search.version to 5.11.7.Final, but did you use this property anywhere in your POM? Spring Boot doesn't manage the dependency to Hibernate Search, so you need to specify the version yourself in your <dependency> markup.

tx:annotation-driven not working

Hello friend i'm developing spring(4.0.3) and hibernate(4.3.6) based application.
I'm facing following error when I saved any object in session factory:
org.hibernate.HibernateException: save is not valid without active transaction
20:38:59,881 ERROR [STDERR] at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:348)
And following is the entry which I have used in my application-context.xml
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryAthena" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
On more thing I'd like to bring here if I used any value in transaction-manager attribute instead actual transactionManager for bean reference then its not throwing error.
So i think its not taking reference bean value.
Please help me!!
You should take a look in this link but follows an example using xml.
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryAthena" />
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<!-- the transactional advice (what happens; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with get are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name=""/>
</tx:attributes>
</tx:advice>
But nowadays I have been seeing the spring community using declarative transactions with annotations. Like the example below:
#Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void updateFoo(Foo foo) {
// do something
}
}
Use #EnableTransactionManagement at the top of your class:
#Component
#EnableTransactionManagement
public class Abc{
}

Spring + #Transactional: can't rollback in case of error

I am trying to implement the following: I need to add two different entities in same same transaction to database.
I have different DAO classes and Service classes for each entity.
public class InvoicesDAO {
#Autowired
protected SessionFactory sessionFactory;
public void save(Invoice object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.persist(object);
}
}
public class RequestsDAO {
#Autowired
protected SessionFactory sessionFactory;
public void save(Request object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.persist(object);
}
}
public class InvoicesService {
#Autowired
private InvoicesDAO invoicesDAO;
#Autowired
private RequestsDAO requestsDAO;
#Transactional
public void add(Invoice object) throws HibernateException {
invoicesDAO.save(object);
}
#Transactional
public void updateAndGenerate(Invoice object1, Request object2) throws HibernateException {
invoicesDAO.save(object1);
requestsDAO.save(object2);
}
}
The config:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/hibernate.properties" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${hibernate.connection.driver_class}" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.ejl.butler.object.data" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<context:annotation-config />
<context:component-scan base-package="com.service" />
<bean id="invoicesDao" class="com.dao.InvoicesDAO" />
<bean id="requestsDao" class="com.dao.RequestsDAO" />
Controller:
//***
/**
* Invoices access service
*/
#Autowired
private InvoicesService invoicesService;
// objects creation
invoicesService.updateAndGenerate(invoice, request);
//***
So when I am trying to call updateAndGenerate method and pass there invalid values for object2 - it fails without rolling back the object1. How can I fix it? Thank you
I dont think it is got to do with Proxies. You dont need a proxy object here. Generally you need a proxy object for instances such for a login service etc where you need a proxy object for the singleton bean definition. But, the only way it can not rollback is if your propogation level on the Transaction isnt correct.
If you use a Trasaction.REQUIRES_NEW then the dao.save wouldnt rollback and it wouldnt tie back to the outer transaction and hence wouldnt rollback.
Finally I figured out where the problem was so I will answer my own question...
According to Declarative transactions (#Transactional) doesn't work with #Repository in Spring and https://stackoverflow.com/a/3250959/705869 the order of the base-package items inside context:component-scan directive is very important. In additional, you should put only really necessary packages.
I had some duplicates inside this directive so the application context was initialized before database context. And that's why transactions were disabled inside services!
So check twice for base-package packages inside context:component-scan and remove unnecessary ones.

Is JTA manager necessary to use transaction features in hibernate 4

I'm following the tutorial here:
http://www.javacodegeeks.com/2013/05/hibernate-4-with-spring.html
to enable the "#Transactional annotation" in my Java web application but failed to make it run properly. Please advise if the JTA manager is really required, and why?
Please note that my webapp is based on Spring 3 + Hibernate 4 + Tomcat 7.
Background and my doubts:
My current web application uses my own custom class (implements HandlerInterceptor) to enable one-hibernatesession-per-request basis. Now I want to improve my application's maintainability by using the "#Transactional annotation" instead since that could save many lines of code.
According to my understanding, the #Transactional basically relies on the AOP concept to ensure the session (Hibernate session) is ready for use in the annotated method. This seems nothing to do with the JTA. But I wonder why can't I make it work on my webapp in Tomcat 7 (without JTA-provider).
After few searches on google, it looks like the JTA is required. This confuses me since this seems to be a very basic functionality that shouldn't have the complicated JTA-provider as a requirement.
Here is the error I got:
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:988)
...
This is the code I use for testing:
....
#Autowired
org.hibernate.SessionFactory sessionFactory;
#Transactional
#RequestMapping(method = RequestMethod.GET)
protected String home() {
Session session = sessionFactory.getCurrentSession(); // I expected the session is good to use now
Province p = (Province) session.get(Province.class, 1L); // This causes no session found error :(
return "home";
}
The spring XML:
....
<tx:annotation-driven/>
<context:component-scan base-package="..."/>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/..."/>
<property name="lookupOnStartup" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
....
Thank you !
Just a speculation:
Your controller is defined in some kind of dispatcher-servlet.xml therefore seperates from the applicationContext in which < tx:annotation-driven/> is defined. The compoment you want to enhance with #Transactional need to be within the same context with < tx:annotation-driven> if I'm not mistaken. So the #Transactional does not work.
That was my silly mistake. The Spring uses CGLIB to proxy methods with #Transactional annotated and it seems like CBLIB can't enhance protected method.
protected String home() {
Changing this to
public String home() {
fixed the problem.

Hibernate-Spring Web container error

Hello I'm new to Hibernate.
I have generated with Hibernate Tools a database access module. The generator generates the code of the DAOS and Hibernate Beans.
When I test this module in a simple Java application all works fine, but when I test it in a Spring Web application I get a very strange error. Since my module is an independent jar it should access the database without regarding the circumstance of being executed in a simple Java application or a Web application. The code of my web application is:
#Controller
#RequestMapping("/")
public class Controller implements ApplicationContextAware
{
private ApplicationContext applicationContext;
#RequestMapping(value = "/purchased/songs", method = RequestMethod.GET)
public String home(Model model)
{
SessionManager.startOperation();
ChargeTryDAOBase ctdb=new ChargeTryDAOBase();
List <ChargeTry> data=ctdb.findByRemoteId("dsfsdfsdf8");
SessionManager.endOperation();
model.addAttribute("result", "data" );
return "home";
}
#Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException
{
this.applicationContext = arg0;
}
}
When running this code on Tomcat I get following error:
org.springframework.web.util.NestedServletException: Handler processing
nested exception is java.lang.NoSuchMethodError:
org.hibernate.SessionFactory.getCurrentSession()Lorg/hibernate/Session;
.....
java.lang.NoSuchMethodError:
org.hibernate.SessionFactory.getCurrentSession()Lorg/hibernate/Session;
When I change some Hibernate dependencies I get following error:
java.lang.IllegalStateException: Could not locate SessionFactory in JNDI
When I test the above code in a simple Java application all works fine.
Is this a spring-hibernate configuration problem?
Thank you for your help.
Please study
1: http://www.javatpoint.com/hibernate-and-spring-integration
and
2 http://viralpatel.net/blogs/spring3-mvc-hibernate-maven-tutorial-eclipse-example/
to get insight of Spring MVC and Hibernate Integration.
You can work with Hibernate Configuration file - here is the link -
Spring and hibernate.cfg.xml
But as your application is within a spring managed container, We will highly recommend to use applicationcontext.xml for better maintenance and management of codebase and performance.
thank you for your help finally I got all working. I followed your link and googled a little bit. The problem was that I didn't enable in my hibernate.cfg.xml file the datasource parameter, I also have configured C3P0 jdbc connection provider.
My final hibernate.cfg.xml file is:
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">true</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="current_session_context_class">thread</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb</property>
<property name="hibernate.connection.username">userdb</property>
<property name="hibernate.connection.password">12345</property>
<property name="hibernate.connection.datasource">java:comp/env/jdbc/mydb</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.min_size">2</property>
<property name="hibernate.c3p0.numHelperThreads">4</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">1800</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<hibernate-configuration>
<session-factory>
In my web.xml I have added following lines:
<resource-ref>
<description>This is a MySQL database connection</description>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
In the Spring context file I have added following lines:
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<beans:property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<beans:property name="username" value="userdb"/>
<beans:property name="password" value="12345"/>
</beans:bean>
<beans:bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="configLocation">
<beans:value>classpath:hibernate.cfg.xml</beans:value>
</beans:property>
</beans:bean>
The strange thing is, that with the default Hibernate connection provider, the above solution didn't work but when I configured C3P0 all started to work.
Thank you for your help.

Resources