Spring第13篇—–Spring整合Hibernate之声明式事务管理
不容置疑的我们可以知道Spring的事务管理是通过AOP(AOP把我们的事务管理织入到我们的业务逻辑里面了)的方式来实现的,因为事务方面的代码与spring的绑定并以一种样板式结构使用。(面向切面编程)其中的事务通知由元数据(目前基于xml和注解)驱动。代理对象由元数据结合产生一个新的代理对象。他使用一个PlatformTransactionManager实现配合TransactionInterceptor在方法调用之前实施事务。
事务加在DAO层还是Service层?
一般都是放在service层上,因为这层会调用dao层,而service层中某个类里的方法调用的不仅仅只是dao里面的一个方法,有可能是多个方法 一起调用,如果调用的这些方法中有一个不成功或抛出异常,就必须全部返回到以前的数据(回滚),所以就必须将这个service层中某个类里的方法进行管理,这就是用到了spring里的事务管理,所以都是事务管理一般都是用在service层中。
采用注解方式配置事务:
配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd " >
<context:component-scan base-package="net.zmcheng"/>
<bean id=" logInterceptor" class="net.zmcheng.aop.LogInterceptor"/>
<aop:config>
<aop:pointcut expression="execution(public * net.zmcheng.serviceImpl.UserServiceImpl.add())" id="servicePointCut"/>
<aop:aspect id="logAspect" ref=" logInterceptor">
<aop:before method="before" pointcut-ref="servicePointCut"/>
<aop:around method="aroundMethod" pointcut-ref="servicePointCut"/>
</aop:aspect>
</aop:config>
<!-- 设置数据源:提供了一个标准化的取得数据库连接的方式,一看到property我们就应该有一个意识,那就是这个类当中有这个属性的setter方法来进行注入 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/BSWS?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- 整合Hibernate -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 使用注解的方式
<property name="annotatedClasses">
<list><value>net.zmcheng.model.NlUser</value></list>
</property>
-->
<property name="mappingResources">
<list>
<value>net/zmcheng/model/NlUser.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!-- 声明式事务 -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 采用@Transactional注解方式使用事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
|
PS:经测试需要加入spring-jdbc-4.0.0.RELEASE-javadoc.jar。
服务层:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package net.zmcheng.serviceImpl;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import net.zmcheng.dao.UserDao;
import net.zmcheng.service.UserService;
@Component
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
@Resource
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
//注意引用的是Spring提供的事务
@Transactional
public void add() {
userDao.add();
}
}
|
DAO层:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package net.zmcheng.daoImpl;
import javax.annotation.Resource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;
import net.zmcheng.dao.UserDao;
import net.zmcheng.model.NlUser;
@Component
public class UserDaoImpl implements UserDao {
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Resource
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private SessionFactory sessionFactory;
public void add() {
/*不要再使用getSession,因为每次调用它都是产生一个新的Session
Session s = sessionFactory.openSession();*/
Session session = sessionFactory.getCurrentSession();
NlUser n = new NlUser();
n.setName("zmcheng");
session.save(n);
System.out.println("添加员工成功");
}
}
|
PS:任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚,这些默认的设置当然也是可以被改变的。
采用XML方式配置事务(大多数情况下):
我们想施加的事务语义封装在<tx:advice/>定义中。<tx:advice/> “把所有以 ‘get’ 开头的方法看做执行在只读事务上下文中, 其余的方法执行在默认语义的事务上下文中”。 其中的 ‘transaction-manager’ 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 ‘txManager’), 该bean将会真正管理事务。配置中最后一段是 <aop:config/> 的定义, 它确保由 ‘txAdvice’ bean定义的事务通知在应用中合适的点被执行。 首先我们定义了 一个切面,然后我们用一个通知器(advisor)把这个切面与 ‘txAdvice’ 绑定在一起, 表示当 ‘fooServiceOperation’ 执行时,’txAdvice’ 定义的通知逻辑将被执行。
配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd " >
<context:component-scan base-package="net.zmcheng"/>
<!-- 设置数据源:提供了一个标准化的取得数据库连接的方式,一看到property我们就应该有一个意识,那就是这个类当中有这个属性的setter方法来进行注入 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/BSWS?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- 整合Hibernate -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 使用注解的方式
<property name="annotatedClasses">
<list><value>net.zmcheng.model.NlUser</value></list>
</property>
-->
<property name="mappingResources">
<list>
<value>net/zmcheng/model/NlUser.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!-- 声明式事务 -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 采用@Transactional注解方式使用事务
<tx:annotation-driven transaction-manager="txManager"/>-->
<bean id=" logInterceptor" class="net.zmcheng.aop.LogInterceptor"/>
<aop:config>
<aop:pointcut expression="execution(public * net.zmcheng.serviceImpl..*.*(..))" id="servicePointCut"/>
<aop:advisor pointcut-ref="servicePointCut" advice-ref="txAdvice"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</beans>
|
DAO:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package net.zmcheng.daoImpl;
import javax.annotation.Resource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;
import net.zmcheng.dao.UserDao;
import net.zmcheng.model.NlUser;
@Component
public class UserDaoImpl implements UserDao {
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Resource
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private SessionFactory sessionFactory;
public void add() {
/*不要再使用getSession,因为每次调用它都是产生一个新的Session
Session s = sessionFactory.openSession();*/
Session session = sessionFactory.getCurrentSession();
NlUser n = new NlUser();
n.setName("zmchen");
session.save(n);
System.out.println("添加员工成功");
}
}
|
事务传播属性(propagation):默认为REQUIRED
REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。 MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。
SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSource -TransactionManager事务管理器起效。
自动扫描实体类的配置(需要使用注解):
1
2
3
4
5
6
|
<!-- 使用packagesToScan -->
<property name="packagesToScan">
<list>
<value>net.zmcheng.model</value>
</list>
</property>
|
PS:因为我看的视频是spring3.0里面还在讲HibernateTemplate(原理是利用了模板设计模),本来想想一篇博客的,一查文档发现文档里说hibernate4+spring集成推荐用sessionFactory.getCurrentSession(),在Spring与hibernate4的整合中,已经取消了HibernateTemplate和HibernateDaoSupport这两个方法只能通过session来进行处理。如果想使用只能自己实现。