MyBatis(三)-事务管理和缓存机制

事务管理:

MyBatis事务的设计重点是Transaction接口;

MyBatis事务管理分为两种:

  • JdbcTransaction:即利用java.sql.Connection对象完成对事务的提交、回滚、关闭等;
  • ManagedTransaction:这种机制MyBatis自身不会实现事务管理,而是让程序的容器来实现对事务的管理;

事务的配置:



        
            
            

            

              

              

              

              

            

        

    

事务的创建:

事务的创建是交给TransactionFactory来完成,当时,MyBatis初始化解析节点时,会根据其创建JdbcTransactionFactory;

JdbcTransaction实现类:Transaction的实现类,通过使用jdbc提供的方式来管理事务,通过Connection提供的事务管理方法来进行事务管理,源码如下:

 

public class JdbcTransaction implements Transaction {
 
  private static final Log log = LogFactory.getLog(JdbcTransaction.class);
 
  /* 连接**/
  protected Connection connection;
  
  /* 数据源**/
  protected DataSource dataSource;
  
  /* 事务等级**/
  protected TransactionIsolationLevel level;
  
  /* 事务提交**/
  protected boolean autoCommmit;
 
  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommmit = desiredAutoCommit;
  }
 
  public JdbcTransaction(Connection connection) {
    this.connection = connection;
  }
 
  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
	//返回连接
    return connection;
  }
 
  @Override
  public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
	  //连接提交
      connection.commit();
    }
  }
 
  @Override
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
	  //连接回滚
      connection.rollback();
    }
  }
 
  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit();
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + connection + "]");
      }
	  //关闭连接
      connection.close();
    }
  }
 
  protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
    try {
	  //事务提交状态不一致时修改
      if (connection.getAutoCommit() != desiredAutoCommit) {
        if (log.isDebugEnabled()) {
          log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(desiredAutoCommit);
      }
    } catch (SQLException e) {
      // Only a very poorly implemented driver would fail here,
      // and there's not much we can do about that.
      throw new TransactionException("Error configuring AutoCommit.  "
          + "Your driver may not support getAutoCommit() or setAutoCommit(). "
          + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
    }
  }
 
  protected void resetAutoCommit() {
    try {
      if (!connection.getAutoCommit()) {
        // MyBatis does not call commit/rollback on a connection if just selects were performed. select操作没有commit和rollback事务
        // Some databases start transactions with select statements 一些数据库在select操作是会开启事务
        // and they mandate a commit/rollback before closing the connection.
        // A workaround is setting the autocommit to true before closing the connection.
        // Sybase throws an exception here.
        if (log.isDebugEnabled()) {
          log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(true);
      }
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Error resetting autocommit to true "
          + "before closing the connection.  Cause: " + e);
      }
    }
  }
  //打开连接
  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
	//从数据源中获得连接
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommmit);
  }
 
}

 

ManagedTransaction实现类:通过容器来进行事务管理,所有它对事务提交和回滚并不会做任何操作,源码如下:

public class ManagedTransaction implements Transaction {
 
  private static final Log log = LogFactory.getLog(ManagedTransaction.class);
 
  private DataSource dataSource;
  private TransactionIsolationLevel level;
  private Connection connection;
  private 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;
  }
 
  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }
  //提交操作无效
  @Override
  public void commit() throws SQLException {
    // Does nothing
  }
  //回滚操作无效
  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }
 
  @Override
  public void close() throws SQLException {
    if (this.closeConnection && this.connection != null) {
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + this.connection + "]");
      }
	  //关闭连接
      this.connection.close();
    }
  }
 
  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());
    }
  }
 
}

缓存机制:

MyBatis的缓存分为两级:一级缓存、二级缓存

  • 一级缓存是SqlSession级别的缓存,缓存的数据只在SqlSession内有效;
  • 二级缓存是mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的;
  • 缓存机制减轻数据库压力,提高数据库性能;

MyBatis(三)-事务管理和缓存机制_第1张图片

MyBatis(三)-事务管理和缓存机制_第2张图片

一级缓存:

  MyBatis的一级缓存是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间的缓存数据区域互不影响;

  • MyBatis的缓存机制是基于id进行的缓存,即使用对象的id作为key,而对象作为value保存;

一级缓存的作用域是SqlSession范围的,在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将查询到的数据写到缓存(内存),第二次查询时会从缓存中查询数据,不再去底层数据库查询,提高效率。

  • 如果SqlSession执行了delete、insert、update操作并提交到数据库,MyBatis会清空一级缓存,保证了缓存中存储最新的信息,避免脏读,当一个SqlSession结束后该SqlSession中的一级缓存也就不存在;

MyBatis(三)-事务管理和缓存机制_第3张图片

测试;

@Test
    public void query(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        try{
            User user=sqlSession.selectOne("selectUserById",1);
            System.out.println(user.getUsername());
            System.out.println("------------------");
            User user1=sqlSession.selectOne("selectUserById",1);
            System.out.println(user1.getUsername());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

MyBatis(三)-事务管理和缓存机制_第4张图片

二级缓存:

  • 二级缓存是mapper级别的缓存,使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储,相比一级缓存,二级缓存的范围更大,多个SqlSession公用二级缓存,二级缓存是跨SqlSession;
  • 二级缓存是多个SqlSession共享,其作用域是mapper的同一个namespace.不同的SqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数相同,即最终执行相同的sql语句,则第一次执行完毕会将查询到的数据写到缓存(内存),第二次查询时会从缓存中查询数据,不再去底层数据库查询,提高效率;
  • MyBatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存;

MyBatis(三)-事务管理和缓存机制_第5张图片


        
        
    

    
@CacheNamespace(size = 512,flushInterval = 6000)

eviction:收回策略,默认LRU,

  • LRU:最近最少使用策略,
  • FIFO:先进先出策略,
  • SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象,
  • WEAK:弱引用策略,更积极的移除基于垃圾回收器状态和弱引用规则的对象

使用二级缓存时,与查询结果映射的Java对象必须实现接口的序列化和反序列化,因为二级缓存数据存储介质多种多样,不一定在内存,有可能是硬盘或者远程服务器;

测试:

@Test
    public void query(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        try{
            User user=sqlSession.selectOne("selectUserById",1);
            System.out.println(user.getUsername());
            sqlSession.close();
            System.out.println("------------------");
            sqlSession=sqlSessionFactory.openSession();
            User user1=sqlSession.selectOne("selectUserById",1);
            System.out.println(user1.getUsername());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

MyBatis(三)-事务管理和缓存机制_第6张图片

当关闭一级缓存时,会从二级缓存中查找,即只执行一次sql语句;

 

你可能感兴趣的:(MyBatis)