事務
數據庫事務
- 事務:邏輯上的多條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 = -6424540398559729838L;
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操作