zoukankan      html  css  js  c++  java
  • Spring aop与HibernateTemplate——session管理(每事务一次 Session)

    一、HibernateTemplate与Spring aop简介

    参见http://bbs.csdn.net/topics/340207475中网友blueram的发言。(感谢blueram

    二、在网友blueram有一句话是本文讲述的主题:

    “更优秀的 Session 管理机制。 Spring 提供"每事务一次 Session" 的机制,该机制能大大提高了系统性能,而且 Spring 对 Session 的管理是透明的,无须在代码中操作 Session。”——举例说明:

    HibernateTemplate有一个saveOrUpdate方法,该方法用于保存或更新数据,且执行一次即关闭session,这样无法在同一session中添加两条或以上的数据,但若在spring的配置文件中对hirbernate进行事务管理(aop),则可避免这一问题,下面进行代码说明。

    三、Spring aop与HibernateTemplate——session管理(每事务一次 Session)

    1.在测试类中测试“利用HibernateTemplate在同一session中添加两条数据”。测试结果:fail

    注:这里测试的表涉及三个:STUDENT,TEACHER,TEACHER_STUDENT。表STUDENT与表TEACHER呈多对多关联,TEACHER_STUDENT为中间表。

    代码:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:spring.xml")
    public class Many2ManyTest {
        @Resource
        private HibernateTemplate hibernateTemplate;
        @Resource
        private SessionFactory sessionFactory;
        @Resource
        private Many2ManyService many2ManyService;
        
        @Test//fail
        public void add0(){
            Set<Teacher> ts = new HashSet<Teacher>(); 
            
            Teacher t1 = new Teacher();  
            t1.setName("t1");  
            ts.add(t1);
            
            Teacher t2 = new Teacher();  
            t2.setName("t2");  
            ts.add(t2);
            
            Set<Student> ss = new HashSet<Student>(); 
            
            Student s1 = new Student();  
            s1.setName("s1");  
            ss.add(s1);
            
            Student s2 = new Student();  
            s2.setName("s2");  
            ss.add(s2);
            
            t1.setStudents(ss);
            t2.setStudents(ss);
            
            hibernateTemplate.saveOrUpdate(t1);
            hibernateTemplate.saveOrUpdate(t2);
            hibernateTemplate.saveOrUpdate(s1);
            hibernateTemplate.saveOrUpdate(s2);
        }
    
    }

    运行,出现异常,异常信息如下:

    org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: com.chen.vo.Student; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.chen.vo.Student
        at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:671)
        at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
        at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:411)
        at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
        at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:737)
        at com.chen.test.Many2ManyTest.add0(Many2ManyTest.java:56)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.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:83)
        at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
        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:71)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
        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: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)
    Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.chen.vo.Student
        at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:242)
        at org.hibernate.type.EntityType.getIdentifier(EntityType.java:430)
        at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:101)
        at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:777)
        at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1165)
        at org.hibernate.action.CollectionRecreateAction.execute(CollectionRecreateAction.java:58)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:171)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028)
        at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390)
        at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:407)
        ... 31 more
    分析:当前的事务需求是在同一session中添加两个老师,两个学生以及他们关联的中间数据,然后同步至数据库。而异常信息“save the transient instance before flushing”表明在同步数据库之前已经保存了数据(transient instance)--这个数据就是t1。在数据库中查询表TEACHER,结果如下:

    这一结果印证了上述异常——简单地说session添加t1就关闭了,无法再继续添加t2,s1,s2。

    当然,这里有替代方法,即利用hibernate的原生session采用编程式事务:

    代码:

    @Test//success
        public void add01(){
            Session s = null;  
            Transaction tx = null; 
            
            Set<Teacher> ts = new HashSet<Teacher>(); 
            
            Teacher t1 = new Teacher();  
            t1.setName("t1");  
            ts.add(t1);
            
            Teacher t2 = new Teacher();  
            t2.setName("t2");  
            ts.add(t2);
            
            Set<Student> ss = new HashSet<Student>(); 
            
            Student s1 = new Student();  
            s1.setName("s1");  
            ss.add(s1);
            
            Student s2 = new Student();  
            s2.setName("s2");  
            ss.add(s2);
            
            t1.setStudents(ss);
            t2.setStudents(ss);
            
            SessionFactory sessionFactory = hibernateTemplate.getSessionFactory();
            s = sessionFactory.openSession();
            tx = s.beginTransaction();
            s.saveOrUpdate(t1);
            s.saveOrUpdate(t2);
            s.saveOrUpdate(s1);
            s.saveOrUpdate(s2);
            tx.commit();
            s.close();
        }

    运行,Hibernate处理的SQL:

    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: insert into Teacher (name, id) values (?, ?)
    Hibernate: insert into Teacher (name, id) values (?, ?)
    Hibernate: insert into Student (name, id) values (?, ?)
    Hibernate: insert into Student (name, id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

    以上SQL表明Hibernate已同步至数据库8条数据,分别是:向表Teacher添加了t1,t1,向表Student添加了s1,s2,向表teacher_student添加了4条级联数据。

    在数据库中查询,结果如下:

    可以看出,已添加了8条数据。

    上述方法虽然解决了问题,但这种事务管理方式并非理想方式,采用Spring 的aop对Hibernate进行事务管理是一个很好的方式,下面进行介绍。

    2.采用Spring 的aop对Hibernate进行事务管理

    Spring的有关aop的配置信息如下:

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <aop:config>
        <aop:pointcut expression="execution(* com.chen.service.*.*(..))" id="managerOperation"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="managerOperation"/>
    </aop:config>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="del*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>

    从上述配置信息可以得知事务管理的类限定在com.chen.service这个包中,现在在这个包中新建一个类:Many2ManyService,添加一个saveOrUpdate方法,将测试类中add0方法中的代码copy其中。

    代码:

    package com.chen.service;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.annotation.Resource;
    
    import org.springframework.orm.hibernate3.HibernateTemplate;
    import org.springframework.stereotype.Service;
    
    import com.chen.vo.Student;
    import com.chen.vo.Teacher;
    
    @Service("many2ManyService")
    public class Many2ManyService {
        @Resource
        private HibernateTemplate hibernateTemplate;
        
        public void saveOrUpdate(){
            Set<Teacher> ts = new HashSet<Teacher>(); 
            
            Teacher t1 = new Teacher();  
            t1.setName("t1");  
            ts.add(t1);
            
            Teacher t2 = new Teacher();  
            t2.setName("t2");  
            ts.add(t2);
            
            Set<Student> ss = new HashSet<Student>(); 
            
            Student s1 = new Student();  
            s1.setName("s1");  
            ss.add(s1);
            
            Student s2 = new Student();  
            s2.setName("s2");  
            ss.add(s2);
            
            t1.setStudents(ss);
            t2.setStudents(ss);
            
            hibernateTemplate.saveOrUpdate(t1);
            hibernateTemplate.saveOrUpdate(t2);
            hibernateTemplate.saveOrUpdate(s1);
            hibernateTemplate.saveOrUpdate(s2);
        }
    
    }

    在测试类中测试此方法:

    代码:

    @Test
        public void add02(){
            many2ManyService.saveOrUpdate();
        }

    运行(注:运行此方法前已清空之前添加的8条数据),Hibernate处理的SQL:

    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: insert into Teacher (name, id) values (?, ?)
    Hibernate: insert into Teacher (name, id) values (?, ?)
    Hibernate: insert into Student (name, id) values (?, ?)
    Hibernate: insert into Student (name, id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
    Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

    以上SQL表明Hibernate已同步至数据库8条数据,分别是:向表Teacher添加了t1,t1,向表Student添加了s1,s2,向表teacher_student添加了4条级联数据。至此,可以小结:在saveOrUpdate方法中连续执行hibernateTemplate的4个saveOrUpdate方法均处于同一session中,等到添加最后一条数据,session才关闭,而不会像在测试类中的add0方法那样只执行一条saveOrUpdate方法后立即关闭。

    在数据库中查询,结果如下:

    可以看出,已添加了8条数据。

    小结:采用Spring aop对Hibernate进行事务管理后,无须在代码中操作 Session,不仅提高了系统性能,且可将事务管理逻辑与代码分离,使事务可在全局事务和局部事务之间切换。

  • 相关阅读:
    < java.util >-- Set接口
    Codeforces 627 A. XOR Equation (数学)
    Codeforces 161 B. Discounts (贪心)
    Codeforces 161 D. Distance in Tree (树dp)
    HDU 5534 Partial Tree (完全背包变形)
    HDU 5927 Auxiliary Set (dfs)
    Codeforces 27E. Number With The Given Amount Of Divisors (暴力)
    lght oj 1257
    Codeforces 219D. Choosing Capital for Treeland (树dp)
    Codeforces 479E. Riding in a Lift (dp + 前缀和优化)
  • 原文地址:https://www.cnblogs.com/wql025/p/4864056.html
Copyright © 2011-2022 走看看