4.0.0
com.learn
demo
0.0.1-SNAPSHOT
javassist
javassist
3.12.1.GA
org.springframework
spring-core
3.0.6.RELEASE
org.springframework
spring-context
3.0.6.RELEASE
org.springframework
spring-aop
3.0.6.RELEASE
org.springframework
spring-orm
3.0.6.RELEASE
org.aspectj
aspectjrt
1.6.1
aspectj
aspectjweaver
1.5.3
cglib
cglib
2.1_2
com.mchange
c3p0
0.9.5.2
mysql
mysql-connector-java
5.1.37
package com.learn.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void add(String name, Integer age) {
String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
int updateResult = jdbcTemplate.update(sql, name, age);
System.out.println("updateResult:" + updateResult);
}
}
package com.learn.service;
//user 服务层
public interface UserService {
public void add();
}
package com.learn.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.learn.dao.UserDao;
import com.learn.service.UserService;
import com.learn.transaction.TransactionUtils;
/**
* 这就是编程式事务
*
* @author Leon.Sun
*
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private TransactionUtils transactionUtils;
@Autowired
private UserDao userDao;
// public void add() {
// TransactionStatus transactionStatus = null;
// try {
// transactionStatus = transactionUtils.begin();
// userDao.add("test001", 20);
// System.out.println("开始报错了..........................");
//// int i = 1/0;
// System.out.println("####################################");
// userDao.add("test002", 21);
// if(transactionStatus!=null) {
// transactionUtils.commit(transactionStatus);
// }
// } catch (Exception e) {
// e.printStackTrace();
// if(transactionStatus!=null) {
// transactionUtils.rollback(transactionStatus);
// }
// }
// }
/**
* 一旦加了这个注解之后
* 就可以通过Spring来帮我们管理事务了
* 记住这是声明式事务
* 或者xml方式
* 声明:@Transactional 或者XML方式
* 一般我们只要加了这个事务注解之后
* logService.addLog();
* 这行代码以走完之后
* 会不会打印日志
* 必须当你代码走完之后
* 方法执行完毕之后,才会提交事务
* 在这里你可以思考一下他的原理
* 方法开始执行之前
* 开启这个事务
* 是不是这样的
* 但是只是我们的肉眼看不到
* 因为他是通过封装起来的
* 这个里面用到了什么技术呢
* 在你的方法之前和之后做了一个处理
* 正常只要你代码一结束
* 只要你方法执行完毕的情况下
* 方法一结束的情况下
* 肯定会提交事务
* 在这边我们来看看效果
* 刷了一下数据库是不是有这样两条数据了
* 这边呢我们怎么做呢
* 我们断点调试一下
* 我抛个异常出来
* 然后我打一个这样的断点
* 然后我们怎么做呢
* 我们这样做
* 我在这边打个断点之后
* 我们走到int i = 1/0;的时候
* 他不会提交到数据库里面去的
* 我把数据库的两条数据给删掉
* 删掉完了之后
* 这个时候我去debug调试一下
* 然后你们注意看
* 现在我刷新一下
* 是不是走完了
* 我们看一下是不是没数据
* 为什么呢
* 因为它需要当你方法执行完毕之后才会提交这样的一个事务的
* 这个就比较简单了
* 然后当我方法一抛异常的情况下
* 这个时候它会走异常通知
* 还没提交
* 这个时候他就会走异常通知
* 接收到异常之后
* 他就会对你的方法做一个回滚
* 你们看一下是不是抛异常了
* 抛异常之后
* 不用看
* 他肯定是不会提交到数据库里面去的
* 也会回滚的
* 那这个回滚的话
* 通过异常通知接收来进行回滚
* 在这里我来问一个问题
*
*
*
*
*/
@Transactional
public void add() {
/**
* 这样做它会回滚吗
* 说一下
* 会不会回滚
* 昨天讲过原理的
* 不要不知道原理
* 会不会
* 绝对不会
* 为什么不会
* 为什么我加上try之后他就不会回滚
* 谁能告诉我这是什么原因
* 谁能解释一下这是什么原因
* 解释一下
* 到底什么原因
* 屏蔽了异常捕获
* 因为异常通知没有接收到这个请求之后
* 没有接收到这个异常之后
* 他就不会做回滚
* 不用看肯定内部消化了
* 算是正常结束
* 我再运行一遍是不是没报错
* 然后发现是不是有test001了
* 但是你会发现
* userDao.add("test002", 21);
* 这条数据没有插进去
* 因为它走到int i = 1/0;这一行的时候
* 报错了
* 进入到catch里面
* 那么我说过了昨天
* 以后你们在使用事务的时候
* 就是你不要去try他
* try的话异常通知
* 我们自己手写事务之后
* 你就知道原理了
* 就是他的异常需要抛到AOP里面去接收起来
* AOP如果是没有拿到异常的时候
* 没有拿到异常通知的情况下
* 因为你是正常结束的
* 我就把你整个事务进行提交了
* 就是这样的
* 出现异常的情况下既然没有提交
* 为什么要回滚呢
* 如果你不回滚的情况下
* 跟你们讲一下
* 会产生什么问题呢
* 就是如果长期这样的话
* 很容器产生死锁的一种现象的
* 我之前遇到一个问题
* 我自己写了一个编程事务
* 一套事务框架
* 就给他们用
* 就是基于这种原理进行封装的
* 然后有些人他不去rollback
* 那么导致什么呢
* 你要知道我搭建个项目有好多人在用这个环境
* 有几百个成员一起跑这个项目
* 然后他的代码一写出来的时候
* 就会导致什么呢
* 这个数据如果没有提交的话
* 只是要mysql查一下
* 查询哪些数据只是没有提交的
* 在MYSQL内存里面的
* 当你提交了之后
* 才会提交到硬盘里面去的
* 那这个有什么坏处呢
* 如果你长期这样做的话
* 很容易导致死锁现象的产生的
* 这个我刚才遇到这个问题的
* 把那些别人没有提交的数据
* 全部给清掉了
* 就好了
* 这个说一下
* 你们一定要注意一下的
* 回滚记住一点
* 既然有人问到过
* 记住一点
* 你有begin
* 还有一个commit
* 然后还有rollback
* rollback是不是就是回滚
* 在这边我给您讲一下
* 你只要begin了
* 那你一定要去commit
* 你一定要去rollback
* 你们知道这个目的是什么意思
* 记住啊
* 就是什么意思呢
* 你既然begin的时候
* 那一定要去commit和rollback
* 如果你不这样的话
* 导致你的事务就一直在存在
* 而且没有把事务结束掉
* 那这样很浪费内存的
* 也很容易产生死锁现象
* 这是我要告诉你的一个原因
* 这个是标配
* 如果执行begin没有rollback或者commit的情况下
* 那MYSQL就会以为你的事务还是一直存在
* 长久情况下
* 就很容易产生死锁现象的
* 只要你们刚开始学事务的时候
* 一般都讲过的
* 我记得我那会都是学过的
* 一定要begin,commit,rollback
* 这是比较基础的知识
* 然后我在这边我告诉你们
* 其实你们看一下文档里面
* 我当时是怎么给你们总结的
* 你们看一下
* 我当时有总结一段话
* 这是你们以后使用事务的一个注意事项
* 什么注意事项呢
* 你们在这边注意看一下
* 这一点你们一定要记住
* 为什么呢
* 不是数据库死锁
* 是表死锁现象
* 我到时讲MYSQL的时候讲一下
* 就会产生死锁现象的
* 事务一直存在就导致挂起了
* 你添加还好
* 尤其是你做修改的时候
* 修改的时候你事务又没有提交
* 别人操作不了
* 你又不提交
* 我又操作不了
* 这个时候肯定会产生死锁现象
* 你们想想是不是这样的
* 你又不提交
* 这个我会跟你讲的
* 不会导致数据库死锁
* 数据库死锁倒不至于
* 数据库死锁那就废了
* 那别人根本就不能操作你的项目了
* 这个我们会在数据库里面讲
* 这是我们讲我们这里的事务
* 然后你们记住一点
* 在使用事务的时候
* 一定要记住什么呢
* 一定不要去try
* 如果直接try的话
* 那你就在catch里面做一个手动的回滚
* 这是我要跟你们讲的
* 获取当前事务
* 手动进行回滚
* 这是我昨天讲过的
* 那我就不再细说了
* 直接获取当前线程的事务
* 然后手动去做这样的回滚
* 写了try之后
* catch里面都会有这段代码
* 手动去回滚
* 这是我要去说的一个问题
* 如果你不手动回滚的话
* 那么我告诉你们
* 绝对产生死锁
* 你们下去试一下
* 绝对产生死锁现象
* 具体我就不给你们谈了
* 这个不谈了
* 然后这个声明式的事务
* 还有一个方式就是xml的方式
* 你们自己下去看一下就行了
*
*
*
*/
try {
userDao.add("test001", 20);
int i = 1/0;
System.out.println("####################################");
userDao.add("test002", 21);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.learn.transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
// 编程事务(需要手动begin 手动回滚 手都提交)
@Component
@Scope("prototype") // 每个事务都是新的实例 目的解决线程安全问题 多例子
public class TransactionUtils {
// 全局接受事务状态
private TransactionStatus transactionStatus;
// 获取事务源
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
// 开启事务
public TransactionStatus begin() {
System.out.println("开启事务");
transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
return transactionStatus;
}
// 提交事务
public void commit(TransactionStatus transaction) {
System.out.println("提交事务");
dataSourceTransactionManager.commit(transaction);
}
// 回滚事务
public void rollback() {
System.out.println("回滚事务...");
dataSourceTransactionManager.rollback(transactionStatus);
}
}
package com.learn.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import com.learn.transaction.TransactionUtils;
/**
* 切面类 基于手手动事务封装
* 记住它是一个封装的事务
* 应该是使用环绕通知好呢
* 还是用后置通知好呢
* 最好是使用环绕通知好
* 因为他是拦截
* 在这里我们只需要定义一个方法就可以了
*
* 这里其实就是类似于声明式事务
* 只是我都放到代码里面去了
* 我怎么验证搭建AOP事务是成功的呢
*
* 解释一下注解里面是怎么封装事务的
*
*
* @author Leon.Sun
*
*/
@Component
@Aspect
public class AopTransaction {
/**
* 我们把自己封装的类注入进来
* 注入进来了之后
* 写一个@Autowired
*
*/
@Autowired
private TransactionUtils transactionUtils;
// TransactionUtils 不要实现为单例子: 如果为单例子的话可能会发生线程安全问题
// // 异常通知
/**
* 如果你长期不提交会产生死锁的
* 会产生死锁现象的
* 是不能捕获异常的
* 我来问一下
* 如果我们这个时候不加异常通知
* public void around(ProceedingJoinPoint proceedingJoinPoint)
* 这段代码这么写对不对
* 肯定不对的
* 他不会走提交
* 如果他不会走提交的话
* 那他就永远占着内存
* 他又不提交
* 这样就会长期的产生死锁对象
* 你们最好通过异常通知
* 去接收这样的一个通知
* 一旦方法抛异常的情况下
* 就做回滚
*
*
* @AfterThrowing这里是Spring底层的框架的默认封装好的
* 这段代码默认就封装好了
* 默认封装好的情况下你try的话
* 你想自己回滚的情况也可以
* 你如果不try把异常抛给我我会回滚的
* 因为首先框架很多是比较底层的
* 接下来会讲到很多
* 并发编程其实不难
* 之前都讲过的
*
*
*/
@AfterThrowing("execution(* com.learn.service.UserService.add(..))")
public void afterThrowing() {
/**
* 回滚事务
*
*/
System.out.println("回滚事务");
/**
* 获取当前事务 直接回滚
* 这里不好写
* transactionStatus这个参数怎么传入进来呢
* 他又在两个不同的方法
* 怎么解决这个问题
* 不要用全局变量
* 用一个方法
* 获取当前的事务
* 怎么获取当前的事务
* 在这边给你讲一下
* TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
* 调用这个方法
* 这个方法干嘛用的
* 这个方法表示什么意思呢
* 直接回滚的
* 就是直接可以进入回滚的
* 那你们可以在这边看一下效果
* 你们不要去定义为全局变量
* 就回滚
* 直接回滚
* 获取当前事务就只解决回滚
* 这边讲一下该怎么做
*
*
*/
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
// 环绕通知 在方法之前和之后处理事情
/**
* 这个方法我们在这里怎么做呢
*
*
* @param proceedingJoinPoint
* @throws Throwable
*/
@Around("execution(* com.learn.service.UserService.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
/**
* 这里表示开启事务
*/
System.out.println("开启事务");
/**
* 这里begin
* 把这个状态transactionStatus传进去
*
*/
TransactionStatus transactionStatus = transactionUtils.begin();
/**
* 然后他会调我们实例的方法
*
*/
proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码
/**
* 这里提交事务
*/
System.out.println("提交事务");
/**
* 如果没有抛异常的情况下就commit
* 然后把transactionStatus这个放进去
* 这里是AOP提交事务
* 提交你再查一下是不是就有了
*
*/
transactionUtils.commit(transactionStatus);
}
}
package com.learn;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.service.UserService;
public class Test001 {
public static void main(String[] args) {
/**
* 我们同样让他去加载spring.xml这个文件
* 去运行一下
*
*
*/
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
/**
* 我们会去运行这个add方法
*
*/
userService.add();
}
}