大纲
-
1、MyBatis缓存管理
-
2、MyBatis事务管理
1、MyBatis缓存管理
MyBatis提供了缓存机制,这可以有效地减少与数据库的交互次数,提高系统性能。
默认情况下,MyBatis会开启一级缓存(SqlSession级别的缓存)和二级缓存(全局共享的缓存)。
1、一级缓存
SQLSession级别缓存,每次MyBatis开启一次和数据库的会话,就会创建出一个SqlSession对象表示一次数据库会话。每一个SqlSession都会创建一个一级缓存,两个不同的SqlSession执行同一个查询Sql也是查询各自的一级缓存。在同一SqlSession中执行的多次查询可以被缓存,从而提高了性能.
-- 优缺点
一级缓存是基于sqlSession级别的缓存,即再同一个sqlSession中执行多次查询可以被缓存,
从而提高性能。并且默认开启,不能被关闭,但可以通过调用clearCache()来清空缓存。
缺点:不能跨SqlSession,SqlSession关闭后,一级缓存将被清空。一级缓存是MyBatis维护的,因此其内部实现可能会影响缓存的透明性和可定制性。
-- 一级缓存的工作流程
-- 创建缓存的key
根据SQLSession执行查询Sql,Executor会根据Sql语句、查询参数等内容创建一个key值,根据key值查询一级缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
-- 获取缓存
根据key值就会从Cache(PerpetualCache)中获取对应的缓存结果
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
// 如果命中,直接将缓存结果返回,否则,会查询数据库,并且将查询到的数据存放在缓存中,最后返回结果。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//占位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//查询数据库
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//清除占位符
localCache.removeObject(key);
}
//将从数据库中的数据存放在缓存中
localCache.putObject(key, list);
//执行Sql都是采用PreparedStatement,可以忽略下面的if语句块
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
//返回结果
return list;
}
2、二级缓存
MyBatis的二级缓存是指在多个SqlSession中执行相同的查询语句时,会将查询结果缓存在内存中,下次执行相同的查询语句时,直接从缓存中获取结果,而不需要再次访问数据库
需要注意的是,二级缓存的实现需要满足以下两个条件:
- 必须在mapper.xml文件中的select标签中添加cache标签,并指定cache的id。
- 查询语句的返回值类型必须是可序列化的,因为缓存是存储在内存中的,需要将缓存结果序列化到磁盘上。如果返回值类型不可序列化,会在缓存时抛出异常。
二级缓存的优缺点:
- 减少数据库的访问次数(优点)
使用二级缓存可以减少数据库的访问次数,从而提高应用程序的响应速度。当应用程序需要重复查询相同的数据时,可以直接从缓存中获取数据,而不用再次访问数据库。
- 提高应用程序的性能(优点)
使用二级缓存可以将查询结果缓存到应用程序的内存中,访问内存的速度比访问数据库的速度要快得多。这样可以大大提高应用程序的性能,尤其是在高并发的情况下。
- 降低数据库的负载(优点)
使用二级缓存可以降低数据库的负载,减少数据库的压力
- 提高应用程序的可扩展性(优点)
使用二级缓存可以提高应用程序的可扩展性。当应用程序需要扩展时,可以在多台服务器之间共享缓存,从而避免了数据不一致的问题,提高了应用程序的可扩展性
- 配置复杂,且可能存在数据不一致性问题(缺点)
二级缓存的配置较为复杂,需要用户进行详细的配置和管理。此外,如果二级缓存中的数据与数据库中的数据不一致,可能会出现问题。因此,二级缓存的正确使用和维护是必要的。
-- 开启二级缓存
全局配置:
<setting name="cacheEnabled" value="true"/>
局部配置:mapper.xml
<!--开启二级缓存-->
<cache/> 或
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
使用:
<select id= "getUserById" paramterType="Integer" resultType="Student" useCache="true">
select * from user where id=#{param}
</select>
注解:
在自定义Mapper接口中,可以通过@CacheNamespace注解来启用单独的二级缓存
@CacheNamespace
public interface UserMapper {
// ...
}
2、MyBatis事务管理
事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。事务可大可小,在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。
1、MyBatis事务管理策略
MyBatis的事务管理分为两种形式:
- (1)使用JDBC的事务管理机制。
这种机制就是利用java.sql.Connection对象完成对事务的提交
- (2)使用MANAGED的事务管理机制。
这种机制mybatis自身不会去实现事务管理,而是让程序的Web容器或者Spring容器来实现对事务的管理。
2、MyBatis中Transaction接口
MyBatis支持的两种事务类型管理器,Transactions接口中对两种事务管理方式进行行为约束。
public interface Transaction {
//JDBC中事务手动管理,需要依靠Connection对象,此方法可以取得Connection对象。
Connection getConnection() throws SQLException;
//设置在什么情况下执行commit()命令
void commit() throws SQLException;
//设置在什么情况下执行rollback()命令
void rollback() throws SQLException;
//业务完毕后,处理Connection对象,一般有两种形式,将这个Connection对象销毁或者将Connection返回数据库连接池中。
void close() throws SQLException;
//Connection向数据库索要一个Transaction对象时的最大等待时间。
Integer getTimeout() throws SQLException;
}
3、Transaction的接口实现类
Transaction接口中有两个实现类:JdbcTransaction和ManagedTransaction。
(1)JdbcTransaction
JdbcTransaction直接使用JDBC的提交和回滚事务管理机制。它依赖与从dataSource中取得的连接connection来管理transaction的作用域,connection对象的获取被延迟到调用getConnection()方法。如果autoCommit设置为on,开启状态的话,它会忽略commit和rollback。
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 autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
this.dataSource = ds;
this.level = desiredLevel;
this.autoCommit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
public Connection getConnection() throws SQLException {
if (this.connection == null) {
this.openConnection();
}
return this.connection;
}
public void commit() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + this.connection + "]");
}
this.connection.commit();
}
}
public void rollback() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + this.connection + "]");
}
this.connection.rollback();
}
}
public void close() throws SQLException {
if (this.connection != null) {
this.resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
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);
}
}
protected void resetAutoCommit() {
try {
if (!this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + this.connection + "]");
}
this.connection.setAutoCommit(true);
}
} catch (SQLException var2) {
if (log.isDebugEnabled()) {
log.debug("Error resetting autocommit to true before closing the connection. Cause: " + var2);
}
}
}
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);
}
public Integer getTimeout() throws SQLException {
return null;
}
}
(2)ManagedTransaction
查看这个类,可知其中的commit方法和rollback方法没有具体实现。ManagedTransaction是让容器来管理事务Transaction的整个生命周期,使用ManagedTransaction的commit和rollback功能不会对事务有任何影响,它没有具体实现,它将事务管理权交给容器来实现。
public class ManagedTransaction implements Transaction {
private static final Log log = LogFactory.getLog(ManagedTransaction.class);
private DataSource dataSource;
private TransactionIsolationLevel level;
private Connection connection;
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;
}
public Connection getConnection() throws SQLException {
if (this.connection == null) {
this.openConnection();
}
return this.connection;
}
public void commit() throws SQLException {
}
public void rollback() throws SQLException {
}
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());
}
}
public Integer getTimeout() throws SQLException {
return null;
}
}
4、MyBatis事务管理流程
MyBatis的事务管理流程如下:
-- 配置事务管理器:
在MyBatis的配置文件中,配置事务管理器的类型和相关属性。
-- 获取事务:
通过事务管理器的getTransaction()方法获取事务对象。
-- 执行数据库操作:
在数据库操作前后,通过事务对象获取数据库连接,并执行相应的数据库操作。
-- 提交事务或回滚事务:
根据操作的成功与否,调用事务对象的commit()或rollback()方法。
-- 关闭事务:
在事务操作完成后,调用事务对象的close()方法关闭数据库连接。
评论