背景:最近接手了一个新项目,但是领导要求使用jplus,而一起做这个项目的同学表示没有orm框架太过折磨,并且之前并没有使用过JFinal(当然我也没用过ヽ(ー_ー)ノ),于是一合计干脆把mybatisPlus搞进来。
翻阅Jplus官方文档可以看到 在JFinal项目初始化后会调用 IBoot4jAppListenerBase 类的 onStart 方法,给于开发者在项目启动后加入一些自定的初始化逻辑,我们可以在这里配置MybatisPlus初始化方法
public class WebInitializer extends IBoot4jAppListenerBase {
public final static Prop prop = PropKit.use("application.properties");
@Override
public void onStart(){
MybatisPlusConfig.initSqlSession(
// 替换为自己的Mapper路径
prop.get("mybatis.path","cn.iboot4j.admin.mapper")
);
}
}
以下是MybatisPlus初始化方法,嫌麻烦可以直接复制(^_−)☆
public class MybatisPlusConfig {
static String url = IBoot4j.configValue("datasource.url");
static String user = IBoot4j.configValue("datasource.user");
static String password = IBoot4j.configValue("datasource.password");
@Getter
private static SqlSessionFactory sqlSessionFactory;
public static void initSqlSession(String mapperPath){
//设置数据源
Environment environment = new Environment("1", new JdbcTransactionFactory(), initDataSource());
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//这是mybatis-plus的配置对象,对mybatis的Configuration进行增强
MybatisConfiguration configuration = new MybatisConfiguration();
//这是初始化配置,后面会添加这部分代码
initConfiguration(configuration);
//这是初始化连接器,如mybatis-plus的分页插件
configuration.addInterceptor(initInterceptor());
//扫描mapper接口所在包
configuration.addMappers(mapperPath);
//配置日志实现
configuration.setLogImpl(StdOutImpl.class);
configuration.setDefaultEnumTypeHandler(org.apache.ibatis.type.EnumOrdinalTypeHandler.class);
//configuration.addInterceptor(new SqlLogInterceptor());
//构建mybatis-plus需要的globalconfig
GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
//此参数会自动生成实现baseMapper的基础方法映射
globalConfig.setSqlInjector(new DefaultSqlInjector());
//设置数据生成
globalConfig.setMetaObjectHandler(new MybatisPlusMetaObjectHandler());
//雪花id
globalConfig.getDbConfig().setIdType(IdType.ASSIGN_ID);
globalConfig.getDbConfig().setTableUnderline(true);
globalConfig.getDbConfig().setLogicDeleteField("is_deleted");
globalConfig.getDbConfig().setLogicDeleteValue("1");
globalConfig.getDbConfig().setLogicNotDeleteValue("0");
//设置id生成器
globalConfig.setIdentifierGenerator(new DefaultIdentifierGenerator());
//设置超类mapper
globalConfig.setSuperMapperClass(BaseMapper.class);
configuration.setEnvironment(environment);
//构建sqlSessionFactory
sqlSessionFactory = builder.build(configuration);
}
private static DataSource initDataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setURL(url);
dataSource.setUser(user);
dataSource.setPassword(password);
if (StringUtils.isBlank(dataSource.getURL())){
dataSource.setURL("数据库连接");
dataSource.setUser("user");
dataSource.setPassword("pwd");
}
return dataSource;
}
/**
* 初始化配置
*
* @param configuration
*/
private static void initConfiguration(MybatisConfiguration configuration) {
//开启驼峰大小写转换
configuration.setMapUnderscoreToCamelCase(true);
//配置添加数据自动返回数据主键
configuration.setUseGeneratedKeys(true);
}
private static Interceptor initInterceptor() {
//创建mybatis-plus插件对象
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//构建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setMaxLimit(500L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
private static final String CREATE_TIME = "createTime";
private static final String UPDATE_TIME = "updateTime";
private static final String ID_DELETED = "isDeleted";
@Override
public void insertFill(MetaObject metaObject) {
if (metaObject.hasSetter(CREATE_TIME)) {
this.fillStrategy(metaObject, CREATE_TIME, LocalDateTime.now());
}
if (metaObject.hasSetter(UPDATE_TIME)) {
this.fillStrategy(metaObject, UPDATE_TIME, LocalDateTime.now());
}
if (metaObject.hasSetter(ID_DELETED)) {
this.fillStrategy(metaObject, ID_DELETED, 0);
}
}
@Override
public void updateFill(MetaObject metaObject) {
if (metaObject.hasSetter(UPDATE_TIME)) {
this.strictUpdateFill(metaObject, UPDATE_TIME, LocalDateTime.class, LocalDateTime.now());
}
}
}
在spring中原来的使用方法是将mapper注入IOC后进行使用,但是因为JFinal官方并没有集成orm框架文档(也可能我没看到( ̄ェ ̄;)),我并不确定以后升级框架会不会导致IOC注入mybatis相关的代码失效,所以从解耦提升稳定性方面考虑,就不使用Jplus框架带的IOC了,我们这里手动创建数据库会话进行操作
public class Mybatis {
private static final Map<Class<?>,SqlSession> SQL_SESSION_MAP = new ConcurrentHashMap<>();
/**
* @param clazz:
* @return T
* @author luoliang
* @description 自动提交
* @date 2024/4/19 9:28
*/
public static <T extends BaseMapper<?>> T getMapper(Class<T> clazz){
return getMapper(clazz,true);
}
public static <T extends BaseMapper<?>> T getMapper(Class<T> clazz,Boolean autoCommit) {
SqlSession sqlSession = MybatisPlusConfig.getSqlSessionFactory().openSession(autoCommit);
SQL_SESSION_MAP.put(clazz,sqlSession);
return sqlSession.getMapper(clazz);
}
@Synchronized
public static void close(Class<?> clazz){
SqlSession sqlSessionClose = SQL_SESSION_MAP.get(clazz);
if (sqlSessionClose!=null){
sqlSessionClose.commit();
sqlSessionClose.close();
SQL_SESSION_MAP.remove(clazz);
}
}
}
这里就集成完毕了,下面是使用方式
1.像往常使用mybatisPlus一样 定义好Mapper类
2.使用mybatisPlus并关闭会话,到这里就完成啦( ̄▽ ̄)~*
后续有同学提出,啊~ 还要手动关闭会话,能不能再简单一点呢?
可以。我们可以使用aop进行自动的会话管理
/**
* @author luoliang
* @description aop自动提交 使用示例见UserServiceImpl
* jfinal没办法对接口使用aop 可能在代码中会存在需要手动提交会话和释放会话的地方
* 比如在 UserServiceImpl中使用了非UserMapper的会话 当出现这种情况 为了保险起见请手动释放sqlSession
*/
@Slf4j
public class SqlSessionInterceptor implements Interceptor {
@Override
public void intercept(Invocation invocation) {
invocation.invoke();
String convertedClassName = convertToMapperClassName(invocation.getTarget().toString());
try {
Mybatis.close(Class.forName(convertedClassName));
} catch (ClassNotFoundException e) {
log.error("SqlSessionInterceptorError : "+convertedClassName);
}
}
public static String convertToMapperClassName(String originalClassName) {
int implIndex = originalClassName.indexOf("impl.");
if (implIndex != -1) {
int serviceImplIndex = originalClassName.indexOf("ServiceImpl", implIndex + 5);
if (serviceImplIndex != -1) {
// 替换为自己的Mapper路径
return "cn.iboot4j.admin.mapper." + originalClassName.substring(implIndex + 5, serviceImplIndex) + "Mapper";
}
}
return null;
}
}
然后在需要使用mybatisPlus的地方加上aop即可
但是这里请注意,JFinal的AOP与Spring中的AOP不同,Jplus更像是一种请求拦截,用于对请求前后进行增强,所以这里会存在一点点隐患,例如在 UserServiceImpl中使用了非UserMapper的会话 会导致非UserMapper的会话无法关闭,所以 为了保险起见还是请手动释放。