demo包中的TxUtils.java,实现数据库的连接和事务
package com.qcby.demo;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 事务的工具类 数据库连接的工具类
*/
public class TxUtils {
//类属性 连接池对象
private static DruidDataSource ds = null;
// 使用ThreadLocal存储当前线程中的Connection对象
private static ThreadLocal threadLocal = new ThreadLocal();
// 在静态代码块中创建数据库连接池
static {
try {
// 通过代码创建druid数据库连接池
ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=UTF-8");
// ds.setUrl("jdbc:mysql:///spring_db");
ds.setUsername("root");
ds.setPassword("123456");
// 添加Druid的filters参数
//ds.setFilters("stat,wall,log4j"); // 你可以根据需要添加其他过滤器
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* @Method: getConnection
* @Description: 从数据源中获取数据库连接
* @Anthor:
* @return Connection
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn == null) {
// 从数据源中获取数据库连接
conn = getDataSource().getConnection();
// 将conn绑定到当前线程
threadLocal.set(conn);
}
return conn;
}
/**
* @Method: startTransaction
* @Description: 开启事务
* @Anthor:
*
*/
public static void startTransaction() {
try {
Connection conn = threadLocal.get();
if (conn == null) {
conn = getConnection();
// 把 conn绑定到当前线程上
threadLocal.set(conn);
}
// 开启事务
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: rollback
* @Description:回滚事务
* @Anthor:
*/
public static void rollback() {
try {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn != null) {
// 回滚事务
conn.rollback();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: commit
* @Description:提交事务
* @Anthor:
*/
public static void commit() {
try {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn != null) {
// 提交事务
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: close
* @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)
* @Anthor:
*
*/
public static void close() {
try {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn != null) {
conn.close();
// 解除当前线程上绑定conn
threadLocal.remove();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: getDataSource
* @Description: 获取数据源
* @Anthor:
* @return DataSource
*/
public static DataSource getDataSource() {
// 从数据源中获取数据库连接
return ds;
}
}
demo包中的JdkProxy.java 实现jdk动态代理
package com.qcby.demo;
import com.qcby.service.AccountService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 传入目标对象,生成该对象的代理对象,返回。对目标对象的方法进行增强
* 使用jdk动态代理,接口
*/
public class JdkProxy {
/**
* 获取代理对象,返回,增强目标对象的方法
* @param accountService
* @return
*/
public static Object getProxy(AccountService accountService){
/**
* 使用Jdk的动态代理生成代理对象
* 通过Java反射包里提供的proxy的类的api方法,按要求返回一个代理对象
*/
Object proxy = Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
/**
* 调用代理对象的方法,invoke方法就会去执行
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
// 开启事务
TxUtils.startTransaction();
// 对象目标对象的方法进行增强
result = method.invoke(accountService, args);
// 提交事务
TxUtils.commit();
} catch (Exception e) {
e.printStackTrace();
TxUtils.rollback();
} finally {
// 关闭资源
TxUtils.close();
}
return result;
}
});
return proxy;
}
}
dao包中的AccountDao接口和实现类
package com.qcby.dao;
import com.qcby.domain.Account;
import java.sql.SQLException;
import java.util.List;
public interface AccountDao {
public void save(Account account) throws SQLException;
}
package com.qcby.dao;
import com.alibaba.druid.pool.DruidDataSource;
import com.qcby.demo.TxUtils;
import com.qcby.domain.Account;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 使用jdbc操作数据库
*/
public class AccountDaoImpl implements AccountDao{
/**
* 保存
* @param account
*/
@Override
public void save(Account account) throws SQLException {
// System.out.println("持久层:保存账户...");
// 把数据存储到数据库中
// 先获取到连接
Connection conn = TxUtils.getConnection();
// 编写sql语句
String sql = "insert into account values (null,?,?)";
// 预编译SQL语句 PreparedStatement防止sql注入 Statement不可以防止
PreparedStatement stmt = conn.prepareStatement(sql);
// 设置值
stmt.setString(1,account.getName());
stmt.setDouble(2,account.getMoney());
// 执行操作
stmt.executeUpdate();
// 关闭资源 ,conn不能关闭
stmt.close();
}
public void setDataSource(DruidDataSource dataSource) {
}
}
service包中的AccountService接口和实现类
package com.qcby.service;
import com.qcby.domain.Account;
import java.sql.SQLException;
import java.util.List;
public interface AccountService {
public void saveAll(Account account1, Account account2) throws SQLException;
}
package com.qcby.service;
import com.qcby.dao.AccountDao;
import com.qcby.demo.TxUtils;
import com.qcby.domain.Account;
import java.sql.SQLException;
import java.util.List;
/**
* 账户业务实现层
* 目的:实现新增账户
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 实现保存
* @param account1
* @param account2
* @throws SQLException
*/
@Override
public void saveAll(Account account1, Account account2) throws SQLException {
// 保存1账号
accountDao.save(account1);
// 模拟异常
// int a = 1/0;
// 保存2账号
accountDao.save(account2);
}
/**
* 保存2个
* @param account1
* @param account2
@Override
public void saveAll(Account account1, Account account2) {
try {
// 开启事务
TxUtils.startTransaction();
// 保存1账号
accountDao.save(account1);
// 模拟异常
// int a = 1/0;
// 保存2账号
accountDao.save(account2);
// 提交事务/回滚事务
TxUtils.commit();
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
// 回滚事务
TxUtils.rollback();
} finally {
// 关闭资源
TxUtils.close();
}
}
*/
}
测试类
package com.qcby.demo;
import com.qcby.domain.Account;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;
public class Demo1 {
@Test
public void run() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取service对象
AccountService accountService = (AccountService) ac.getBean("accountService");
Account account1 = new Account();
account1.setName("熊大");
account1.setMoney(10000.00);
Account account2 = new Account();
account2.setName("美羊羊");
account2.setMoney(4000.00);
// accountService.saveAll(account1,account2);
// 生成代理对象
Object proxyobj = JdkProxy.getProxy(accountService);
// 强转
AccountService proxy = (AccountService) proxyobj;
// 调用代理对象方法
proxy.saveAll(account1,account2);
}
}
配置文件applicationContext.xml
AOP是OOP(Object-Oriented Programming,面向对象编程)的补充,它允许开发者在不改变原有业务逻辑的情况下,对程序的功能进行增强。这种增强是通过在切点(Pointcut)处插入额外的代码(称为通知,Advice)来实现的。切点定义了程序中需要被增强的具体位置,而通知则定义了要插入的具体代码。
AOP的底层原理主要涉及到代理模式。Spring AOP默认使用JDK动态代理或CGLIB来创建代理对象。
①为接口创建代理类的字节码文件
②使用ClassLoader将字节码文件加载到JVM
③创建代理类实例对象,执行对象的目标方法
2.CGLIB代理:如果被代理的目标对象没有实现任何接口,Spring会使用CGLIB来创建代理对象。CGLIB是一个强大的、高性能的代码生成库,它可以在运行时生成目标对象的子类,并通过重写方法来插入通知代码。
在代理对象被创建后,所有的方法调用都会被转发到代理对象上。代理对象会根据切点的定义来决定是否需要在方法调用前后插入额外的通知代码。
1. AOP相关的术语
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象)-- 代理的目标对象
Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程
Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)-- 是切入点和通知的结合,以后咱们自己来编写和配置的
2. AOP配置文件方式
创建maven项目,坐标依赖
aopalliance
aopalliance
1.0
org.springframework
spring-aspects
5.0.2.RELEASE
org.aspectj
aspectjweaver
1.8.3
创建Spring的配置文件,引入具体的AOP的schema约束
创建包结构,编写具体的接口和实现类
package com.qcby.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("业务层:保存用户...");
}
}
将目标类配置到Spring中
定义切面类
package com.qcby.demo;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 自定义切面类 = 切入点(表达式) + 通知(增强的代码)
*/
public class MyXmlAspect {
/**
* 通知
*/
public void log(){
// 发送手机短信
// 发送邮件/记录日志/事务管理
System.out.println("增强的方法执行了...");
}
/**
* 环绕通知
*/
public void logAround(ProceedingJoinPoint point){
try{
System.out.println("前置方法执行了...");
//执行业务逻辑
//目的执行目标对象的业务逻辑 jdk动态代理 method.invoke执行目标方法
//让目标对象的方法去执行
point.proceed();
System.out.println("后置方法执行了...");
}catch(Throwable e){
e.printStackTrace();
System.out.println("异常方法执行了...");
}finally{
System.out.println("最终方法执行了...");
}
}
}
在配置文件中定义切面类
在配置文件中完成aop的配置
完成测试
package com.qcby.test;
import com.qcby.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo2 {
@Autowired
private UserService userService;
/**
* 测试
*/
@Test
public void run(){
userService.save();
}
}
3. 切入点的表达式
再配置切入点的时候,需要定义表达式,具体展开如下:
切入点表达式的格式如下:
· execution([修饰符] 返回值类型 包名.类名.方法名(参数))
· 修饰符可以省略不写,不是必须要出现的。
· 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
· 包名例如:com.tx.demo3.BookDaoImpl
o 首先com是不能省略不写的,但是可以使用 * 代替
o 中间的包名可以使用 * 号代替
o 如果想省略中间的包名可以使用 ..
· 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
· 方法也可以使用 * 号代替
· 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
4. AOP的通知类型
1. 前置通知 目标方法执行前,进行增强。
2. 最终通知 目标方法执行成功或者失败,进行增强。
3. 后置通知 目标方法执行成功后,进行增强。
4. 异常通知 目标方法执行失败后,进行增强。
5. 环绕通知 目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。
1. AOP注解方式入门程序
编写切面类
给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明
package com.qcby.demo;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 切面类
*/
@Component // 把该类交给IOC去管理
@Aspect // 声明是切面类 ==
public class MyAnnoAspect {
/**
* 通知的方法
*/
// @Before(value = "切入点的表达式")
@Before(value = "execution(public * com.qcby.service.OrderServiceImpl.save(..))")
public void log(){
System.out.println("增强了...");
}
}
配置文件中开启自动代理
package com.qcby.service;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements OrderService{
@Override
public void save() {
System.out.println("业务层:保存用户...");
}
}
编写测试
package com.qcby.test;
import com.qcby.service.UserService;
import com.qcby.service.OrderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
//@ContextConfiguration(classes = SpringConfig.class)
public class Demo3 {
@Autowired
private OrderService orderService;
/**
* 测试
*/
@Test
public void run(){
orderService.save();
}
}
2. 通知类型的注解
通知类型注解
@Before -- 前置通知
@AfterReturing -- 后置通知
@Around -- 环绕通知(目标对象方法默认不执行的,需要手动执行)
@After -- 最终通知
@AfterThrowing -- 异常抛出通知
3. 纯注解的方式
纯注解的方式
package com.qcby.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration // 配置类
@ComponentScan(value = "com.qcby") // 扫描包
@EnableAspectJAutoProxy // 开启自动代理 ==
public class SpringConfig {
@Before(value = "execution(public * com.qcby.service.OrderServiceImpl.save(..))")
public void log(){
System.out.println("增强了...");
}
}
1. JDBC模板技术概述
什么是模板技术:Spring框架中提供了很多持久层的模板类来简化编程,使用模板类编写程序会变的简单
· template 模板 都是Spring框架提供XxxTemplate
提供了JDBC模板,Spring框架提供的 JdbcTemplate类,Connection 表示连接,管理事务 Statement ResultSet
2. JDBC的模板类的使用
jdbc模板类的使用,创建maven工程,引入坐标依赖
org.springframework
spring-context
5.0.2.RELEASE
commons-logging
commons-logging
1.2
log4j
log4j
1.2.12
junit
junit
4.12
org.springframework
spring-test
5.0.2.RELEASE
aopalliance
aopalliance
1.0
org.springframework
spring-aspects
5.0.2.RELEASE
org.aspectj
aspectjweaver
1.8.13
mysql
mysql-connector-java
5.1.6
org.springframework
spring-jdbc
5.0.2.RELEASE
org.springframework
spring-tx
5.0.2.RELEASE
编写测试代码(自己来new对象的方式)
package com.qcby.test;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class Demo1 {
/**
* 使用new对象方式完成
*/
@Test
public void run(){
// 创建连接池对象,Spring框架内置了连接池对象
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// 设置4个参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db?useUnicode=true&characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 提供模板,创建对象
JdbcTemplate template = new JdbcTemplate(dataSource);
// update 完成数据的增删改操作
//query 完成查询操作
template.update("insert into account values (null,?,?)","熊大",1000);
}
}
3. 使用Spring框架来管理模板类
刚才编写的代码使用的是new的方式,应该把这些类交给Spring框架来管理。
Spring管理内置的连接池
编写测试方法
package com.qcby.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo2 {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 测试的方式
*/
@Test
public void run(){
jdbcTemplate.update("insert into account values (null,?,?)","小花",500);
}
}
4. Spring框架管理开源的连接池
配置开源的连接池,使用Druid开源的连接池,引入坐标如下
com.alibaba
druid
1.1.10
①第一种方法:直接在配置文件applicationContext_jdbc.xml中配置,完成核心配置
②第二种方法:将数据库连接的信息配置到属性文件jdbc.properties中
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
完成核心配置
5. Spring框架的JDBC模板的简单操作
package com.qcby.domain;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 实现类,用来进行数据封装的
*/
public class BeanMapper implements RowMapper {
/**
* 是一行一行进行数据封装的
* @param resultSet
* @param i
* @return
* @throws SQLException
*/
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getDouble("money"));
return account;
}
}
增删改查代码编写
package com.qcby.test;
import com.qcby.domain.Account;
import com.qcby.domain.BeanMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo3 {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 测试的方式
*/
@Test
public void run1(){
jdbcTemplate.update("insert into account values (null,?,?)","熊四",800);
}
/**
* 修改
*/
@Test
public void run2(){
jdbcTemplate.update("update account set name = ?,money = ? where id = ?","光头强",100,3);
}
/**
* 删除
*/
@Test
public void run3(){
jdbcTemplate.update("delete from account where id = ?",2);
}
/**
* 通过id查询
*/
@Test
public void run4(){
Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new BeanMapper(), 1);
System.out.println(account);
}
/**
* 查询所有的数据
*/
@Test
public void run5(){
List list = jdbcTemplate.query("select * from account", new BeanMapper());
for (Account account : list) {
System.out.println(account);
}
}
}
1. 完成转账代码的编写
service代码的编写
package com.qcby.service;
public interface AccountService {
/**
* 转账的方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
public void pay(String out,String in,double money);
}
package com.qcby.service;
import com.qcby.dao.AccountDao;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 转账方法
*
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
@Override
public void pay(String out, String in, double money) {
// 调用dao方法
accountDao.outMoney(out, money);
accountDao.inMoney(in, money);
}
}
dao代码的编写
package com.qcby.dao;
public interface AccountDao {
/**
* 付款
* @param out
* @param money
*/
public void outMoney(String out,double money);
/**
* 收款
* @param in
* @param money
*/
public void inMoney(String in,double money);
}
package com.qcby.dao;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 付款
* @param out
* @param money
*/
@Override
public void outMoney(String out, double money) {
jdbcTemplate.update("update account set money = money - ? where name = ?",money,out);
}
/**
* 收款
* @param in
* @param money
*/
@Override
public void inMoney(String in, double money) {
jdbcTemplate.update("update account set money = money + ? where name = ?",money,in);
}
}
配置文件代码编写
测试代码编写
package com.qcby.test;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class Demo4 {
@Autowired
private AccountService accountService;
/**
* 测试转账的方法
*/
@Test
public void testPay(){
accountService.pay("熊大","熊二",100);
}
}
2. Dao编写的方式(第二种方式)
service编写
package com.qcby.service;
import com.qcby.dao.AccountDao;
public class AccountServiceImpl2 implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 转账方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
@Override
public void pay(String out, String in, double money) {
// 调用dao方法
accountDao.outMoney(out,money);
accountDao.inMoney(in,money);
}
}
dao编写
package com.qcby.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {
/**
* 付款
* @param out
* @param money
*/
@Override
public void outMoney(String out, double money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,out);
}
/**
* 收款
* @param in
* @param money
*/
@Override
public void inMoney(String in, double money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,in);
}
}
配置文件编写applicationContext2.xml
测试方法
package com.qcby.test;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext2.xml")
public class Demo5 {
@Autowired
private AccountService accountService;
/**
* 测试转账的方法
*/
@Test
public void testPay(){
accountService.pay("熊大","熊二",100);
}
}
1. Spring框架的事务管理相关的类和API
PlatformTransactionManager接口
平台事务管理器。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类!
接口方法如下:
void commit(TransactionStatusstatus)
void rollback(TransactionStatusstatus)
如果使用的Spring的JDBC模板或者MyBatis框架,需要选择DataSourceTransactionManager实现类
如果使用的是Hibernate的框架,需要选择HibernateTransactionManager实现类
TransactionDefinition接口,事务定义信息接口
定义了事务隔离级别,事务的隔离级别定义了事务处理并发操作时的行为,主要是为了解决数据并发访问时可能出现的问题,如脏读、不可重复读和幻读。隔离级别从低到高分别为:读取未提交内容(Read Uncommitted)、读取提交内容(Read Committed)、可重读(Repeatable Read)和可串行化(Serializable)。其中,Repeatable Read和Serializable级别可以解决脏读和不可重复读问题,而Serializable级别可以解决所有问题。
定义了事务传播行为,事务的传播行为指的是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。Spring框架规定了7种类型的事务传播行为,包括:
2. Spring框架声明式事务管理
配置文件的方式applicationContext3.xml
配置文件+注解的方式applicationContext4.xml
service代码
package com.qcby.service;
import com.qcby.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(isolation = Isolation.DEFAULT)
public class AccountServiceImpl4 implements AccountService {
@Autowired
private AccountDao accountDao;
/**
* 转账方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
@Override
public void pay(String out, String in, double money) throws SQLException{
// 调用dao方法
accountDao.outMoney(out,money);
// 模拟异常
int a = 1/0;
accountDao.inMoney(in,money);
}
}
纯注解的方式
package com.qcby.domain;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* 配置类
*/
@Configuration
@ComponentScan(basePackages="com.qcby")
@EnableTransactionManagement // 开启事务注解
public class SpringConfig {
/**
* @return
* @throws Exception
*/
@Bean(name = "dataSource")
public DataSource createDataSource() throws Exception {
// 创建连接池对象,Spring框架内置了连接池对象
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// 设置4个参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
/**
* 创建模板对象
*
* @return
*/
@Resource(name = "dataSource") // 不仅可以作用在属性上,也可以作用方法上。
@Bean(name = "jdbcTemplate") // 把JdbcTemplate保存到IOC容器中
public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
/**
* 创建平台事务管理器对象
*
* @param dataSource
* @return
*/
@Resource(name = "dataSource")
@Bean(name = "transactionManager")
public PlatformTransactionManager createTransactionManager(DataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
return manager;
}
}