Mybatis事务管理
事务管理方式
Transaction接口
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
接口实现类有三个分别是JDBCTransaction.class、ManagedTransaction和SpringManagedTransaction(这里主要说前两者)
JDBC事务管理机制(JdbcTransaction)
即JDBC使用的到的Connection对象,下面我们看一下Connection的部分源码怎么写的
PreparedStatement prepareStatement(String sql) throws SQLException;
void setAutoCommit(boolean autoCommit) throws SQLException;
boolean getAutoCommit() throws SQLException;
PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException;
void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException;
String getClientInfo(String name) throws SQLException;
int getHoldability() throws SQLException;
void rollback(Savepoint savepoint) throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
通过这些commit(),rollback(),close()可以实现事物的提交、回滚、关闭等操作
下面来看看JdbcTransaction的一些方法
protected Connection connection;
protected DataSource dataSource;
protected TransactionIsolationLevel level;
//ManagerTransaction没有这个属性
protected boolean autoCommit;
//构造方法
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
protected void openConnection() throws SQLException {
//打印日志
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
//获取连接
this.connection = this.dataSource.getConnection();
//设置事物等级
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
//设置自动提交
this.setDesiredAutoCommit(this.autoCommit);
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (this.connection.getAutoCommit() != desiredAutoCommit) {
if (log.isDebugEnabled()) {
log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + this.connection + "]");
}
//设置自动提交
this.connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException var3) {
throw new TransactionException("Error configuring AutoCommit. Your driver may not support getAutoCommit() or setAutoCommit(). Requested setting: " + desiredAutoCommit + ". Cause: " + var3, var3);
}
}
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close();
}
}
MANAGER管理事物(ManagedTransaction)
这种MANAGER机制主要是为了保证mybatis对事务管理的拓展性和灵活性,使用这种机制就可以和其他框架联合使用,将具体事物的管理都交由第三方框架来进行管理,主要和mybatis整合的框架基本就是Springframework
private DataSource dataSource;
private TransactionIsolationLevel level;
private Connection connection;
//JdbcTransaction类没有这个属性
private final boolean closeConnection;
//构造方法
public ManagedTransaction(Connection connection, boolean closeConnection) {
this.connection = connection;
this.closeConnection = closeConnection;
}
public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
this.dataSource = ds;
this.level = level;
this.closeConnection = closeConnection;
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}
public void close() throws SQLException {
if (this.closeConnection && this.connection != null) {
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
public void commit() throws SQLException {
// Does nothing
}
public void rollback() throws SQLException {
// Does nothing
}
与JDBCTransaction不同的是,它没有尝试设置自动提交。且在关闭是我们可以看出在JdbcTransaction这个类中关闭方法只是判断连接是否为空,而ManagerTransaction还必须判断属性closeConnection是否是true才进行关闭。而且提交和回滚方法都没有进行具体的实现,而是提供给spring去实现具体的业务
配置文件中配置事物执行流程
现在我们来看看在mybatis配置文件中写过配置(具体mybatis配置文件解释||具体mybatis执行流程)
<?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>
<properties resource="db/mysqlconn.properties"></properties>
<!-- 开启懒加载 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 将other转为null值 -->
<setting name="jdbcTypeForNull" value="NULL"/><!-- 不设置这个参数的话有一些数据库不能识别null,如oracle数据库 -->
<!-- 指定日志为log4j -->
<setting name="logImpl" value="LOG4J"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<package name="pojo" />
</typeAliases>
<environments default="development">
<environment id="development">
<!-- 在这里我们选择事务机制,选择了JDBC -->
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<package name="mapper" />
</mappers>
</configuration>
此时我们设置了事务为JDBC,那么在之后在调用XMLConfigBuilder.environmentsElement()方法时,就会生成一个JDBCTransactionFactory来产生JDBCTransaction对象,代码如下:
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
//就这一步,将你传过来的JDBC解析成一个JDBCTransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
下面我们来看事务工厂TransactionFactory接口的源码
public interface TransactionFactory {
//设置一些必要的属性
default void setProperties(Properties props) {
// NOP
}
//和之前的JDBCTransaction和ManagerTransaction的构造方法像吧
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
具体实现类也是和Transaction接口类似,有三个,分别是JDBCTransactionFactory.class、ManagedTransactionFactory和SpringManagedTransactionFactory(主要看前两者)
JdbcTransactionFactory
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
ManagedTransactionFactory
public void setProperties(Properties props) {
if (props != null) {
String closeConnectionProperty = props.getProperty("closeConnection");
if (closeConnectionProperty != null) {
closeConnection = Boolean.valueOf(closeConnectionProperty);
}
}
}
@Override
public Transaction newTransaction(Connection conn) {
return new ManagedTransaction(conn, closeConnection);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new ManagedTransaction(ds, level, closeConnection);
}
可以看出JDBCTransactionFactory没有实现TransactionFactory的setProperties()方法,而ManagerTransactionFactory是实现的
JdbcTemplate
内部为用户封装了JDBC的步骤
public JdbcTemplate() {
}
//看得出必须依赖一个数据源
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
setDataSource(dataSource);
setLazyInit(lazyInit);
afterPropertiesSet();
}
//代理模式(线程安全)
protected Connection createConnectionProxy(Connection con) {
return (Connection) Proxy.newProxyInstance(
ConnectionProxy.class.getClassLoader(),
new Class<?>[] {ConnectionProxy.class},
new CloseSuppressingInvocationHandler(con));
}
在获取连接对象Connection的时,使用了JDK动态代理,所以这个类是线程安全的,每一次拿的连接对象(Connection)都是代理对象