Spring
1:对每一个服务层类Bean配置一个代理:
a.首先创建一个maven quick-start 项目,然后配置依赖包:
mysql-connector,spring-context,spring-orm,hibernate,
hibernate-core,dbcp2(数据源)
b.在test数据库中编写数据库脚本并执行,以及编写applicationContext.xml文件(在src目录下):
DROP TABLE IF EXISTS account;
CREATE TABLE account (
accountId int primary key auto_increment,
accountname varchar(20),
money int not null
);
INSERT INTO ACCOUNT(ACCOUNTNAME,MONEY) VALUES('zhangsan',100);
INSERT INTO ACCOUNT(ACCOUNTNAME,MONEY) VALUES('lisi',100);
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT"></property>
<property name="username" value="root"></property>
<property name="password" value="mysqladmin"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>kai/SpringTransaction1/Account.hbm.xml</value>
</list>
</property>
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO和service -->
<bean id="accountDAO" class="kai.SpringTransaction1.AccountDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="accountServiceTarget" class="kai.SpringTransaction1.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- 每个bean配置一个代理 -->
<bean id="accountService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="accountServiceTarget" />
<property name="proxyInterfaces" value="kai.SpringTransaction1.IAccountService" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
c.编写Account类
package kai.SpringTransaction1;
public class Account implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 909891879728703117L;
private Integer accountId;
private String accountname;
private Integer money;
// Property accessors
public Integer getAccountId() {
return this.accountId;
}
public void setAccountId(Integer accountId) {
this.accountId = accountId;
}
public String getAccountname() {
return this.accountname;
}
public void setAccountname(String accountname) {
this.accountname = accountname;
}
public Integer getMoney() {
return this.money;
}
public void setMoney(Integer money) {
this.money = money;
}
// Constructors
/** default constructor */
public Account() {
}
/** full constructor */
public Account(String accountname, Integer money) {
this.accountname = accountname;
this.money = money;
}
}
d.编写AccountDAO和IAccountService,AccountService和Account.hbm.xml,因为是基于代理的,因此服务层必须要有接口,即必须有IAccountService,不能直接代理AccountService。
package kai.SpringTransaction1;
import org.hibernate.SessionFactory;
public class AccountDAO {
public SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory=sessionFactory;
}
@SuppressWarnings("deprecation")
public void addMoney(Integer accountId, int money) {
Account ac=(Account) sessionFactory.getCurrentSession().get(Account.class, accountId);
ac.setMoney(money+ac.getMoney());
sessionFactory.getCurrentSession().update(ac,accountId);
}
public void subMoney(Integer accountId, int money) {
Account ac=(Account) sessionFactory.getCurrentSession().get(Account.class, accountId);
ac.setMoney(ac.getMoney()-money);
sessionFactory.getCurrentSession().update(ac,accountId);
}
}
package kai.SpringTransaction1;
public interface IAccountService {
public void transfer(Integer fromAccountId, Integer toAccountId, int money) ;
}
package kai.SpringTransaction1;
public class AccountService implements IAccountService{
private AccountDAO accountDAO;
/**
* 通过 Spring 将 DAO 注入到 Service
*
* @param accountDAO
*
*/
public void setAccountDAO(AccountDAO accountDAO) {
this.accountDAO = accountDAO;
}
/**
* 转账方法包括两个原子方法:转出方法和转入方法
*
* @param fromAccountId
* @param toAccountId
* @param money
*/
public void transfer(Integer fromAccountId, Integer toAccountId, int money) {
accountDAO.subMoney(fromAccountId, money);
accountDAO.addMoney(toAccountId, money);
}
}
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="kai.SpringTransaction1.Account" table="account" catalog="test">
<id name="accountId" type="java.lang.Integer">
<column name="accountId" />
<generator class="native" />
</id>
<property name="accountname" type="java.lang.String">
<column name="accountname" length="20" />
</property>
<property name="money" type="java.lang.Integer">
<column name="money" length="20" />
</property>
</class>
</hibernate-mapping>
e.编写测试类
package kai.SpringTransaction1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
ApplicationContext act = new FileSystemXmlApplicationContext(
"src/applicationContext.xml");
IAccountService accountService = (IAccountService) act
.getBean("accountService");
try {
// 帐号1转账1元至帐号2
accountService.transfer(1, 2, 1);
} catch (Exception e) {
System.out.println("转账失败");
}
}
}
f.运行:查看数据库可知钱成功转账,若将测试类中改为从账户1转到不存在的张户2,则1的钱也不会减少。
2.对服务层所有bean配置同一个代理:
只需将1中的applicationContext.xml文件修改为如下:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT"></property>
<property name="username" value="root"></property>
<property name="password" value="mysqladmin"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>kai/SpringTransaction1/Account.hbm.xml</value>
</list>
</property>
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO和service -->
<bean id="accountDAO" class="kai.SpringTransaction1.AccountDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="accountServiceTarget" class="kai.SpringTransaction1.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- 配置所有服务层bean的共同代理-->
<bean id="transactionBase"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 所有bean配置一个代理 -->
<bean id="accountService" parent="transactionBase">
<property name="target" ref="accountServiceTarget" />
</bean>
</beans>
3.基于拦截器的实现,也是只需要修改applicationContext.xml文件:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT"></property>
<property name="username" value="root"></property>
<property name="password" value="mysqladmin"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>kai/SpringTransaction1/Account.hbm.xml</value>
</list>
</property>
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO和service -->
<bean id="accountDAO" class="kai.SpringTransaction1.AccountDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="accountService" class="kai.SpringTransaction1.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- 配置拦截器实现事务 -->
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
</beans>
4.使用AOP方式实现:
在1的基础上修改applicationContext.xml文件以及引入依赖包:aspectj和aspectjweaver。
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT"></property>
<property name="username" value="root"></property>
<property name="password" value="mysqladmin"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>kai/SpringTransaction1/Account.hbm.xml</value>
</list>
</property>
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO和service -->
<bean id="accountDAO" class="kai.SpringTransaction1.AccountDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="accountService" class="kai.SpringTransaction1.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- 下面使用aop切面的方式来实现 事务控制-->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<!--配置事务传播性,隔离级别以及超时回滚等问题 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置事务切点 -->
<aop:pointcut id="services"
expression="execution(* kai.SpringTransaction1.AccountService.*(..))" />
<aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
</aop:config>
</beans>
5.注解式事务:(这是我觉得最优雅的):
修改applicationContext.xml文件同时在需要加事务控制的方法上加上@Transactional注解。此处即在AccountService的transfer方法上加上@Transactional注解。
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=GMT"></property>
<property name="username" value="root"></property>
<property name="password" value="mysqladmin"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>kai/SpringTransaction1/Account.hbm.xml</value>
</list>
</property>
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO和service -->
<bean id="accountDAO" class="kai.SpringTransaction1.AccountDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="accountService" class="kai.SpringTransaction1.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- 下面使用全注解的方式来实现 事务控制-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
补充说明:
1.@Transactional
(1)这个注解放在类上面,会对这个类中的所有public方法都起作用;
(2)放在方法上,只有当这个方法是public方法才起作用。
(3)事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
(4)事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
参考文章:
http://blog.youkuaiyun.com/it_man/article/details/5074371
http://blog.youkuaiyun.com/lifangshui/article/details/6324348
http://blog.youkuaiyun.com/qh_java/article/details/51811533