在整合SpringMVC时,openSession和getCurrentSession事物控制区别,在测试时如果报could not initialize proxy - no Session

    xiaoxiao2025-09-17  293

    Hibernate session在service实现事务,getCurrentSession()和openSession()区别

        1 条评论

    目录 [hide]

    1 在DAO进行Session事务出现的问题 2 在service层写session的数据库事务 3 getCurrentSession()与openSession()区别 4 getCurrentSession()与openSession()关联 5 参考资料

    当我们在使用Hibernate作为数据库操作的类库时,我们一般在DAO层里与数据库相关的操作,把业务逻辑写在service层里。但是如果我们的项目比较小,那么直接在dao层里写事务也是可以的,这个就是看个人了,没有什么特别的规定。但是如果项目比较大,那么DAO应该只做单纯的数据库的操作,service写事务的操作,即整个业务逻辑。

    例如:业务逻辑要求向数据库中的用户表增加一个用户,同时向日志表中加入一条日志,而这需要调用DAO的两个方法(UserDao的saveUser和LogDao的saveLog)。这显然是一个事务,也就是如果一个操作出现了问题,就要回滚到初始的状态。那么如何在Service层控制事务呢,本文就以此例的代码说明。

    在DAO进行Session事务出现的问题

    我们先看看在DAO层里写Hibernate的session的事务。

    01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.xxg;   import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration;   public class HibernateUtil {        private static final SessionFactory sessionFactory = buildSessionFactory();      private static SessionFactory buildSessionFactory() {          try {              // Create the SessionFactory from hibernate.cfg.xml              return new Configuration().configure().buildSessionFactory();          }          catch (Throwable ex) {              // Make sure you log the exception, as it might be swallowed              System.err.println( "Initial SessionFactory creation failed." + ex);              throw new ExceptionInInitializerError(ex);          }      }      public static SessionFactory getSessionFactory() {          return sessionFactory;      } }

    创建用户表T_user(id,username)和日志表T_log(id,content),以及它们对应的实体类User、Log及映射文件,这里就不一一贴出代码。

    01 02 03 04 05 06 07 08 09 10 11 12 13 public class UserDao {        public void saveUser(User user){          SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory          Session session = sessionFactory.openSession(); // openSession          session.beginTransaction(); //开始事务            session.save(user);            session.getTransaction().commit(); //事务提交          session.close(); //关闭session      } } 01 02 03 04 05 06 07 08 09 10 11 12 13 14 public class LogDao {        public void saveLog(Log log){          SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory          Session session = sessionFactory.openSession(); // openSession          session.beginTransaction(); //开始事务            session.save(log);            session.getTransaction().commit(); //事务提交          session.close(); //关闭session      }   }

    接下来我们看看在service中写一个业务逻辑

    01 02 03 04 05 06 07 08 09 10 11 12 13 public class TestService {        public void save(User user){          UserDao userDao = new UserDao();          userDao.saveUser(user);            LogDao logDao = new LogDao();          Log log = new Log();          log.setContent( "插入一个用户" );          logDao.saveLog(log);      }   }

    可以看到,我们在两个DAO里写了数据库的事务,代码中高亮显示了,session.beginTransaction()显示声明事务的开始。

    这样写是不对的,因为这两个事情作为一个事务来进行的,会出现一个事务成功提交,而另外一个可能提交失败,导致不一致的情况,这样这两个操作不算是一个事务transaction,所以这么写就是一个失败的事务。

    因此,我们要将事务在service中进行声明。

    在service层写session的数据库事务

    为了将事务放在service中,我们需要更改HibernateUtil的代码才能实现。否则使用上面的那个不能达到我们的需求。在这个新的HibernateUtil代码中,利用了ThreadLocal的线程内的局部变量来保存hibernate的session对象。这样就可以在不同的class中使用同一个session对象,而不用传递参数。

    01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class HibernateUtil {      public static final ThreadLocal session = new ThreadLocal();        public static final SessionFactory sessionFactory;      static {          try {              sessionFactory = new Configuration().configure().buildSessionFactory();          } catch ( Throwable ex ) {              throw new ExceptionInInitializerError( ex );          }      }        public static Session currentSession() throws HibernateException      {          Session s = session.get();          if ( s == null )          {              s = sessionFactory.openSession();              session.set( s );          }          return (s);      }        public static void closeSession() throws HibernateException      {          Session s = session.get();          if ( s != null )          {              s.close();          }          session.set( null );      } }

    接下来,我们将事务放在service中。看代码:

    01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 public class TestService {        public void save(User user){            SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory          Session session = sessionFactory.getCurrentSession(); //getCurrentSession          session.beginTransaction(); //事务开始            UserDao userDao = new UserDao();          userDao.saveUser(user);            LogDao logDao = new LogDao();          Log log = new Log();          log.setContent( "插入一个用户" );          logDao.saveLog(log);          session.getTransaction().commit(); //事务提交      }   }

     

    01 02 03 04 05 06 07 08 09 10 11 public class LogDao {        public void saveLog(Log log) throws RuntimeException{          SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory          Session session = sessionFactory.getCurrentSession(); //getCurrentSession            session.save(log);            throw new RuntimeException();      } }

     

    01 02 03 04 05 06 07 08 09 10 11 public class UserDao {        public void saveUser(User user){          SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory          Session session = sessionFactory.getCurrentSession(); //getCurrentSession            session.save(user);        }   }

    通过getCurrentSession()可以获得当前线程的session对象,通过它来进行共享session。这样事务就从service开始,然后再service结束。

    getCurrentSession()与openSession()区别

    getCurrentSession创建的session会和绑定到当前线程,而openSession不会。

    getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭

    采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession() 创建的session则不会 * 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession() 创建的session必须手动关闭

    2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:

    如果使用的是本地事务(jdbc事务) <property name=”hibernate.current_session_context_class”>thread</property> 如果使用的是全局事务(jta事务) <property name=”hibernate.current_session_context_class”>jta</property>

    getCurrentSession()与openSession()关联

    在 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 执行时,如果当前 Session 为空, currentSession 会调用 SessionFactory 的 openSession 。所以 getCurrentSession() 对于 Java EE 来说是更好的获取 Session 的方法。

    参考资料

    http://blog.csdn.net/xiao__gui/article/details/7695698 http://blog.csdn.net/loveyout/article/details/4193894

    出现异常 org.hibernate.LazyInitializationException: could not initialize proxy - no Session  网上出现最多的解决方案是需要在web.xml文件里配置Spring的openSessionInViewFilter过滤器

    <filter> <filter-name>openSessionInViewerFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> <!-- <init-param> <param-name>singleSession</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>sessionFactoryBeanName</param-name> <param-value>sessionFactory</param-value> </init-param> --> </filter> <filter-mapping> <filter-name>openSessionInViewerFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>其次需要修改Spring的beans.xml的配置文件

    <!--创建Spring的SessionFactory工厂 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 设置Spring取那个包中查找相应的实体类 --> <property name="packagesToScan"> <value>com.han.model</value> </property> <property name="hibernateProperties"> <!-- <value> hibernate.dialect=org.hibernate.dialect.HSQLDialect </value> --> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.format_sql">false</prop> <prop key="hibernate.enable_lazy_load_no_trans">true</prop> </props> </property> </bean> <!-- 配置Spring的事务处理 --> <!-- 创建事务管理器--> <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>

    如果在junit测试报错org.hibernate.LazyInitializationException: could not initialize proxy - no Session,在该测试方法上加上@Transactional通过以下方式就可以解决

    package com.han.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; import com.han.dao.UserDao; import com.han.model.User; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:beans.xml") public class TestUser{ @Resource(name = "userDao") private UserDao userDao; @Test public void testAdd(){ this.userDao.add(new User(1, "admin")); } /** * 如果报错org.hibernate.LazyInitializationException: could not initialize proxy - no Session * 加上注解@Transactional就可以了 */ @Test @Transactional public void testload(){ User user = this.userDao.load(1); System.out.println(user.getUsername()); } }

    spring的@Transactional注解详细用法可参考:https://www.cnblogs.com/yepei/p/4716112.html

    转载请注明原文地址: https://ju.6miu.com/read-1302765.html
    最新回复(0)