框架的理解
框架是整个或部分系统的可重用设计
@Service
public class UserService {
@Resource
JdbcTemplate jdbcTemplate;
//常用ORM框架-MyBatis,Hibernate,JdbcTemplate,JPA支持
/**
* 使用spring注解管理事务
*/
@Transactional(rollbackFor = {Exception.class})
public void insertUser(User user) {
//插入用户表
jdbcTemplate.execute("INSERT INTO user_table VALUES('" + user.getId() + "','" + user.getUserName() + "','" + user.getPassword() + "')");
//插入日志记录表
jdbcTemplate.execute("INSERT INTO log_table VALUES('" + UUID.randomUUID().toString() + "','新增用户:" + user.getUserName() + "')");
//模拟异常
int i = 1 / 0;
}
@Resource
DataSource dataSource;
/**
* 不使用框架进行事务管理
*/
public void deleteUser(String userId) throws Exception {
//数据库连接
Connection connection = dataSource.getConnection();
//关闭事务自动提交
connection.setAutoCommit(false);
//执行SQL语句
Statement statement = connection.createStatement();
try {
//---------业务---------
//删除指定id用户
statement.execute("DELETE FROM user_table WHERE id='" + userId + "'");
//插入日志记录表
statement.execute("INSERT INTO log_table VALUES('" + UUID.randomUUID().toString() + "','删除用户:" + userId + "')");
//模拟异常
int i = 1 / 0;
connection.commit();
} catch (Exception e) {
e.printStackTrace();
connection.rollback();
}
}
}
不用框架如何进行事务管理
jdbc-基于连接的层面去控制事务
ORM框架的工作流程:
ORM框架
简单模拟实现JdbcTemplate
@Component//spring托管,创建对象,注入等可复用的功能是oop的体现
public class MyJdbcTemplate {
@Qualifier
DataSource dataSource;
public void execute(String sql) throws Exception {
//数据库连接 每次获取的都是新的连接
Connection connection = dataSource.getConnection();
//关闭自动提交
connection.setAutoCommit(false);
//执行SQL语句
Statement statement = connection.createStatement();
try {
statement.execute(sql);
connection.commit();
} catch (Exception e) {
e.printStackTrace();
connection.rollback();
}
}
}
再次使用只需传入sql语句,无需关心连接等细节。实现了代码的复用。
但此时每次执行SQL语句都是新的连接,无法做事务的控制。
ORM框架只关心绑定参数生成SQL,pojo对象结果映射,SQL的执行。
不在ORM框架中进行事务的控制。
在JdbcTemplate中可以看到是通过调用DataSourceUtils.getConnection()方法获取连接的
它间接调用了TransactionSynchronizationManager(事务同步管理器,用来存放连接)
mybatis
在mybatis中SqlSession就好比是一个数据库的连接
通过SqlSessionUtils工具类的getSqlSession()方法可以得到它的实例
可以看到该方法中也是通过TransactionSynchronizationManager来获取连接的。
所有要和Spring框架集成的ORM框架都需要从这里来获取连接。
TransactionManager
简单实现TransactionMannager
此时存在问题,当多个用户访问同一个controller方法,此时tomcat会用多线程来处理多个请求对该方法的访问,那么多个线程在通过Transaction获取数据库连接时就会获取到同一个连接。那么如果A用户的操作成功,B用户的操作失败了,就需要进行回滚。由于他们使用的是同一个连接,就会导致A用户的操作同样被回滚。
ThreadLocal
此时就引入了ThreadLocal,它是Java中的一种特殊变量。
ThreadLocal:一个线程级别的变量,就是每个线程都拥有了自己独立的一个变量,多线程间的竞争条件被它彻底消除,在并发模式下是绝对安全的变量。
用法:ThreadLocal<T> var=new ThreadLocal<T>();
会自动在每个线程创建一个T的副本,副本之间彼此独立,互不影响。可以用ThreadLocal存储一些参数,以便在线程中多个方法中使用,用来代替方法传参的做法。
测试
在自己定义的TransactionManager中使用TheadLocal以解决上面的问题
此时就可以保证在一个线程的情况下,使用的是同一个连接,就可以在业务层面进行事务管理了。
Spring中的事务
不使用注解管理
spring注解
注解只是一段标记,谁定义的给谁看。
自定义注解
使用注解标记方法为是需要事务的,但此时是不起作用的
要想要它起作用就涉及到aop。对代码进行进一步封装
AOP
功能增强,动态的将新代码的逻辑切入到指定方法的指定位置
上图中,可以看出在使用事务时,发生变化的仅仅是业务逻辑的部分
通过AOP和注解增强业务方法来实现事务
实际的方法执行时按照AOP重新定义的
Spring是通过cglib来实现AOP的
cglib
模拟spring初始化过程
TransactionInterceptor
public class TransactionInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.isAnnotationPresent(Transactional.class)) {
System.out.println("TransactionInterceptor:如果方法加了Transactional注解,则执行前,先执行");
}
//调用被代理方法
Object object = methodProxy.invokeSuper(o, objects);
return object;
}
}
cglib采用继承的方式来实现对被代理方法的增强,子类重写父类的方法。重点在于子类是动态生成的
生成的子类类似下图(用作理解)
生成的子类
总结
了解了框架的意义
为什么使用框架?
框架是开发者定制的可重用应用框架,是模板化的代码帮我们实现很多基础的功能,我们只需要专心于需要实现的业务逻辑,而不需要考虑很多底层功能。
事务
在不使用框架是如何实现事务?
通过连接的层面来处理。
为什么不在ORM框架下管理事务?
ORM框架只关心绑定参数生成SQL,pojo对象结果映射,SQL的执行。事务是在连接层面的,不是它的职责,它只需在TransactionManager中获取到连接即可。
Spring如何管理事务?
通过TransactionManager中ThreadLocal类型变量来保证多线程情况下,每一个线程所执行的操作在同一连接下。只需在业务逻辑前后进行事务操作即可。
定义方法拦截器在业务方法被调用时拦截,通过注解和AOP将事务操作动态的添加到业务方法,注解用来标识需要进行事务操作的业务方法。而AOP通过cglib代理生成被代理方法的子类用继承的方式对事务进行增强。