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

事务管理:

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

MyBatis事务管理分为两种:

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

事务的配置:

<environments default="development">

        <environment id="development">
            
            <transactionManager type="JDBC" />

            <dataSource type="POOLED">

              <property name="driver" value="org.gjt.mm.mysql.Driver" />

              <property name="url" value="jdbc:mysql://127.0.0.1/test?useUnicode=true&amp;characterEncoding=utf-8" />

              <property name="username" value="root" />

              <property name="password" value="root" />

            </dataSource>

        </environment>

    </environments>

事务的创建:

事务的创建是交给TransactionFactory来完成,当<transactionManager type="JDBC" />时,MyBatis初始化解析<environment>节点时,会根据其创建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的一级缓存是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间的缓存数据区域互不影响;

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

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

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

测试;

@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();
        }
    }

二级缓存:

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

<settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
<!-- 当前mapper下-->
    <cache eviction="LRU" flushInterval="6000" size="512" readOnly="true"></cache>
@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();
        }
    }

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MyBatis是一个Java持久层框架,它提供了许多特性和功能来简化数据库访问。其中一个重要的特性就是缓存机制,它可以提高数据库访问的性能。 MyBatis缓存机制可以分为两个层次:一级缓存和二级缓存。 一级缓存是在会话级别进行的缓存,也被称为本地缓存。当MyBatis执行查询时,查询的结果会被缓存在会话对象中。如果后续的查询请求使用相同的SQL语句和参数,MyBatis会直接从会话的一级缓存中获取结果,而不再去访问数据库。一级缓存默认是开启的,但在事务提交或关闭会话时会被清空。 二级缓存是在mapper级别进行的缓存,也被称为全局缓存。它可以被多个会话共享,在同一个应用程序中多个会话可以共享相同的数据。二级缓存默认是关闭的,需要在配置文件中进行配置。配置后,当一个会话执行查询时,查询的结果会被缓存在二级缓存中。如果后续的会话执行相同的查询,MyBatis会先检查二级缓存,如果存在缓存结果,则直接返回结果而不再访问数据库。 需要注意的是,当数据发生变动(增删改操作)时,MyBatis会自动清空相应的缓存,以保证数据的一致性。 总结来说,MyBatis缓存机制可以有效地减少数据库访问次数,提高系统的性能。一级缓存是默认开启的会话级别缓存,而二级缓存需要手动配置并在mapper级别使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream答案

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值