如何开发一个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());}@Overridepublic <T> T selectOne(String statement) {return sessionProxy.selectOne(statement);}@Overridepublic <T> T selectOne(String statement, Object parameter) {return sessionProxy.selectOne(statement, parameter);}@Overridepublic <E> List<E> selectList(String statement) {return sessionProxy.selectList(statement);}@Overridepublic <E> List<E> selectList(String statement, Object parameter) {return sessionProxy.selectList(statement, parameter);}@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {return sessionProxy.selectList(statement, parameter, rowBounds);}@Overridepublic <K, V> Map<K, V> selectMap(String statement, String mapKey) {return sessionProxy.selectMap(statement, mapKey);}@Overridepublic <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {return sessionProxy.selectMap(statement, mapKey, mapKey);}@Overridepublic <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {return sessionProxy.selectMap(statement, mapKey, mapKey, rowBounds);}@Overridepublic <T> Cursor<T> selectCursor(String statement) {return sessionProxy.selectCursor(statement);}@Overridepublic <T> Cursor<T> selectCursor(String statement, Object parameter) {return sessionProxy.selectCursor(statement, parameter);}@Overridepublic <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {return sessionProxy.selectCursor(statement);}@Overridepublic void select(String statement, Object parameter, ResultHandler handler) {sessionProxy.select(statement, parameter, handler);}@Overridepublic void select(String statement, ResultHandler handler) {sessionProxy.select(statement, handler);}@Overridepublic void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {sessionProxy.select(statement, parameter, rowBounds, handler);}@Overridepublic int insert(String statement) {return sessionProxy.insert(statement);}@Overridepublic int insert(String statement, Object parameter) {return sessionProxy.insert(statement, parameter);}@Overridepublic int update(String statement) {return sessionProxy.update(statement);}@Overridepublic int update(String statement, Object parameter) {return sessionProxy.update(statement, parameter);}@Overridepublic int delete(String statement) {return sessionProxy.delete(statement);}@Overridepublic int delete(String statement, Object parameter) {return sessionProxy.delete(statement, parameter);}@Overridepublic void commit() {throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");}@Overridepublic void commit(boolean force) {throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");}@Overridepublic void rollback() {throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");}@Overridepublic void rollback(boolean force) {throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理事务");}@Overridepublic List<BatchResult> flushStatements() {return sessionProxy.flushStatements();}@Overridepublic void close() {throw new UnsupportedOperationException("不支持的方法,已经交由 final-magic 管理连接");}@Overridepublic void clearCache() {sessionProxy.clearCache();}@Overridepublic Configuration getConfiguration() {// 从会话工厂中获取return sqlSessionFactory.getConfiguration();}@Overridepublic <T> T getMapper(Class<T> type) {return getConfiguration().getMapper(type, this);}@Overridepublic Connection getConnection() {return sessionProxy.getConnection();}private class SqlSessionProxy implements InvocationHandler {private int countNumber = 0;@Overridepublic 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循环加载所有 xmlInputStream 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());}}
输出如下
转自 https://lingkang.top/archives/ru-he-kai-fa-yi-mybatis-kuo-zhan-kuang-jia