事务
数据库事务
事务:逻辑上的多条sql的分组。该分组内的事务要么全部成功(commit),要么全部失败(rollback)。
数据库默认自动提交事务,即发一条sql就执行一条。
单条sql语句不存在事务概念。
mysql事务语法:
start transaction
...
...
commit/rollback
- start transaction:开启事务
- rollback:回滚事务
- commit:提交事务
ACID
原子性 :事务内的一组sql,要么全部成功,要么全部失败
一致性 :事务执行之前和执行之后状态一致,e.g.拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性 :多个并发事务之间相互隔离,感知不到其他事务的存在
持久性 :事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
隔离级别
没有隔离可能会出现的错误
脏读:A事务读到B事务未提交的更改数据
不可重复读:A事务读数据Data为状态S1, B事务将Data状态更新为S2并提交。A事务再次读Data,Data的状态
幻读
JDBC事务
jdbc从数据库获取connection,默认情况下connection会自动向数据库提交它发送的sql。
jdbc事务流程:
// 关闭自动提交,相当于mysql的start transaction
connection.setAutoCommit(false);
...
...
connection.commit()/rollback();
Spring事务
spring结合mybatis配置
<bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:basePackage="com.cmbchina.ccd.pluto.babylon.*.dao" />
<bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref ="dataSource"
p:configLocation="classpath:mybatis-config.xml" />
<bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref ="dataSource" />
<tx:annotation-driven transaction -manager="transactionManager" />
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration >
<typeAliases >
<package name ="com.cmbchina.ccd.pluto.babylon.deploy.model" />
<package name ="com.cmbchina.ccd.pluto.babylon.deploy.vo" />
<package name ="com.cmbchina.ccd.pluto.babylon.resource.model" />
<package name ="com.cmbchina.ccd.pluto.babylon.resource.vo" />
</typeAliases >
<plugins >
<plugin interceptor ="com.github.pagehelper.PageHelper" >
<property name ="dialect" value ="sqlserver" />
<property name ="offsetAsPageNum" value ="true" />
<property name ="rowBoundsWithCount" value ="true" />
<property name ="pageSizeZero" value ="true" />
<property name ="reasonable" value ="false" />
<property name ="supportMethodsArguments" value ="false" />
<property name ="returnPageInfo" value ="none" />
</plugin >
</plugins >
</configuration >
mybatis技术内幕
SqlSessionFactory: SqlSession工厂bean,singleton,在spring启动时载入mybatis全局配置文件,创建SqlSessionFactory。
SqlSession: 程序与数据库交互的一次会话,SqlSession封装数据库增删改查及事务方法。
SqlSession生命周期:
开启spring事务:一个事务共用一个SqlSession
未开启spring事务:调用一次mybatis方法一个SqlSession
Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据
实现原理:动态代理
/**
* <i>invocation handler</i> of a proxy instance
* Created by z673414 on 2018/6/29.
*
* @author z673414
*/
public class MapperProxy implements InvocationHandler {
public static void main (String[] args) {
MapperProxy mapperProxy = new MapperProxy();
UserMapper userMapper = mapperProxy.newInstance(UserMapper.class);
User user = userMapper.getUserById(1 );
System.out.println(user);
}
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this , args);
} catch (Exception t) {
t.printStackTrace();
}
}
return new User(1 , "xiaoming" );
}
/**
* 创建proxy对象
*
* @param clz
* @param <T>
* @return
*/
public <T> T newInstance (Class<T> clz) {
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, this );
}
}
public class MapperProxy <T > implements InvocationHandler , Serializable {
private static final long serialVersionUID = -6424540398559729838 L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
......
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this , args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
......
}
}
- spring事务执行:
- Spring 创建MapperScannerConfigurer bean,searches recursively starting from a base package for interfaces and registers them as {@code MapperFactoryBean },将sqlSessionFactory注入每个mapperFactoryBean
- MapperFactoryBean : BeanFactory that enables injection of MyBatis mapper interfaces
- getSqlSession().getConfiguration().addMapper(this.mapperInterface)
- Spring 创建SqlSessionFactory bean,解析配置文件生成Configuration 对象
- Configuration 生成MapperRegistry 对象
- MapperRegistry getMapper()生成mapperProxyFactory
- MapperProxyFactory newInstance()生成mapperInterface的代理对象,用于调用mybatis方法,执行数据库CRUD 操作