由於對SSH還停留在比較初級的應用水平上,今天在遇到一個疑惑時折騰了很久,具體問題是這樣的,
有這么一個測試方法,
1 public static void test1() { 2 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml","daoContext.xml"); 3 MgrManager mgr = (MgrManager)ctx.getBean("mgrManager"); 4 List<EmpBean> emps = mgr.getEmpsByMgr("weblogic"); 5 for (EmpBean empBean : emps) { 6 System.out.println(empBean.getEmpName()); 7 } 8 }
其中的MgrManager是一個業務類, 提供一個根據名字查詢員工的功能, 以上方法執行完全沒有問題,
然后又有下面這個測試方法,
public static void test2() { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml","daoContext.xml"); EmployeeDao empDao = (EmployeeDao)ctx.getBean("employeeDao"); Employee emp = empDao.findByName("oracle"); System.out.println(emp.getSalary()); }
test1是使用業務類間接得查詢數據庫,得到結果,而test2是通過dao直接查詢數據庫, 但是test2始終報錯說沒有數據庫session。
百思不得其解,按理說所有的bean是通過spring管理的,既然spring為業務類注入了sessionFactory, 為何dao類就沒有呢?
然后通過手工在test2中寫了如下代碼,發現是可以通過測試的,
public static void test3() { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml","daoContext.xml"); SessionFactory sf = (SessionFactory)ctx.getBean("sessionFactory"); Session session = sf.openSession(); List<Employee> emps = session.createQuery("select e from Employee e where e.name = 'oracle'").list(); }
也就是說,問題不在於Spring容器中沒有sessionFactory, 而是sessionFactory沒有被open!那為什么test1的例子中的sessionFaction又open了呢?
看了配置文件半天,發現有這么關鍵的一行配置,
<aop:pointcut id="leePointcut" expression="bean(empManager)||bean(mgrManager))" />
另外,通過面向切面編程原理,Spring通過AOP機制為dao對象的數據操作提供事務管理,有如下配置,
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 配置詳細的事務語義 --> <tx:attributes> <!-- 所有以get開頭的方法是只讀的 --> <tx:method name="get*" read-only="true" /> <!-- 其他方法使用默認設置 --> <tx:method name="*" /> </tx:attributes> </tx:advice> <aop:config> <!-- 配置一個切入點,用來匹配empManager和mgrManager兩個Bean的所有方法的執行 --> <aop:pointcut id="leePointcut" expression="bean(empManager)||bean(mgrManager)||bean(employeeDao)" /> <!-- 指定在leePointcut切入點應用txAdvice事務增強 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="leePointcut" /> </aop:config>
而下面這段配置是用來配置真正的事務管理類, 正是通過上面的切面配置, 將業務類與增強處理關聯起來, 同時通過下面的事務管理類進行事務管理
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" p:sessionFactory-ref="sessionFactory" />
那么重點就在上面的transactionManager這個bean了, 通過查詢資料,初步了解到, HibernateTransactionManager這個類提供 sessionFactory的管理,為了實現數據同步,在HibernateTransactionManager內部會進行Hibernate session的open和close,並將打開的Hibernaate sesion關聯到當前的Application session,在Application中則通過getCurrentSession方式獲取爭取的打開的Hibernate session, 從而解決某些方面的線程安全及同步問題。
由此可見,由於上面配置事務管理的切面類僅僅只是針對了業務類,即默認情況下只有業務類才用於打開的session, 由此可以理解上面的test2為什么報“沒有打開的session”錯誤了。
針對上面的配置, 我做了如下修改,
<bean id="TestManager" class="service.impl.TestManagerImpl" p:empDao-ref="employeeDao"/>
果然修改之后test2就能正常執行了。
以上僅僅涉及非常非常小的問題, 主要還是對HibernateTransactionManager的理解不夠深入造成的。