1. Spring的AOP编程
1.1 纯xml编程
<!--通知配置类型-->
<aop:config>
<!--设置切面-->
<aop:aspect ref="logger">
<!--设置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
<!--设置通知-->
<!--前置通知-->
<aop:before method="beforeLog" pointcut-ref="pt"></aop:before>
<!--后置通知-->
<aop:after-returning method="afterReturningLog" pointcut-ref="pt"></aop:after-returning>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
<!--最终通知-->
<aop:after method="after" pointcut-ref="pt"></aop:after>
</aop:aspect>
</aop:config>
或
<!--通知配置类型-->
<aop:config>
<!--设置切面-->
<aop:aspect ref="logger">
<!--设置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
<!--设置通知-->
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pt"></aop:around>
</aop:aspect>
</aop:config>
1.2 结合xml和注解编程
- 需求:service层实现事务管理,包括事务执行、事务提交,事务回滚。建议使用环绕通知
1.2.1 创建项目、添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.azure</groupId>
<artifactId>day54projects_spring_AOP</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
1.2.2 service
1.2.2.1 接口
public interface IAccountService {
/*模拟保存账户功能*/
void save();
String update(int id);
}
1.2.2.2 实现类
public class AccountServiceImpl implements IAccountService {
@Override
public void save() {
System.out.println("执行保存方法!");
}
@Override
public String update(int id) {
System.out.println("方法参数:"+id);
return "Service本身方法返回值";
}
}
1.2.4 事务切面类
/**
* 事务切面类
*/
@Aspect //指定当前类为切面类
@Component //创建切面类对象
public class TransactionManager {
//定义切点表达式
@Pointcut("execution(* com.azure.service.*.*(..))")
public void pt(){}
/*
// 【前置通知】
@Before("pt()")
public void beginTransaction(){
System.out.println("【前置通知】 在执行目标方法之前执行");
}
//【后置通知(返回后通知)】
@AfterReturning("pt()")
public void afterReturning(){
System.out.println("【后置通知】执行目标方法正常结束执行");
}
//【异常通知】
@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("【异常通知】 执行目标方法出现异常时候执行");
}
//【最终通知】
@After("pt()")
public void after() {
System.out.println("【最终通知】在调用目标对象方法后始终执行");
}
*/
// 环绕目标对象方法执行
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
try {
System.out.println("[环绕通知] 环绕前");
//获取参数
Object[] args = pjp.getArgs();
//修改参数
args[0] = 898;
//执行方法
Object retV = pjp.proceed(args);
System.out.println("[环绕通知] 环绕后");
//返回方法执行结果,还能修改结果
return retV + "~~~想不到吧";
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("[环绕通知] 环绕异常");
return null;
} finally {
System.out.println("[环绕通知] 环绕最终");
}
}
}
1.2.5 测试类
public class TestApp {
public static void main(String[] args) {
//创建对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//因为service实现接口,故采用jdk动态代理。必须用接口接收。如果用实现类接收会报错!
IAccountService service = (IAccountService) ac.getBean("accountServiceImpl");
//class com.sun.proxy.$Proxy18
System.out.println(service.getClass());
String result = service.update(999);
System.out.println(result);
}
}
1.3 纯注解编程
- 需求:改造上述案例代码,使用纯注解实现
1.3.1 配置管理类
/**
* 配置管理类
* 1.@Configuration 指定该类为配置管理类,创建容器时候只要加载当前类字节码就可以?
* 2.@ComponentScans 开启注解扫描,可指定多个@ComponentScan,实现扫描多个包
* 注解多个包的时候,要把所有包放在{}中,每个包之间用逗号分隔
* 3.@ComponentScan 开启注解扫描,扫描一个包(子包)
* 4.@EnableAspectJAutoProxy 开启AOP自动代理
*/
@Configuration
@ComponentScans({
@ComponentScan(basePackages = "com.azure.service"),
@ComponentScan(basePackages = "com.azure.utils")
})
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
1.3.2 测试类
@RunWith(SpringJUnit4ClassRunner.class)//使用spring的junit
@ContextConfiguration(classes = SpringConfiguration.class) //加载配置类
public class TestApp2 {
//从容器中获取对象
@Autowired
private IAccountService service;
@Test
public void test() {
System.out.println(service.getClass());
String result = service.update(999);
System.out.println(result);
}
}
2. 事务回顾
2.1 事务概念
- 事务指一组执行单元,该单元的各个组成部分要不同时执行成功,要不同时执行失败。
2.2 事务特性ACID
- A-原子性(Automicity):事务是不可再分割的最小工作单位;
- C-一致性(Consistency):事务必须是数据库状态从一个一致性状态,转变成另外一个一致性状态。类似能量守恒定律
- I-隔离性(Isolation):一个事务的执行不能受到其它事务的影响。事务间相互独立。
- D-持久性(Durability):一个事务一旦提交,则它对数据库中的数据的改变将会永久存储
3. Spring声明式事务管理
- 声明式事务管理式作用于业务层的事务处理
- Spring的事务管理控制是基于AOP的,可以使用编程方式也可使用配置方式。
3.1 String事务控制的API
- PlatformTransactionManager接口是Spring的事务管理器,提供获取事务状态信息、提交事务、回滚事务的方法。
- 实际开发中使用其实现类
-
使用 SpringJDBC 或 iBatis 进行持久化数据时使用:
org.springframework.jdbc.datasource.DataSourceTransactionManager -
Hibernate 版本进行持久化数据时使用org.springframework.orm.hibernate5.HibernateTransactionManager
3.1.1 TransactionDefinition
-
事务的定义信息对象
-
包含以下方法:
- String getName() 获取事务对象名称 - int getIsolationLevel() 获取事务隔离级 - int getPropagationBehavior() 获取事务传播行为 - int getTimeout() 获取事务超时时间 - boolean isReadOnly() 获取事务是否只读
-
事务的传播行为有7种,常用的有以下三种
REQUIRED 1. 默认值 2. 表示当前执行方法必须有事务环境 3. 如果当前执行方法没有事务环境,则创建新的事务;如果当前执行方法有事务环境,则加入当前事务,就不创建新的事务。 4. 应用:添加、修改、删除需要指定事务的传播行为是REQUIRED SUPPORTS 1. 支持事务 2. 当前执行方法有事务环境则支持,没有事务环境也可以运行。事务可有可无。 3. 应用:查询 REQUERS_NEW 1. 表示当前执行方法必须有事务环境 2. 不管当前方法有没有事务环境,都会创建一个新的事务。
-
超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
-
是否是只读事务
增删改是读写事务,查询设置为只读
3.1.2 TransactionStatus
- 事务具体的运行状态,提交事务和回滚事务的方法需要传入此参数
3.2 xml配置方式实现事务管理(重要)
- 作用于service层
- 实现事务代码与业务代码完全解耦,或者说完全分离
下面以service层调用dao层方法保存用户为例,如果保存两个用户的方法之间存在错误代码,而应用事务之后数据库没有变化,说明事务有生效
3.2.0 创建表格
- 此处省略
3.2.1创建项目,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.azure</groupId>
<artifactId>day55projects_spring_tx_xml</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aop支持包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!--事务支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!--数据库连接池c3p0-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--jdbctemplate-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--junit支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
3.2.2 实体类
public class Account {
private int accountId;
private int uid;
private double money;
/*省略*/
}
3.2.3 dao层
3.2.3.1 dao接口
public interface IAccountDao {
/*保存账户*/
void save(Account account);
}
3.2.3.2 dao接口
@Repository
public class AccountDaoImpl implements IAccountDao {
/*使用注解获取对象*/
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save(Account account) {
jdbcTemplate.update("insert into account values (null,?,?)",account.getUid(),account.getMoney());
}
}
3.2.4 service层
3.2.4.1 service接口
public interface IAccountService {
/*保存账户*/
void save(Account account);
}
3.2.4.2 service实现类
@Service
public class AccountServiceImpl implements IAccountService {
/*使用注解获取dao对象*/
@Autowired
private IAccountDao accountDao;
@Override
public void save(Account account) {
/*模拟事务*/
accountDao.save(account);
int i = 1/0;
accountDao.save(account);
}
}
如果注释掉int i = 1/0;
,数据库插入两条数据,而不注释该行代码数据库无变动,说明事务控制已生效
3.2.5 bean.xml配置
思路:
-
需要进行数据库操作,所以要加载数据库连接池,而加载数据库连接池又要先加载数据库配置文件;
-
需要进行数据库操作,所以要加载jdbcTemplate;
-
因为使用到注解配置,所以要开启注解扫描;
-
使用事务管理,首先要配置事务管理类(切面类),并注入连接池
然后配置事务通知规则,拦截哪些方法,对这些方法进行什么管理(读写?只读?)
因为事务的原理是AOP,所以要配置AOP,里面包含切入点表达式,还有重要的一点,将事务通知规则与切入点表达式建立起关系
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--0.加载数据库配置文件,context标签-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--0.开启注解扫描-->
<context:component-scan base-package="com.azure"></context:component-scan>
<!--1.配置数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--2.配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.配置声明式事务-->
<!--3.1配置事务管理器(事务切面类)-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.2事务通知规则(拦截到指定的方法后如何管理事务:读写、只读),tx标签-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--*表示所有方法,运行时必须有事务环境,且是读写事务-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--3.3AOP配置:监理切入点表达式与事务通知规则的关系,aop标签-->
<aop:config>
<!--设置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
<!--建立关联-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
3.2.6 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml") //加载配置类
public class TestApp {
/*从容器中获取并注入对象*/
@Autowired
private IAccountService accountService;
@Test
public void save(){
Account account = new Account();
account.setUid(46);
account.setMoney(100100);
//保存用户
accountService.save(account);
}
}
3.2.7 bean.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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--0.加载数据库配置文件,context标签-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--0.开启注解扫描-->
<context:component-scan base-package="com.azure"></context:component-scan>
<!--1.配置数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--2.配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.配置声明式事务-->
<!--3.1配置事务管理器(事务切面类)-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.2事务通知规则(拦截到指定的方法后如何管理事务:读写、只读),tx标签-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--对查询的方法进行拦截,事务环境可有可无,只读事务-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="search*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
<!--注意,*号语句必须放在最下方,否则所有的方法都会被拦截而无法执行查询的规则-->
<!--*表示所有方法,运行时必须有事务环境,且是读写事务-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--3.3AOP配置:监理切入点表达式与事务通知规则的关系,aop标签-->
<aop:config>
<!--设置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.azure..*ServiceImpl.*(..))"></aop:pointcut>
<!--建立关联-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
逻辑图:
3.3 注解方式实现事务管理(重要)
- 在bean.xml配置中使用
<tx:annotation-driven transaction-manager="txManager"/>
开启声明式事务注解支持,就可以在业务实现类使用@Transactional注解配置信息
3.3.1 bean.xml改造开启事务注解
<!--0.加载数据库配置文件,context标签-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--0.开启注解扫描-->
<context:component-scan base-package="com.azure"></context:component-scan>
<!--1.配置数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--2.配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.配置声明式事务-->
<!--3.1配置事务管理器(事务切面类)-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.2开启声明式事务注解支持-->
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
3.3.2 service实现类改造
/**
* @Transactional
* 1.Spring声明式事务控制注解
* 2.定义到实现类上,表示当前实现类的所有方法都进行事务控制
* 3.定义到实现类的方法上,表示当前方法应用事务
* 4.定义到接口上,表示接口的所有实现类都应用事务
* 5.定义到接口方法上,表示实现该接口的所有实现类实现此方法时候自动应用事务。
* 6.属性说明
* readOnly = false 默认值,表示读写的事务:增删改操作
* true 只读事务,只能做查询。
* propagation = Propagation.REQUIRED 默认值;
* 表示当前运行方法必须有事务环境。
* isolation = Isolation.DEFAULT 数据事务的隔离级别。
* timeout = -1 表示事务超时时间,指定为-1表示不设置超时时间,
* 由数据库来决定超时时间
* noRollbackFor = ArithmeticException.class
* 遇到指定的异常不回滚。
*/
@Service
@Transactional(
readOnly = false,
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = -1
)
public class AccountServiceImpl_anno implements IAccountService {
/*使用注解获取dao对象*/
@Autowired
private IAccountDao accountDao;
@Override
public void save(Account account) {
/*模拟事务*/
accountDao.save(account);
int i = 1/0;
accountDao.save(account);
}
}
3.3.3 测试类
/**
* 使用spring的junit测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean_anno.xml")
public class TestApp_anno {
/*从容器中获取并注入对象*/
@Qualifier("accountServiceImpl_anno") //因为本模块下有两个IAccountService的实现类,所以要用@Qualifier指定注入的对象
@Autowired
private IAccountService accountService;
@Test
public void save(){
Account account = new Account();
account.setUid(46);
account.setMoney(100100);
//保存用户
accountService.save(account);
}
}
3.4 纯注解方式实现事务管理
思路:xml转纯注解的套路,将xml的语句转化成配置类,在配置类上使用注解进行配置
3.4.1 配置管理类SpringConfiguration
/**
* @Configuration 设置为配置类,容器开启时记载此注解的类
* @ComponentScan("com.azure") 开启注解扫描
* @EnableTransactionManagement 开启声明式事务管理,可以使用@Transactional注解
* @Import(JdbcConfig.class) 加载其他配置管理类
*/
@Configuration
@ComponentScan("com.azure")
@EnableTransactionManagement
@Import(JdbcConfig.class)
public class SpringConfiguration {
}
3.4.2 配置JdbcConfig管理类
//加载jdbc.properties配置文件
@PropertySource("jdbc.properties")
public class JdbcConfig {
// 通过@Value获取配置文件值
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 1.创建连接池,加入ioc容器
* @Bean 会自动把方法返回的结果加入ioc容器
* name属性指定加入ioc容器的对象的名称
*/
@Bean(name = "dataSource")
public DataSource createDataSource() throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}
/**
* 2.创建JdbcTemplate,加入ioc容器
* @Bean 修饰的方法的参数:
* 1.会自动根据参数类型去容器中对象注入;
* 2.如果类型有多个,就根据形参名称取容器找该名称对应的对象注入。
* 3.如果想根据指定的名称去容器中找对象,注入到方法的形参上,使用@Qualifier注解
* @Qualifier("dataSource") 去容器找名称是“dataSource”的对象注入方法形参。
*/
@Bean(name = "jdbcTemplate")
public JdbcTemplate createJdbcTemplate(@Qualifier("dataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
/**
* 3. 创建事务管理器
*/
@Bean(name = "txManager")
public DataSourceTransactionManager createDataSourceTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.4.3 测试类
/**
* 使用spring的junit测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class) //加载配置类
public class TestApp {
/*从容器中获取并注入对象*/
@Autowired
private IAccountService accountService;
@Test
public void save(){
System.out.println(accountService.getClass());
Account account = new Account();
account.setUid(46);
account.setMoney(100100);
//保存用户
accountService.save(account);
}
}
4. 总结
一、Spring声明式事务控制
1)什么是声明式事务?
因为每个项目,每个数据访问层实现技术,都要涉及事务控制。所以,spring已经提供了对不同的数据库实现技术的不同事务控制实现。
- PlatformTransactionManager 接口
- DataSourceTransactionManager 连接池中事务控制实现
- JpaTransactionManager jpa中事务控制支持
- HibernateTransactionManager 对hibernate的事务控制支持
2)如何实现实现spring声明式事务?
- XML配置 事务管理器、通知规则、Aop配置
- 注解实现 @Transacrtional 注解
- 纯注解实现 @EnableTransactionManagement
二、Spring声明式事务, 原理
Spring声明式事务原理----> AOP---->动态代理
三、Spring声明式事务的优缺点
-
优点
- 任何项目都要用事务,spring提供了事务控制统一实现,减少开发中事务控制的代码
- 解耦:业务代码与事务控制代码完全分离,最解耦的设计实现。
-
缺点
- Spring声明式事务控制,是粗粒度的事务控制。方法级别的事务控制,不能对方法的某几行进行事务控制。
四、细粒度的事务控制
可以对方法的任意几行进行事务控制。
如何实现细粒度事务控制? 自己写代码控制事务。
(Spring提供的编程式事务控制可以解决:细粒度事务控制问题。)
5. 编程方式实现事务管理
-
使用模板
5.1 改造纯注解案例,bean.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:tx="http://www.springframework.org/schema/tx"
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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--0.加载数据库配置文件,context标签-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--0.开启注解扫描-->
<context:component-scan base-package="com.azure"></context:component-scan>
<!--1.配置数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--2.配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.配置声明式事务-->
<!--3.1配置事务管理器(事务切面类)-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.2编程方式实现事务管理-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
<property name="transactionManager" ref="txManager"></property>
</bean>
</beans>
5.2 改造service实现类
@Service
public class AccountServiceImpl_tsTemplate implements IAccountService {
/*使用注解获取dao对象*/
@Autowired
private IAccountDao accountDao;
//创建编程事务模板
@Autowired
private TransactionTemplate transactionTemplate;
@Override
public void save(Account account) {
//套用模板
TransactionCallback<Object> callback = new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
/*模拟事务*/
accountDao.save(account);
int i = 1/0;
accountDao.save(account);
return null;
}
};
//编程式事务控制
transactionTemplate.execute(callback);
}
}
5.3 改造测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean_template.xml") //加载配置类
public class TestApp_tsTemplate {
/*从容器中获取并注入对象*/
@Qualifier("accountServiceImpl_tsTemplate")
@Autowired
private IAccountService accountService;
@Test
public void save(){
System.out.println(accountService.getClass());
Account account = new Account();
account.setUid(46);
account.setMoney(100100);
//保存用户
accountService.save(account);
}
}