0
votes

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

1
The fact if you use java, annotations or xml doesn't matter, they all are the same, they are for configuration. a couple of things, why on earth are you using a ProxyFactoryBean, spring automatically creates proxies for you. There are also 2 things wrong with you starting the application, first you are using a BeanFactory instead of an ApplicationContext and second you are explicitly asking for the non proxied bean (that will never have transactions applied).M. Deinum
@M.Deinum I understand there is nothing to do with XML or Annotation based configuration. i can use ApplicationContext but i did not get what do you mean non proxied bean here ? if you see my aop:config , i have already applied there transaction advice or is there anything wrong there ?I use ProxyFactoryBean here to get in depth understanding of how spring doing AOP internally , thanksMihir
Your configuration is useless. As you are getting the currentTarget which is unproved. Also you are using 2 proxy mechanisms never do that, next to that it doesn't add anything but complexity.M. Deinum

1 Answers

-1
votes

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.