目录
一、概念引入
入门案例(概念引入)
1.引入依赖
XML
<dependencies>
<!--spring的核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--连接池 alibaba的-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql驱动-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--spring-test-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
2.工具类
java
package com.qcby.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
/**
* 事务的工具类
*/
public class TxUtils {
private static DruidDataSource ds = null;
// 使用ThreadLocal存储当前线程中的Connection对象
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
// 在静态代码块中创建数据库连接池---static代码块
static {
try {
// 通过代码创建C3P0数据库连接池
ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///spring_db");
ds.setUsername("root");
ds.setPassword("2020");
} 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:
*
*/
//jdbc开启事务靠链接开启
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;
}
}
3.实体类
java
package com.qcby.model;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Account() {
}
public Account(Integer id, String name, Double money) {
this.id = id;
this.name = name;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
4.持久层实现类
java
package com.qcby.dao.impl;
import com.qcby.dao.AccountDao;
import com.qcby.pojo.Account;
import com.qcby.utils.TxUtils;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import com.mysql.jdbc.Driver;
public class AccountDaoImpl implements AccountDao {
//实现两个账户的转账
@Override
public void updateSaveAll(Account account){
Connection connection=null;
PreparedStatement stmt=null;
try{
//获取连接
connection = TxUtils.getConnection();
String sql = "update account set money = money + ? where name = ?";
stmt = connection.prepareStatement(sql);
stmt.setDouble(1, account.getMoney());
stmt.setString(2, account.getName());
// 查询
int result = stmt.executeUpdate();
System.out.println("修改影响了"+result+"行数据!!");
}catch(Exception e){
try {
stmt.close();
//connection.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
}
5.业务层实现类
java
package com.qcby.service.impl;
import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void updateSaveAll(Account account1, Account account2) {
//保存账号1
accountDao.updateSaveAll(account1);
//保存账号2
accountDao.updateSaveAll(account2);
}
}
6.配置文件
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">
<!--配置持久层和业务层-->
<bean id="accountService" class="com.qcby.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.qcby.dao.impl.AccountDaoImpl">
</bean>
</beans>
7.测试类
java
package com.qcby;
import com.qcby.model.Account;
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("classpath:applicationContext.xml")
public class demo {
@Autowired
private AccountService accountService;
@Test
public void run(){
Account account1=new Account(null,"aaa",500.00);
Account account2=new Account(null,"bbb",-500.00);
accountService.updateSaveAll(account1,account2);
}
}
8.运行
查看数据库:
之前:
现在:
9.现在如果转账过程中出现异常
AccountServiceImpl(模拟异常)
java
package com.qcby.service.impl;
import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void updateSaveAll(Account account1, Account account2) {
//保存账号1
accountDao.updateSaveAll(account1);
//模拟异常
int a=1/0;
//保存账号2
accountDao.updateSaveAll(account2);
}
}
再运行:
查看数据库:
只有第一条数据变了,出现错误
10.现在做事务管理
AccountServiceImpl(事务管理)
java
package com.qcby.service.impl;
import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void updateSaveAll(Account account1, Account account2) {
//在这里进行事务管理
try {
//开启事务
TxUtils.startTransaction();
//保存账号1
accountDao.updateSaveAll(account1);
//模拟异常
int a=1/0;
//保存账号2
accountDao.updateSaveAll(account2);
//提交事务
TxUtils.commit();
}catch (Exception e){
e.printStackTrace();
//回滚事务
TxUtils.rollback();
}finally {
//关闭资源
TxUtils.close();
}
}
}
这里仍然有异常,但是进行了事务管理,
运行
查看数据库:
原来:
现在:
回滚了
11.生成代理对象
代理对象:
java
package com.qcby.proxy;
import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 传入目标对象,生成该对象的代理对象,返回。对目标对象的方法进行增强
* */
public class JdkProxy {
//获取代理对象
public static Object getProxy(AccountService accountService){
/*
* 使用JDK动态代理生成代理对象
* 第一个参数:类的加载其
* 第二个参数:当前传入的对象实现了哪些接口要字节码的对象
* 第三个参数:回调函数
* */
Object proxy = Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler(){
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;
}
}
业务层实现类
(有异常)(之前在这里写的事务就不用写了)
java
package com.qcby.service.impl;
import com.qcby.dao.AccountDao;
import com.qcby.pojo.Account;
import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;
import java.sql.SQLException;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;//因为这里是空指针,所以要有set方法
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void updateSaveAll(Account account1, Account account2){
try {
//保存账号1
accountDao.updateSaveAll(account1);
//模拟异常
int a=1/0;
//保存账号2
accountDao.updateSaveAll(account2);
}catch (Exception e){
e.printStackTrace();
}
}
}
测试类
(多了一个run2())
java
package com.qcby.springAopTest;
import com.qcby.pojo.Account;
import com.qcby.proxy.JdkProxy;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private AccountService accountService;
@Test
public void run1(){
Account account1=new Account(null,"aaa",500.00);
Account account2=new Account(null,"bbb",-500.00);
accountService.updateSaveAll(account1,account2);
}
@Test
public void run2(){
Account account1=new Account(null,"aaa",500.00);
Account account2=new Account(null,"bbb",-500.00);
//生成代理对象
Object proxyobj= JdkProxy.getProxy(accountService);
AccountService proxy=(AccountService)proxyobj;
proxy.updateSaveAll(account1,account2);
}
}
运行:
查看数据库:
原来:
现在: