This same question has been asked so many times on SO, but please read
my question fully before making it duplicates.
I don't want to use Annotation based Transaction Management, so my problem is different then questions asked here.
MY XML Declaration
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:/comp::/env/jdbc/DS</value>
</property>
</bean>
<!-- Create SessionFactory , one instance per application only -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- Just for Testing Purpose -->
<property name="mappingResources">
<list>
<value>com/mycompany/hbmapping/platform/support/Currency.hbm.xml</value>
</list>
</property>
<!-- <property name="mappingDirectoryLocations"> <value>/WEB-INF/classes/com/mycompany/hbmapping</value>
</property> -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<!-- Cache related properties -->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory
</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache.xml</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTxManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
Following DAO Declaration as a bean
<bean id="currency" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>
com.mycompany.dao.platform.support.CurrencyDao
</value>
</property>
<property name="target">
<ref bean="currencyTarget" />
</property>
</bean>
<bean id="currencyTarget"
class="com.mycompany.dao.platform.support.CurrencyDaoImpl">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
My TX Advice
<tx:advice id="txAdvice" transaction-manager="hibernateTxManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"></tx:method>
<tx:method name="update*" propagation="REQUIRED"></tx:method>
<tx:method name="delete*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
AOP Configuration
<aop:config>
<aop:pointcut
expression="within(com.mycompany.dao.platform.support.CurrencyDao)"
id="currencyPointCut" />
</aop:config>
<!-- applying advice on joint point -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut-ref="currencyPointCut" />
</aop:config>
MY DAO
public class CurrencyDaoImpl extends BaseBusinessDao implements CurrencyDao {
/**
*
*/
public CurrencyDaoImpl() {
}
public Serializable save(CurrencyModel currency) {
Session session = getCurrentSession();
Serializable id = session.save(currency);
return id;
}
public void update(CurrencyModel currency) {
Session session = getCurrentSession();
session.update(currency);
}
public void delete(Serializable id) {
Session session = getCurrentSession();
session.delete(id);
}
}
My Model
public class CurrencyModel extends BaseModel {
/**
*
*/
private static final long serialVersionUID = 6543232156842168468L;
private String currencyId;
/**
* name of the currency.
*/
private String currency;
private String trId;
/**
*
*/
public CurrencyModel() {
}
public String getCurrencyId() {
return currencyId;
}
public void setCurrencyId(String currencyId) {
this.currencyId = currencyId;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public String getTrId() {
return trId;
}
public void setTrId(String trId) {
this.trId = trId;
}
#Override
public int hashCode() {
return currency.hashCode();
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!this.getClass().equals(obj.getClass())) {
return false;
}
String anotherCurrency = ((CurrencyModel) obj).getCurrency();
if (getCurrency().equals(anotherCurrency)) {
return true;
}
return false;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Currency for this instance is " + getCurrency());
return sb.toString();
}
}
My Hibernate Mappings file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mycompany.model.platform.support">
<class name="CurrencyModel" table="tblcurrency">
<id name="currencyId" column="currencyId">
<generator class="uuid"></generator>
</id>
<version name="version" column="version" type="long"></version>
<property name="rowStatus" column="rowStatus" not-null="true"></property>
<property name="currency" column="currency" not-null="true"></property>
<!-- this property needs to be replaces with transaction management root
object UserTransactionModel
-->
<property name="trId" not-null="true"></property>
</class>
</hibernate-mapping>
When i am running this application using programmatically by following code,
SimpleNamingContextBuilder scb = new SimpleNamingContextBuilder();
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://url:3306/db");
ds.setUsername("dtsnuser");
ds.setPassword("0okmnji9");
ds.setValidationQuery("select 1");
ds.setInitialSize(10);
ds.setMaxActive(20);
ds.setMaxIdle(10);
ds.setMaxWait(-1);
scb.bind("java:/comp::/env/jdbc/DS", ds);
scb.activate();
// setup bean factory
dlBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xbl = new XmlBeanDefinitionReader(dlBeanFactory);
xbl.loadBeanDefinitions(new FileSystemResource(
"src/main/webapp/WEB-INF/app-config/applicationContext.xml"));
currencyDao = (CurrencyDaoImpl) dlBeanFactory.getBean("currencyTarget");
currencyModel = new CurrencyModel();
currencyModel.setCurrency("INR");
id = UUID.randomUUID().toString();
currencyModel.setCurrencyId(id);
String trId = UUID.randomUUID().toString();
currencyModel.setTrId(trId);
it throws following exception
org.hibernate.HibernateException: Could not obtain
transaction-synchronized Session for current thread at
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:990)
at
com.mycompany.dao.base.BaseBusinessDao.getCurrentSession(BaseBusinessDao.java:41)
at
com.mycompany.dao.platform.support.CurrencyDaoImpl.delete(CurrencyDaoImpl.java:45)
at
com.mycompany.dao.platform.support.TestCurrencyDaoImpl.testDelete(TestCurrencyDaoImpl.java:130)
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
junit.framework.TestCase.runTest(TestCase.java:154) at
junit.framework.TestCase.runBare(TestCase.java:127) at
junit.framework.TestResult$1.protect(TestResult.java:106) at
junit.framework.TestResult.runProtected(TestResult.java:124) at
junit.framework.TestResult.run(TestResult.java:109) at
junit.framework.TestCase.run(TestCase.java:118) at
junit.framework.TestSuite.runTest(TestSuite.java:208) at
junit.framework.TestSuite.run(TestSuite.java:203) at
org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:131)
at
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
My DataSource is located at remote server.
What i am doing wrong ?
Is advice is not applied properly ? Can i figure out that advice is applied properly ?
Please , don't advice me to use annotation based approach, i can't go with it for the moment.
Thanks
Mihir
In web apps this is the way to handle the transaction with aop,
import javax.persistence.EntityManager;
public final class JPAUtil {
private static final ThreadLocal<EntityManager> currentEntityManager= new ThreadLocal<EntityManager>();
private EntityManagerFactory entityManagerFactory;
/**
* Get the EntityManager for specified persistence unit for this thread.
*/
public EntityManager em(String persistenceName) {
EntityManager entityManager = null;
if(entityManagerFactory != null) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
bindForCurrentThread(entityManager);
}
return entityManager;
}
/**
* Get the default EntityManager for this thread.
*/
public static final EntityManager em() {
EntityManager em = currentEntityManager.get();
if (jpaPlugin == null || em == null) {
return em(END_USER);
}
return em;
}
/**
* Bind an EntityManager to the current thread.
*/
public static final void bindForCurrentThread(EntityManager em) {
currentEntityManager.set(em);
}
public static final void closeEM() {
Logger.debug("Closing entity manager...");
EntityManager em = currentEntityManager.get();
if (em != null && em.isOpen()) {
em.close();
}
Logger.debug("Entity manager closed successfully.");
bindForCurrentThread(null);
}
public static final void beginTransaction() {
em().getTransaction().begin();
}
public static final void commitTransaction() {
em().getTransaction().commit();
}
}
#Aspect
public class DBAspects {
private static final String READONLY_CONNECTION = "org.hibernate.readOnly";
/**
* Injecting entity manager before calling the em() method of JPABaseDAO.
* #param joinPoint
* #param bd
*/
#Before("call(* com.xo.web.models.dao.JPABaseDAO+.em(..)) && this(bd)")
public void injectEntityManager(JoinPoint joinPoint, JPABaseDAO bd) {
bd.setEntityManager(JPAUtil.em());
//Logger.info("Injected enitymanager to : " + joinPoint.getSignature().getName());
}
/**
* Pointcuts to get the XODBTransaction methods
*/
#Pointcut("execution(#com.xo.web.persistence.XODBTransaction * *(..)) || call(public play.mvc.Result com.xo.web.controllers.*.*(..))")
public void getTransactionMethods(){
}
/**
* Pointcuts to get the XODBTransaction methods
*/
#Pointcut("execution(#com.xo.web.persistence.XODBReadOnly * *(..))")
public void getReadOnlyTransactionMethods(){
}
/**
* Processing the transactions based on the XODBTransaction annotation.
* #param joinPoint
* #return
* #throws Throwable
*/
#Around("getTransactionMethods()")
public Object handleTransaction(ProceedingJoinPoint joinPoint) {
Object resultObject = null;
EntityManager entityManager = JPAUtil.em();
try{
if(entityManager != null) {
javax.persistence.EntityTransaction transaction = entityManager.getTransaction();
try{
final String callerName = joinPoint.getSignature().getName();
if(transaction != null && !transaction.isActive()) {
transaction.begin();
Logger.info("Transaction started for : " + callerName);
}
resultObject = joinPoint.proceed();
if(transaction != null && transaction.isActive()) {
transaction.commit();
Logger.info("Transaction ended for : " + callerName);
}
}catch(Throwable th) {
if(transaction != null && transaction.isActive()) {
transaction.rollback();
}
Logger.info("Error while performing CUD operation...", th);
}
}
} catch(Throwable th) {
Logger.info("Error occurred while processing the request.", th);
} finally {
JPAUtil.closeEM();
}
Signature sig = joinPoint.getSignature();
if (sig instanceof MethodSignature) {
Method method = ((MethodSignature) sig).getMethod();
if(method.getReturnType() == Result.class) {
Context.current().session().clear();
}
}
return resultObject;
}
}
Hopes this gives an idea.
Related
Help! I'm using Spring+Hibernate and I'm trying to use AOP like this :
package ua.i.pustovalov.table;
enter code here
#Aspect
public class Aoprad {
#Pointcut("execution(* *getAll(..))")
public void performance() {
}
public Aoprad() {
}
#Around("performance()")
public void AfterAndPriv(ProceedingJoinPoint joinPoint) {
try {
System.out.println("Open query");
joinPoint.proceed();
System.out.println("Close query");
} catch (Throwable e) {
e.printStackTrace();
}
}
}
When I try to use my method, it returns null.
public class TestDao {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "applicationContext.xml" }, true);
DaoInterface das = (DaoInterface) context.getBean("dataDao");
System.out.println(das.getAll());
}
<context:annotation-config />
<aop:aspectj-autoproxy />
<context:component-scan base-package="ua.i.pustovalov.*">
</context:component-scan>
<bean id="audience" class="ua.i.pustovalov.table.Aoprad" />
<aop:config>
<aop:pointcut id="myPointcut"
expression="execution(* ua.i.pustovalov.maven.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- <tx:method name="get*" propagation="REQUIRED" read-only="true" /> -->
<tx:method name="getAll*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/homebase</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/hibernate.cfg.xml" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<!-- <prop key="hibernate.hbm2ddl.auto">create</prop> -->
</props>
</property>
</bean>
<bean id="dataDao" class="ua.i.putsovalov.dao.DaoStudent">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
My DAO class
package ua.i.putsovalov.dao;
public class DaoStudent extends HibernateDaoSupport implements DaoInterface {
public DaoStudent() {
}
#Override
protected HibernateTemplate createHibernateTemplate(
SessionFactory sessionFactory) {
HibernateTemplate result = super
.createHibernateTemplate(sessionFactory);
result.setAllowCreate(true);
return result;
}
#Override
public Student get(int id) {
return (Student) getSession().get(Student.class, id);
}
#Override
public Student get(String text) {
return (Student) getSession().get(Student.class, text);
}
#SuppressWarnings("unchecked")
#Override
public Collection<Student> getAll() {
return getSession().createCriteria(Student.class).list();
}
}
And interface
public interface DaoInterface {
Student get(int id);
Student get(String text);
Collection<Student> getAll();
}
What steps can I take to debug this issue?
When using an around aspect you should always use a method signature that returns Object and not void and you should always return the result of the call to the proceed() method. If you fail to do so every method that is advices by this aspect will return null because there is nothing returned.
#Around("performance()")
public Object AfterAndPriv(ProceedingJoinPoint joinPoint) {
try {
System.out.println("Open query");
return joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println("Close query");
}
}
I am running an Apache CXF web service under spring. I use JPA to persist the information. The service has a method that updates a series of rows. Before persisting each row, I check that the values to be persisted really exist in the databes. If there is a value that does not exists, then an Exception is thrown. The problem is I need to rollback al the values updated. I though that using #Transactional in my web service method would do the trick, but instead of that, the values that got persisted are really modified in the database, wich is not the desired behavior.
This is the code of the web service method
#Transactional( propagation = Propagation.REQUIRED )
public UpdateDescriptionResponse updateDescription(UpdateDescriptionRequest updateDescriptionRequest) throws SIASFaultMessage {
try {
SubstanceEntity substance = service.findSubstanceBySubstanceID(updateDescriptionRequest.getUpdateDescriptionRequestData().getIdentity().getSubstanceID());
if (substance!=null){
for(DescriptionKeyValueType keyValue: updateDescriptionRequest.getUpdateDescriptionRequestData().getSubstanceDescriptionData() ){
boolean descriptionExists = false;
for(DescriptionEntity desc: substance.getDescriptionsById()){
if (desc.getDescKey().equals(keyValue.getKey())) {
descriptionExists = true;
break;
}
}
if (!descriptionExists){
SIASFaultDetail faultDetail = new SIASFaultDetail();
faultDetail.setSIASFaultDescription("Description key does not match given substance ID");
faultDetail.setSIASFaultMessage(SIASFaultCode.INVALID_INPUT.toString());
faultDetail.setSIASFaultType(SIASFaultCode.INVALID_INPUT);
SIASFaultMessage fault = new SIASFaultMessage("Description key does not match given substance ID", faultDetail);
throw fault;
}
else
descriptionLogic.updateDescription(substance.getSubstanceId(), keyValue.getKey(),keyValue.getValue());
}
UpdateDescriptionResponse response = new UpdateDescriptionResponse();
UpdateDescriptionResponse.UpdateDescriptionResponsePackage responsePackage = new UpdateDescriptionResponse.UpdateDescriptionResponsePackage();
ResponseStatus status = new ResponseStatus();
status.setMessage(messageOk);
status.setReturn(BigInteger.valueOf(0));
responsePackage.setResponseStatus(status);
response.setUpdateDescriptionResponsePackage(responsePackage);
return response;
}
else
{
SIASFaultDetail faultDetail = new SIASFaultDetail();
faultDetail.setSIASFaultDescription("Substance ID does not exists");
faultDetail.setSIASFaultMessage(SIASFaultCode.INVALID_SUBSTANCE_ID.toString());
faultDetail.setSIASFaultType(SIASFaultCode.INVALID_SUBSTANCE_ID);
SIASFaultMessage fault = new SIASFaultMessage("Substance ID does not exists", faultDetail);
throw fault;
}
} catch (SIASFaultMessage ex) {
throw ex;
} catch (Exception ex) {
SIASFaultDetail a = new SIASFaultDetail();
a.setSIASFaultDescription("Unknown error processing enroll request");
a.setSIASFaultMessage("SERVICE_ERROR");
a.setSIASFaultType(SIASFaultCode.UNKNOWN_ERROR);
SIASFaultMessage fault = new SIASFaultMessage("Something happened", a);
throw fault;
}
}
This is the code for the instance of descriptionLogic.updateDescription(...)
#Override
public void updateDescription(String substanceID, String key, String value) {
PageRequest page = new PageRequest(1, 1);
Map<String, Object> filters = new HashMap<String, Object>();
filters.put("SUBSTANCE_ID", substanceID);
List<SubstanceEntity> substances = substanceService.findAll(page, filters);
if (substances.size() == 0) {
return;
}
SubstanceEntity substanceEntity = substances.get(0);
for (DescriptionEntity desc : substanceEntity.getDescriptionsById()) {
if (desc.getDescKey().equals(key)) {
desc.setDescValue(value);
descriptionService.persist(desc);
}
}
}
This is the test that fails
#Test()
public void testUpdateDescription_does_not_modify_description_with_invalid_values() throws Exception {
UpdateDescriptionRequest request = new UpdateDescriptionRequest();
UpdateDescriptionRequest.UpdateDescriptionRequestData data = new UpdateDescriptionRequest.UpdateDescriptionRequestData();
SIASIdentity identity = new SIASIdentity();
identity.setSubstanceID("804ab00f-d5e9-40ff-a4d3-11c51c2e7479");
data.getSubstanceDescriptionData().add(new DescriptionKeyValueType() {{
setKey("KEY3_1");
setValue("NEW_VALUE_1");
}});
data.getSubstanceDescriptionData().add(new DescriptionKeyValueType() {{
setKey("KEY3_5");
setValue("NEW_VALUE_2");
}});
data.setIdentity(identity);
request.setUpdateDescriptionRequestData(data);
try {
siasService.updateDescription(request);
}
catch (SIASFaultMessage ex){
}
DescriptionEntity descriptionEntity1 = descriptionService.findById(1);
DescriptionEntity descriptionEntity2 = descriptionService.findById(2);
assertThat("The value does not math",descriptionEntity1.getDescValue(), not(equalTo("NEW_VALUE_1")));
assertThat("The value does not math",descriptionEntity2.getDescValue(), not(equalTo("NEW_VALUE_2")));
Assert.assertEquals("The description does not math","KEY3_1", descriptionEntity1.getDescKey());
Assert.assertEquals("The description does not math","KEY3_2", descriptionEntity2.getDescKey());
}
It fails in this line:
assertThat("The value does not math",descriptionEntity1.getDescValue(), not(equalTo("NEW_VALUE_1")));
This is my datasource configuration in my spring context configuration file
.
.
.
<bean id="myDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<jdbc:initialize-database data-source="myDataSource">
<jdbc:script location="classpath:test-data.sql" />
</jdbc:initialize-database>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="packagesToScan" value="cu.jpa"/>
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
<property name="jpaDialect">
<bean class="cu.jpa.specifications.IsolationSupportHibernateJpaDialect" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">${hdm2ddl.auto}</prop>
</props>
</property>
<property value="/META-INF/persistence.xml" name="persistenceXmlLocation"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven/>
.
.
.
This is my persistence.xml file content:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="NewPersistenceUnit">
<class>cu.jpa.entities.PatternEntity</class>
.
.
.
<class>cu.jpa.entities.TraceRegEntity</class>
</persistence-unit>
</persistence>
Extract of the test class:
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/repositories.xml"})
public class ServiceImplUpdateDescriptionTest {
.
.
.
#Test()
public void testUpdateDescription_does_not_modify_description_with_invalid_values() throws Exception{
.
.
.
}
}
Spring will only rollback the transaction if it is an unchecked exception, if the exception is a checked exception then you will have to add that to your #Transactional annotation.
#Transactional(rollbackFor = SIASFaultMessage.class)
I am using spring 3.2 with hibernate 4. I want to use spring to control the transactions.
However with the configuration mentioned below I get the
'Servlet.service() for servlet spring threw exception: org.hibernate.HibernateException: No Session found for current thread'
exception:
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* com.locator.service.impl.ServiceTypeService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="hbTransactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- Hibernate session factory -->
<bean id="hbSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>../spring/model/ServiceType.hbm.xml</value>
</list>
</property>
</bean>
<bean id="hbTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="hbSessionFactory" />
</bean>
<bean id="serviceTypeService" class="com.locator.service.impl.ServiceTypeService">
<property name="serviceTypeDao" ref="serviceTypeDao"></property>
</bean>
<bean id="serviceTypeDao" class="com.locator.dao.impl.ServiceTypeDao">
<property name="sessionFactory" ref="hbSessionFactory"></property>
</bean>
The code for the Dao layer and the Service is as follows:
public class ServiceTypeDao implements IServiceTypeDao{
private static final Log log = LogFactory.getLog(ServiceTypeDao.class);
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory(){
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public ServiceType findById(int id) {
log.debug("getting ServiceType instance with id: " + id);
try {
Session session = getSessionFactory().getCurrentSession();
ServiceType instance = (ServiceType) session.get("com.locator.model.ServiceType", id);
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
instance.setName(instance.getName()+"0");
session.saveOrUpdate(instance);
return instance;
}catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
}
public class ServiceTypeService implements IServiceTypeService{
private ServiceTypeDao serviceTypeDao;
public void setServiceTypeDao(ServiceTypeDao serviceTypeDao){
this.serviceTypeDao = serviceTypeDao;
}
public ServiceType getServiceTypeById(int id){
return serviceTypeDao.findById(id);
}
}
Replacing getSessionFactory().getCurrentSession() with getSessionFactory().openSession() will resolve the above issue however, it will mean that the developer will then be responsible for the session open/close rather than spring. Therefore, please advise how this can be resolved using spring.
I was able to resolve the issue. It was occurring due to the following problems:
The Service class had not been Auto wired into the controller i.e. the #Autowired annotation was missing.
The configuration for the web.xml had to be modified with the listener class 'org.springframework.web.context.ContextLoaderListener' and the context-param was added.
I'm newbie to spring framework.I have integrated spring + hibernate to create RESTFUI API web service.
public interface IGenericDao {
long persists(T clazz) throws ResourceFailureException;
List select(T clazz)throws ResourceFailureException;
}
#Repository(value="genericDao")
public class GenericDao implements IGenericDao {
#Autowired
SessionFactory sessionFactory;
#Override
public long persists(T clazz) throws ResourceFailureException {
long generatedID;
generatedID = (Long) getCurrentSession().save(clazz);
getCurrentSession().getTransaction().commit();
return generatedID;
}
#Override
public List select(T clazz) throws ResourceFailureException {
String queryStr = " FROM "+clazz.getClass().getName()+" as table WHERE (table.isDelete is null OR"
+ " table.isDelete = false) ";
return this.select(queryStr);
}
protected final Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
//Implement GenericDAO class
#Repository(value="roleCapabilityDao")
public class RoleCapabilityDAO extends GenericDao{
public final void persistRole(final Role role) throws ResourceFailureException {
persists(role);
}
public final List getRoles(final String whereString)
throws ResourceFailureException {
String queryStr = "FROM Role " + whereString;
return select(queryStr);
}
public final Role getRoleById(final int roleId) throws ResourceFailureException {
String whereString = "WHERE id=" + roleId;
List roles = getRoles(whereString);
if (roles != null && !roles.isEmpty()) {
return roles.get(0);
}
return null;
}
}
//Servlet-context class.
#Transactional
public class HibernateUtility {
private static Logger logger =Logger.getLogger(HibernateUtility.class.getName());
public HibernateUtility() {
}
#Autowired(required=true)
RoleCapabilityDAO roleCapabilityDao;
#Autowired(required=true)
UserDAO userDao;
public void createDefaultUser(final ServletContext context) {
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, context);
Users user = new Users();
Role role = new Role();
.....
try {
role = roleCapabilityDao.getRoleByName("SystemAdmin");
}catch (ResourceFailureException re) {
logger.error("Resource not found" +re.getMessage());
}
}
applicationContext.xml
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:annotation-config />
<context:component-scan base-package="com.base" use-default-filters="false"/>
<bean id="propertyConfigurer"
.....
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}" p:password="${jdbc.password}">
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<servlet-name>HibernateUtility</servlet-name>
<servlet-class>com.base.hibernate.HibernateUtilityServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
//RESTFUL API calls
#Path("/roles")
#Service
#Transactional
public class RoleCapabilityResource {
public RoleCapabilityResource(){
super();
}
#Autowired
UserDAO userDao;
#Autowired
RoleCapabilityDAO roleCapabilityDao;
private static Logger roleLogger=Logger.getLogger(RoleCapabilityResource.class.getName());
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/getAllRoles")
#Loggable(value = LogLevel.DEBUG)
#CapabilityCode(value = "C_User_R")
public Response getAllRoles(#Context final HttpServletRequest request) {
HttpSession session = request.getSession();
try {
String loggedUser = session.getAttribute("userName").toString();
Users user = userDao.getCurrentUser(loggedUser.trim());
if(user == null){
return Response.status(Response.Status.BAD_REQUEST).type("text/plain").entity("Current User not found").build();
}
List<Role> roleList = roleCapabilityDao.getValidRolesForUserRole(user.getRole().getName(), false);
JSONObject allRolesJson = getRoleJSON(roleList);
return Response.status(Response.Status.OK).type(MediaType.APPLICATION_JSON).entity(allRolesJson).build();
} catch (ResourceFailureException re) {
roleLogger.error("Error in resource"+re.getMessage());
return Response.status(Response.Status.BAD_REQUEST).type("text/plain").entity(re.toString()).build();
}
}
}
//HibernateUtilizyServlet
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Hibernate Utility Servlet is invoked only once during JBoss Startup.
*/
#Service
#Transactional
public class HibernateUtilityServlet extends HttpServlet {
/**
* Default serial Version ID.
*/
private static final long serialVersionUID = 1L;
#Override
public void init(ServletConfig config) {
try {
super.init(config);
ServletContext context = getServletContext();
HibernateUtility hibernateUtil = new HibernateUtility();
hibernateUtil.createDefaultUser(context);
} catch (ServletException e) {
e.printStackTrace();
}
}
}
If i run the application it throws below error message
StandardWrapper.Throwable: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63) [:3.1.0.RELEASE]
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:685) [:3.6.0.Final]
at com.base.dao.GenericDao.getCurrentSession(GenericDao.java:186) [:]
at com.base.dao.GenericDao.select(GenericDao.java:80) [:]
at com.base.dao.RoleCapabilityDAO.getRoles(RoleCapabilityDAO.java:29) [:]
at com.base.dao.RoleCapabilityDAO.getRoleByName(RoleCapabilityDAO.java:40) [:]
at com.base.hibernate.HibernateUtility.createDefaultUser(HibernateUtility.java:180) [:]
at com.base.hibernate.HibernateUtilityServlet.init(HibernateUtilityServlet.java:41) [:]
I have tried some links as mentioned below,
1.Spring MVC + Hibernate: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.
2.I am receiving HibernateException "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here"
but I have not resolved yet.The only difference is here I didn't use #controller annotation.
How to resolve this Error?
How to set sessions handle by spring?
HibernateUtilityServlet is no Spring Bean! It is just some simple Http Servlet created by your Servlet container (not by Spring).
Therefore
your Spring annotations at HibernateUtilityServlet are ignored (they are only taken in account if your Object is an Spring Bean (created by Spring))
you can not inject something in HibernateUtilityServlet
Futuremore: when you create an instance with new (like you did HibernateUtility hibernateUtil = new HibernateUtility();), then this Object will be no Spring Bean. Therefore
(you already know it):
your Spring annotations at this class are ignored
you can not inject something in this object
I don't really know what you want do do with this HibernateUtilityServlet, but it looks like you try to setup the database when the application starts. -- A Much more easyer way would be using the spring default functionality for this:
#Component
public class OnStartupAction implements ApplicationListener<ContextStartedEvent> {
#Override
public void onApplicationEvent(final ContextStartedEvent event) {
// do whatever you need here
}
}
More details in this answer of mine.
All - In the Spring 3.0, in the applicationContext.xml .... are we supposed to have the bean property name and the reference value to be the same ? If I give a different value, it returns null object. But on giving the same value, it works. For my project, i am supposed to give different values for them. Kindly help. bye, HS
This works: (same values)
<bean id="MNCWIRAdminBaseAction" class="com.megasoft.wiradmin.web.action.WIRAdminBaseAction">
<property name="cacheDelegate">
<ref bean="cacheDelegate" />
</property>
</bean>
This doesn't work: (different values)
<bean id="MNCWIRAdminBaseAction" class="com.megasoft.wiradmin.web.action.WIRAdminBaseAction">
<property name="cacheDelegate">
<ref bean="MNCCacheDelegate" />
</property>
</bean>
bye, HS
My Full Code here:
WIRAdminBaseAction.java ---> my base action
AuthenticateAction.java ---> my java file that calls the bean here
applicationContext.xml --> system's applicationcontext file
applicationContext_MNC.xml ---> my applicationContext for a specific company ... this is getting loaded by my java file, which gets invoked by the web.xml file.
CacheDelegate.java
StatusDBDAO.java
PreXMLWebApplicationContext.java ----> loads my applicationContext file for the specific company.
****** 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean name="exceptionHandler" class="com.megasoft.wir.eStatement.web.interceptor.WIRExceptionHandlerInterceptor"/>
<bean name="security" class="com.megasoft.wir.eStatement.web.interceptor.SecurityInterceptor"/>
<bean name="permission" class="com.megasoft.wir.eStatement.web.interceptor.PermissionInterceptor"/>
<!-- AutoProxies -->
<bean name="loggingAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>base</value>
<value>exceptionHandler</value>
<value>security</value>
<value>permission</value>
</list>
</property>
</bean>
</beans>
****** applicationContext_MNC.xml ******
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="MNCWIRAdminBaseAction" class="com.megasoft.wiradmin.web.action.WIRAdminBaseAction">
<property name="cacheDelegate">
<ref bean="MNCCacheDelegate" />
</property>
</bean>
<bean id="MNCCacheDelegate" class="com.megasoft.wiradmin.delegate.CacheDelegate" >
<property name="statusDBDAO"><ref bean="MNCStatusDBDAO" /></property>
</bean>
<bean id="MNCStatusDBDAO" class="com.megasoft.wiradmin.dao.StatusDBDAO">
<property name="dataSource">
<ref bean="MNCAdminDataSource" />
</property>
</bean>
<!-- database configuration from property file -->
<bean id="MNCAdminDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" lazy-init="default" autowire="default" dependency-check="default">
<property name="driverClass" value="${jdbc.driver}" ></property>
<property name="jdbcUrl" value="${admin.jdbc.url}" ></property>
<property name="user" value="${admin.jdbc.user}" ></property>
<property name="password" value="${admin.jdbc.password}" ></property>
<property name="initialPoolSize" value="3" ></property>
<property name="minPoolSize" value="3" ></property>
<property name="maxPoolSize" value="25" ></property>
<property name="acquireIncrement" value="1" ></property>
<property name="acquireRetryDelay" value="1000" ></property>
<property name="debugUnreturnedConnectionStackTraces" value="true" ></property>
<property name="maxIdleTime" value="300" ></property>
<property name="unreturnedConnectionTimeout" value="300000" ></property>
<property name="preferredTestQuery" value="SELECT COUNT(*) FROM LOCALE_CODE" ></property>
<property name="checkoutTimeout" value="300000" ></property>
<property name="idleConnectionTestPeriod" value="600000" ></property>
</bean>
<!-- this bean is set to map the constants which needs to be configured as per
the environment to the java constants file -->
<bean id="envConstantsConfigbean" class="com.megasoft.wiradmin.util.constants.Environm entConstantsSetter">
<property name="loginUrl" value="${login.url}"/>
<property name="logoutIR" value="${logout.from.image.retrieval}"/>
<property name="adminModuleUrl" value="${admin.url}"/>
<property name="adminUrlSym" value="${admin.url.sym}"/>
<property name="envProperty" value="${env.property}"/>
</bean>
</beans>
****** AuthenticateAction.java ******
package com.megasoft.wiradmin.web.action;
import java.net.UnknownHostException;
import java.sql.SQLException;
import org.bouncycastle.crypto.CryptoException;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.web.context.support.WebApplica tionContextUtils;
import com.megasoft.wiradmin.delegate.ICacheDelegate;
public class AuthenticateAction extends WIRAdminBaseAction {
private static final long serialVersionUID = 1L;
public String authenticate() throws UnknownHostException, CryptoException,
DataAccessException, SQLException{
/** This way of calling works...... This is not encouraged, as we should not use applicationContext always **/
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContex t(getServletRequest().getSession().getServletConte xt());
ICacheDelegate cacheAction = (ICacheDelegate) applicationContext.getBean("MNCCacheDelegate");
/** The below way of calling does NOT work .... returns null value.... Please help...
* I assume that, since I have extended the WIRAdminBaseAction, i should be able to call the getCacheDelegate directly
* and it should return my cacheDelegate object ...
* Again, Please note.....if I change my applicationContext_MNC.xml as below, the below way of calling works fine...
* but, i don't want to change my applicationContext_MNC.xml as below, due to some necessity.
*
<bean id="MNCWIRAdminBaseAction" class="com.megasoft.wiradmin.web.action.WIRAdminBaseAction">
<property name="cacheDelegate">
<ref bean="cacheDelegate" />
</property>
</bean>
*
<bean id="cacheDelegate" class="com.megasoft.wiradmin.delegate.CacheDelegate" >
<property name="statusDBDAO"><ref bean="MNCStatusDBDAO" /></property>
</bean>
*
... is it that the name and bean should have the same value.... ??? No Need to be.....Am i right ? Please advise.
*
* **/
getCacheDelegate().getActorAction(1); // this way of calling doesn't work and returns null value. please help.
return "success";
}
}
****** WIRAdminBaseAction.java ******
package com.megasoft.wiradmin.web.action;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Preparable;
import com.opensymphony.xwork2.config.entities.Parameteri zable;
import com.megasoft.wiradmin.delegate.ICacheDelegate;
public class WIRAdminBaseAction extends ActionSupport implements Preparable, ParameterAware, Parameterizable, SessionAware,RequestAware {
private HttpServletRequest request;
private static final long serialVersionUID = 1L;
private HttpServletResponse response;
private ICacheDelegate cacheDelegate;
private Map session;
private Map<String, String> params;
private Map parameters;
public void prepare() throws Exception {
}
public String execute() throws Exception {
return SUCCESS;
}
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public HttpServletRequest getServletRequest() {
return this.request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public HttpServletResponse getServletResponse() {
return this.response;
}
public ICacheDelegate getCacheDelegate() {
return cacheDelegate;
}
public void setCacheDelegate(ICacheDelegate cacheDelegate) {
this.cacheDelegate = cacheDelegate;
}
public void addParam(final String key, final String value) {
this.params.put(key, value);
}
public Map getParams() {
return params;
}
public void setParams(final Map<String, String> params) {
this.params = params;
}
public Map getSession() {
return this.session;
}
public void setSession(final Map session) {
this.session = session;
}
public void setParameters(final Map param) {
this.parameters = param;
}
}
PreXMLWebApplicationContext.java **
package com.megasoft.wiradmin.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.support.XmlWebApplicationContext;
public class PreXMLWebApplicationContext extends XmlWebApplicationContext {
/**
* This initializes the Logger.
*/
private static Log logger = LogFactory.getLog(PreXMLWebApplicationContext.class);
protected String[] getDefaultConfigLocations() {
String environment = System.getProperty("envProperty");
String webirConfig = System.getProperty("webirConfig");
String fi = System.getProperty("FI");
String preHostConfiguration =null;
logger.info("The environment is "+environment);
logger.info("The webirConfig is "+webirConfig);
logger.info("The fi is "+fi);
if(environment != null && webirConfig != null && fi != null) {
preHostConfiguration = DEFAULT_CONFIG_LOCATION_PREFIX +
"classes/applicationContext" + "_" + fi.toUpperCase() +
DEFAULT_CONFIG_LOCATION_SUFFIX;
}
return new String[]{DEFAULT_CONFIG_LOCATION, preHostConfiguration};
}
/**
* This is close API.
*
* #see org.springframework.context.support.AbstractApplicationContext
* #close()
*/
public void close() {
this.doClose();
logger.info("Login-->into the closed");
}
}
<property name="userDelegate" ref="userDelegate" />
name is the field name in your class. When name is userDelegate, it means that WIRAdminBaseAction has a field named userDelegate (and probably a setter setUserDelegate())
ref is the bean name you want to set this field to. In your example, you should have another bean, named userDelegate or bmoUserDelegate which should be set as userDelegate in WIRAdminBaseAction.
So if you want to use the second configuration:
You just need to create a bean with id bmoUserDelegate:
<bean id="bmoUserDelegate" class="mypackage.BmoUserDelegateClass"/>