使用注解版本声明事务


  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();
	}

}

 

你可能感兴趣的:(使用注解版本声明事务)