Spring框架JdbcTemplate与事务
- Spring学习总结三
- 0、需要的jar包
- 1、JdbcTemplate模板
- 2、转账的例子,层层递进
- 2.0、首先事务分级别
- 至此,spring除了整合没有说以外,其他的都写的差不多了!
- 参考
Spring学习总结三
今天主要说下面几个点:
- JdbcTemplate模板
- API的例子
- 配置spring.xml实现操作数据库的例子(采用DBCP数据源)
- 配置spring.xml实现操作数据库的例子(采用C3P0数据源)
- 继承JdbcDaoSupport的例子
- 事务的讲解(转账的例子,层层递进)
- 正常的一个转账(不存在事务)
- 使用事务管理器+事务模板来处理事务
- 使用事务代理器来处理事务
- 使用AOP来处理事务
- 使用注解来处理事务
0、需要的jar包
下面的包太多了,就不帮大家找了哈!一般这样百度mchange.c3p0 commons.dbcp commons.logging
就可以找到对应的jar,spring的话直接百度Spring下载地址
就行了
1、JdbcTemplate模板
说到JdbcTemplate模板,其实我也没学过!我只学过Hibernate的模板。不过都是数据库,大体操作类似!
1.1、JdbcTemplateAPI的操作
大概需要下面几个步骤:
- 一个实体类
- 一个测试类
1.1.1、User类
package com.csa.entity;
public class User {
private String username;
private String phone;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", phone=" + phone + ", password=" + password + "]";
}
}
1.1.2、测试类
package com.csa.main;
import java.util.List;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.csa.entity.User;
public class JdbcTemplateAPI {
public static void main(String[] args) {
// 1.创建数据源(连接池)dhcp
BasicDataSource dataSource = new BasicDataSource();
// * 基本4项
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:自己数据库的端口号/自己的数据库");
dataSource.setUsername("自己的用户名");
dataSource.setPassword("自己的密码");
// 2.创建模板,JdbcTemplate需要一个数据源,这里是基本的数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 3.通过api操作
@SuppressWarnings({ "unchecked", "rawtypes" })
List<User> queryForList = jdbcTemplate.query("select * from User",
new BeanPropertyRowMapper(User.class));
for (User user : queryForList) {
System.out.println(user);
}
}
}
测试结果:根据大家数据库的不同,数据结果自然不一样!
1.2、配置spring.xml实现操作数据库的例子(采用DBCP数据源)
从上面的例子我们可以看出,dataSource和jdbcTemplate可以交由给spring容器来做!
于是接下来我们将把生成bean的任务交给spring容器,实验需要做的几步:
- 创建一个实体类(与上面的User一样)
- 创建一个UserDao
- 配置spring.xml,将生产bean的任务给spring容器
- 测试
1.2.1、创建User实体类
同上!
1.2.2、创建UserDao类
下面采用的是依赖注入的思想。
package com.csa.dao;
import java.util.List;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.csa.entity.User;
public class UserDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<User> list(){
return jdbcTemplate.query("select * from User", new BeanPropertyRowMapper(User.class));
}
}
1.2.3、配置spring.xml,将生产bean的任务交给spring容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:端口/数据库名"></property>
<property name="username" value="用户名"></property>
<property name="password" value="密码"></property>
</bean>
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- Dao -->
<bean id="userDao" class="com.csa.dao.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
</beans>
1.2.4、测试
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.dao.UserDao;
import com.csa.entity.User;
public class TestDBCP {
public static void main(String[] args) {
String xmlPath = "config/spring.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
List<User> list = userDao.list();
for (User user : list) {
System.out.println(user);
}
}
}
1.3、配置spring.xml实现操作数据库的例子(采用C3P0数据源)
我们现在采用的数据源是基本的数据源,于是我们可以改变一下数据源。改为C3P0数据源!
同样我们把生成bean的任务交给spring容器,实验需要做的几步:
- 创建一个实体类(与上面的User一样)
- 创建一个UserDao
- 配置spring.xml,将生产bean的任务给spring容器
- 测试
1.3.1、创建User实体类
同上!
1.3.2、创建UserDao类
同上!使用依赖注入的思想!
1.3.3、配置spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 这里要注意,BasicDataSource类的属性名与C3P0的属性名有点不同!大家写的时候别忽略了! -->
<!-- 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:端口/数据库名"></property>
<property name="user" value="用户名"></property>
<property name="password" value="密码"></property>
</bean>
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- Dao -->
<bean id="userDao" class="com.csa.dao.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
</beans>
1.3.4、测试类
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.dao.UserDao;
import com.csa.entity.User;
public class TestC3P0 {
public static void main(String[] args) {
String xmlPath = "config/spring.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
List<User> list = userDao.list();
for (User user : list) {
System.out.println(user);
}
}
}
1.4、继承JdbcDaoSupport的例子
我们从上面的DBCP和C3P0的例子中可以看到,我们在Dao里面总要写一个JdbcTemplate模板!很麻烦,这种事情应该交给框架来做。
于是要做下面几个步骤:
- 实体类
- Dao类,并且继承一个JdbcDaoSupport的类
- 配置spring.xml,将IoC和DI给spring来做
- 写jdbc.properties配置文件
- 测试类
1.4.1、实体类
同上!
1.4.2、UserDao类并继承JdbcDaoSupport类
package com.csa.dao;
import java.util.List;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.csa.entity.User;
// 这样简洁多了!
public class UserDao extends JdbcDaoSupport {
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<User> list(){
return this.getJdbcTemplate().query("select * from User", new BeanPropertyRowMapper(User.class));
}
}
1.4.3、配置spring.xml
我们发现spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载jdbc配置文件 -->
<context:property-placeholder location="classpath:config/jdbc.properties"/>
<!-- 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 下面的有点像EL表达式 -->
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- Dao -->
<bean id="userDao" class="com.csa.dao.UserDao">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
1.4.4、写jdbc.properties配置文件
我们看到了在spring.xml中有一个元素是<context:property-placeholder location="classpath:config/jdbc.properties"/>
。
这个我们要在src/config/
下创建一个jdbc.properties
配置文件!内容如下:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:端口/数据库名
jdbc.user=用户名
jdbc.password=密码
1.4.5、测试类
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.dao.UserDao;
import com.csa.entity.User;
public class TestSupport {
public static void main(String[] args) {
String xmlPath = "config/spring.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
List<User> list = userDao.list();
for (User user : list) {
System.out.println(user);
}
}
}
2、转账的例子,层层递进
要说事务,首先是事务具有 原子性、一致性、隔离性、持久性 ,而转账的例子可以说是非常经典了。
需要的jar(多了AOP):
2.0、首先事务分级别
在mysql的默认隔离级别是4,而oracle默认隔离级别是2。
2.1、Read Uncommitted, Read commited, Repeatable read, Serializable
2.1.1、Read Uncommitted
最低的隔离级别,Read Uncommitted最直接的效果就是一个事务可以读取另一个事务并未提交的更新结果。
2.1.1、Read Committed
Read Committed通常是大部分数据库采用的默认隔离级别,它在Read Uncommitted隔离级别基础上所做的限定更进一步, 在该隔离级别下,一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果。 所以,Read Committed可以避免Read Uncommitted隔离级别下存在的脏读问题, 但,无法避免不可重复读取和幻读的问题。
2.1.1、Repeatable Read
Repeatable Read隔离级别可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数据的更新提交与否。 Repeatable Read隔离级别避免了脏读和不可重复读取的问题,但无法避免幻读。(mysql默认隔离级别)
2.1.1、Serializable
最为严格的隔离级别,所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别, 但同时也是性能最差的隔离级别,因为所有的事务在该隔离级别下都需要依次顺序执行,所以,并发度下降,吞吐量上不去,性能自然就下来了。 因为该隔离级别极大的影响系统性能,所以,很少场景会使用它。通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证了系统性能不会损失太大,也能够一定程度上保证数据的一致性。
2.2、并发问题
2.2.1、脏读
针对未提交数据如果一个事务中对数据进行了更新,但事务还没有提交,另一个事务可以“看到”该事务没有提交的更新结果,这样造成的问题就是,如果第一个事务回滚,那么,第二个事务在此之前所“看到”的数据就是一笔脏数据。
2.2.2、不可重复读
针对其他提交前后,读取数据本身的对比不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的,所以,Read Uncommitted也无法避免不可重复读取的问题。
2.2.3、幻读
针对其他提交前后,读取数据条数的对比 幻读是指同样一笔查询在整个事务过程中多次执行后,查询所得的结果集是不一样的。幻读针对的是多笔记录。在Read Uncommitted隔离级别下, 不管事务2的插入操作是否提交,事务1在插入操作之前和之后执行相同的查询,取得的结果集是不同的,所以,Read Uncommitted同样无法避免幻读的问题。
2.1、正常的一个转账(不存在事务)
说完事务,接下来我们来做一个关于转账,然后不存在事务的情况会发生什么?
需要的几个步骤:
- 创建AccountDao接口
- 创建实现类AccountDaoImp
- 创建AccountService接口
- 创建实现类AccountServiceImp
- 配置applicationContext.xml
- 测试
2.1.1、创建AccountDao接口
package com.csa.dao;
public interface AccountDao {
/**
* 出账
* @param outer
* @param money
*/
public void out(String outer, Integer money);
/**
* 入账
* @param inter
* @param money
*/
public void in(String inner, Integer money);
}
2.1.2、创建实现类AccountDaoImp
package com.csa.dao.imp;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.csa.dao.AccountDao;
public class AccountDaoImp extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outer, Integer money) {
this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money, outer);
}
@Override
public void in(String inner, Integer money) {
this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money, inner);
}
}
2.1.3、创建AccountService接口
package com.csa.service;
public interface AccountService {
/**
* 转账
* @param outer 出账人
* @param inner 入账人
* @param money 金钱
*/
public void transfer(String outer, String inner, int money);
}
2.1.4、创建实现类AccountServiceImp
package com.csa.service.imp;
import com.csa.dao.AccountDao;
import com.csa.service.AccountService;
public class AccountServiceImp implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
// 如果将下面的语句放开,程序执行后将报错,并且出账人会减1000大洋,而入账人并没有增加1000大洋
// int i = 1/0;
accountDao.in(inner, money);
}
}
2.1.5、配置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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- dataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<!--
我这里采用的数据库名是transferAccounts
其中有一个表:Account表,存储每个人的"大洋"信息!
-->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transferAccounts"></property>
<property name="user" value="用户名"></property>
<property name="password" value="密码"></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.csa.dao.imp.AccountDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.csa.service.imp.AccountServiceImp">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
2.1.6、测试
package com.csa.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.service.AccountService;
public class App {
public static void main(String[] args) {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
// 数据库中有jack和rose这两个条目!
accountService.transfer("jack","rose",1000);
}
}
2.2、使用事务管理器+事务模板来处理事务
我们发现,如果上面**出现除零异常后,在我们转账的逻辑上出现了异常,这1000大洋不翼而飞(入我们的口袋了)!**这不是我们想要的结果。那该怎么办呢?
spring处理事务的其中一种,它的操作如下:
- 在applicationContext.xml中配置事务管理器
- 在applicationContext.xml配置事务模板
- 在需要处理事务的地方使用事务模板
这个例子的几个步骤:
- 接口AccountDao
- 实现类AccountDaoImp
- 接口AccountService
- 实现类AccountServiceImp
- applicationContext.xml配置
- 测试
2.2.1、接口AccountDao
与上面的一样!
2.2.1、实现类AccountDaoImp
与上面的一样!
2.2.1、接口AccountService
与上面的一样!
2.2.1、实现类AccountServiceImp
需要注入一个TransactionTemplate,事务模板调用execute方法。
package com.csa.service.imp;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.csa.dao.AccountDao;
import com.csa.service.AccountService;
public class AccountServiceImp implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(String outer, String inner, int money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDao.out(outer, money);
int i = 1/0;
accountDao.in(inner, money);
}
});
}
}
2.2.1、applicationContext配置
需要配置事务管理器和事务模板,并且将事务模板注入到Serveice中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- dataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transferAccounts"></property>
<property name="user" value="用户名"></property>
<property name="password" value="密码"></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.csa.dao.imp.AccountDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.csa.service.imp.AccountServiceImp">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- transactionManager管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- transactionTemplate模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"></property>
</bean>
</beans>
2.2.1、测试
package com.csa.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.service.AccountService;
public class App {
public static void main(String[] args) {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
accountService.transfer("jack","rose",1000);
}
}
2.3、使用事务代理器来处理事务
使用事务代理来处理事务需要以下几个步骤:
- 在applicationContext.xml中配置事务管理器。
- 在applicationContext.xml中创建事务代理。
- 在事务代理中配置事务属性。
我们的例子需要做下面几个步骤:
- 创建2个接口
- 创建2个实现类
- 编写applicationContext.xml
- 测试
2.3.1、创建2个接口
两个接口都未改变!
2.3.2、创建2个实现类
UserDao没变,UserService改变为第一个版本!
package com.csa.service.imp;
import com.csa.dao.AccountDao;
import com.csa.service.AccountService;
public class AccountServiceImp implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
// int i = 1/0;
accountDao.in(inner, money);
}
}
2.3.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- dataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transferAccounts"></property>
<property name="user" value="用户名"></property>
<property name="password" value="密码"></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.csa.dao.imp.AccountDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.csa.service.imp.AccountServiceImp">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- transactionManager管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4 service 代理对象
4.1 proxyInterfaces 接口
4.2 target 目标类
4.3 transactionManager 事务管理器
4.4 transactionAttributes 事务属性(事务详情)
prop.key :确定哪些方法使用当前事务配置
prop.text:用于配置事务详情
格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
传播行为 隔离级别 是否只读 异常回滚 异常提交
例如:
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认传播行为,和隔离级别
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只读
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop> 有异常扔提交
-->
<bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces" value="com.csa.service.AccountService"></property>
<property name="target" ref="accountService"></property>
<!-- 事务管理器 -->
<property name="transactionManager" ref="txManager"></property>
<!-- 事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
</props>
</property>
</bean>
</beans>
2.3.4、测试
package com.csa.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.service.AccountService;
public class App {
public static void main(String[] args) {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("proxyAccountService");
accountService.transfer("jack","rose",1000);
}
}
2.4、使用AOP来处理事务
AOP来处理事务需要做的事情:
- 在applicationContext.xml中配置事务管理器
- 将事务管理器当做切面类,应该叫做事务通知
- 在这个事务通知(切面类)中设置事务属性
- AOP编程,将事务通知(切面类)与需要织入的方法关联
我们要做的事情:
- 2个接口
- 2个实现类
- 编写applicationContext.xml
- 测试
2.4.1、2个接口
没变!
2.4.2、2个实现类
没变!
2.4.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: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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- dataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transferAccounts"></property>
<property name="user" value="用户名"></property>
<property name="password" value="密码"></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.csa.dao.imp.AccountDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.csa.service.imp.AccountServiceImp">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 1.事务管理器 -->
<!-- transactionManager管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.事务详情(事务通知) , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
<tx:attributes> 用于配置事务详情(属性属性)
<tx:method name=""/> 详情具体配置
propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
isolation 隔离级别
-->
<tx:advice id="txAdvisor" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 3.AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
<aop:config>
<aop:advisor advice-ref="txAdvisor" pointcut="execution(* com.csa.service..*.*(..))"/>
</aop:config>
</beans>
2.4.4、测试
package com.csa.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.service.AccountService;
public class App {
public static void main(String[] args) {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
accountService.transfer("jack","rose",1000);
}
}
2.5、使用注解来处理事务
用注解来处理事务:
- 在applicationContext.xml中配置注解扫描器。
- 在需要添加事务的方法之上,或者类之上添加
@Transactional
。
我们需要做的事情:
- 2个接口
- 2个实现类
- 配置applicationContext.xml
- 测试
2.5.1、2个接口
与上面的一样,没变!
2.5.2、2个实现类
UserDao没变,UserService添加了注解。
package com.csa.service.imp;
import org.springframework.transaction.annotation.Transactional;
import com.csa.dao.AccountDao;
import com.csa.service.AccountService;
// 在类上面加的话,是作用于这个类的所有方法
@Transactional
public class AccountServiceImp implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
// 在方法上面加的话只作用于这个方法。
// @Transactional
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
int i = 1/0;
accountDao.in(inner, money);
}
}
2.5.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: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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- dataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transferAccounts"></property>
<property name="user" value="root"></property>
<property name="password" value="3306"></property>
</bean>
<!-- dao -->
<bean id="accountDao" class="com.csa.dao.imp.AccountDaoImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- service -->
<bean id="accountService" class="com.csa.service.imp.AccountServiceImp">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- transactionManager管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注解驱动 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
2.5.4、测试
package com.csa.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.csa.service.AccountService;
public class App {
public static void main(String[] args) {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
accountService.transfer("jack","rose",1000);
}
}
至此,spring除了整合没有说以外,其他的都写的差不多了!
参考
各种博客,各种百度,各种以前的案例,黑马的笔记等!链接太多!