如何开发一个mybatis扩展框架
都说官网是最好的入门。当你参考mybatis官网执行查询发现这样:
// try 执行完后会关闭 session
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
如果你不关闭 SqlSession
会发现,执行几个后,连接池将会耗尽,因为你未关闭连接。
前提可参考这篇文章:https://lingkang.top/archives/tong-guo-dai-ma-jia-zai-mybatis-de-mapperxml
如果你要开发一个 mybatis
扩展框架,如何能反复调用呢?
如果你要开发一个 mybatis
扩展框架,如何能反复调用呢?
我们可以参考 mybatis-spring
框架源码,mybatis-spring 的SqlSessionTemplate
类实现了 mybatis 的 SqlSession
接口,在它构造函数中初始化了 SqlSessionFactory
代理处理 SqlSessionInterceptor
再查看代理实现 SqlSessionInterceptor.invoke
,发现它会在执行完毕后关闭连接
基于代理 SqlSessionFactory
开发一个框架
基于这篇文章搭建的环境:https://lingkang.top/archives/tong-guo-dai-ma-jia-zai-mybatis-de-mapperxml
编写一个 SqlSession
代理处理
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
/**
* @Author lingkang
* @Date 2024/2/29 9:25
*/
@Slf4j
public class MySqlSession implements SqlSession {
private SqlSessionFactory sqlSessionFactory;
private ExecutorType executorType;
private SqlSession sessionProxy;
public MySqlSession(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
// 创建我们的代理,以扩展更多功能
sessionProxy = (SqlSession) Proxy.newProxyInstance(getClass()
.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionProxy()
);
}
@Override
public <T> T selectOne(String statement) {
return sessionProxy.selectOne(statement);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
return sessionProxy.selectOne(statement, parameter);
}
@Override
public <E> List<E> selectList(String statement) {
return sessionProxy.selectList(statement);
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return sessionProxy.selectList(statement, parameter);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return sessionProxy.selectList(statement, parameter, rowBounds);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return sessionProxy.selectMap(statement, mapKey);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return sessionProxy.selectMap(statement, mapKey, mapKey);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return sessionProxy.selectMap(statement, mapKey, mapKey, rowBounds);
}
@Override
public <T> Cursor<T> selectCursor(String statement) {
return sessionProxy.selectCursor(statement);
}
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter) {
return sessionProxy.selectCursor(statement, parameter);
}
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
return sessionProxy.selectCursor(statement);
}
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
sessionProxy.select(statement, parameter, handler);
}
@Override
public void select(String statement, ResultHandler handler) {
sessionProxy.select(statement, handler);
}
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
sessionProxy.select(statement, parameter, rowBounds, handler);
}
@Override
public int insert(String statement) {
return sessionProxy.insert(statement);
}
@Override
public int insert(String statement, Object parameter) {
return sessionProxy.insert(statement, parameter);
}
@Override
public int update(String statement) {
return sessionProxy.update(statement);
}
@Override
public int update(String statement, Object parameter) {
return sessionProxy.update(statement, parameter);
}
@Override
public int delete(String statement) {
return sessionProxy.delete(statement);
}
@Override
public int delete(String statement, Object parameter) {
return sessionProxy.delete(statement, parameter);
}
@Override
public void commit() {
throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");
}
@Override
public void commit(boolean force) {
throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");
}
@Override
public void rollback() {
throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");
}
@Override
public void rollback(boolean force) {
throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");
}
@Override
public List<BatchResult> flushStatements() {
return sessionProxy.flushStatements();
}
@Override
public void close() {
throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理连接");
}
@Override
public void clearCache() {
sessionProxy.clearCache();
}
@Override
public Configuration getConfiguration() {
// 从会话工厂中获取
return sqlSessionFactory.getConfiguration();
}
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
@Override
public Connection getConnection() {
return sessionProxy.getConnection();
}
private class SqlSessionProxy implements InvocationHandler {
private int countNumber = 0;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 例如创建时将 session 存储到线程变量中,线程处理结束就关闭此会话
SqlSession session = sqlSessionFactory.openSession(executorType);
countNumber++;
log.info("获取会话");
System.out.println("获取连接: " + countNumber);
// 如果存在事务时,开启事务
// session.getConnection().setAutoCommit(false);
Object result = null;
try {
result = method.invoke(session, args);
// 事务处理 如果不存在事务就提交操作
// AOP 结束、 请求处理结束、提交事务
} catch (Exception e) {
// 事务处理 如果存在事务就要回滚
// session.rollback();
// log.info("事务回滚");
throw new RuntimeException(e);
} finally {
// 判断是否存在事务,不存在就关闭连接
session.close();
countNumber--;
System.out.println("关闭连接: " + countNumber);
}
return result;
}
}
}
调用
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import top.lingkang.solonweb.mapper.UserMapper;
import java.io.InputStream;
/**
* @author lingkang
* Created by 2024/2/29
*/
@Slf4j
public class Demo01 {
public static boolean isOk = false;
public static void main(String[] args) throws Exception {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test?useSSL=true&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("123456");
config.setMaximumPoolSize(3);
HikariDataSource dataSource = new HikariDataSource(config);
Configuration configuration = new Configuration();
// 用for循环加载所有 xml
InputStream inputStream = Resources.getResourceAsStream("mapper/UserMapper.xml");
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, "mapper/UserMapper.xml",
configuration.getSqlFragments());
mapperParser.parse();
// 配置
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("dev", transactionFactory, dataSource);
configuration.setEnvironment(environment);
configuration.setMapUnderscoreToCamelCase(true);// 下划线转化驼峰
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
// 模拟aop生命周期,session可以存储到线程变量中,线程处理结束就关闭此会话
MySqlSession session = new MySqlSession(sqlSessionFactory, null);
UserMapper mapper = session.getMapper(UserMapper.class);
System.out.println(mapper.selectAll());// 查询所有
System.out.println(mapper.selectOne());
System.out.println(mapper.selectOne());
System.out.println(mapper.selectOne());
System.out.println(mapper.selectOne());
}
}