mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
!506 重新适配 mybatis-flex-solon-plugin
Merge pull request !506 from 西东/main
This commit is contained in:
commit
c1e16abc0a
@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
@ -10,47 +12,38 @@
|
||||
数据扩展插件,为 Solon Data 提供基于 mybatis-flex([代码仓库](https://gitee.com/mybatis-flex/mybatis-flex))的框架适配,以提供ORM支持。
|
||||
|
||||
|
||||
#### 2、强调多数据源支持
|
||||
|
||||
> Solon 的 ORM 框架都是基于多数据源理念进行适配的。关于 Solon 数据源概念的描述,可参考:[多数据源与动态数据源](https://solon.noear.org/article/353)
|
||||
|
||||
* 强调多数据源的配置。例:demo.db1...,demo.db2...
|
||||
* 强调带 name 的 DataSource Bean
|
||||
* 强调使用 @Db("name") 的数据源注解
|
||||
|
||||
|
||||
@Db 可注入类型:
|
||||
可注入类型:
|
||||
|
||||
| 支持类型 | 说明 |
|
||||
| -------- | -------- |
|
||||
| Mapper.class | 注入 Mapper。例:`@Db("db1") UserMapper userMapper` |
|
||||
| FlexConfiguration | 注入 FlexConfiguration,一般仅用于配置。例:`@Db("db1") FlexConfiguration db1Cfg` |
|
||||
| FlexGlobalConfig | 注入 FlexGlobalConfig,一般仅用于配置。例:`@Db("db1") FlexGlobalConfig db1Gc` |
|
||||
| SqlSessionFactory | 注入 SqlSessionFactory。例:`@Db("db1") SqlSessionFactory db1` (不推荐直接使用) |
|
||||
| RowMapperInvoker | 注入 RowMapperInvoker。例:`@Db("db1") RowMapperInvoker rowMapper` |
|
||||
| -------- |----------------------------------------------------------------------|
|
||||
| Mapper.class | 注入 Mapper。例:`@Inject UserMapper userMapper` |
|
||||
| FlexConfiguration | 注入 FlexConfiguration,一般仅用于配置。例:`@Inject FlexConfiguration db1Cfg` |
|
||||
| FlexGlobalConfig | 注入 FlexGlobalConfig,一般仅用于配置。例:`@Inject FlexGlobalConfig db1Gc` |
|
||||
| SqlSessionFactory | 注入 SqlSessionFactory。例:`@Inject SqlSessionFactory db1` (不推荐直接使用) |
|
||||
| RowMapperInvoker | 注入 RowMapperInvoker。例:`@Inject RowMapperInvoker rowMapper` |
|
||||
|
||||
|
||||
#### 3、数据源配置
|
||||
|
||||
|
||||
```yml
|
||||
# 配置数据源
|
||||
demo.db1:
|
||||
schema: rock
|
||||
# 配置数据源(或者使用 solon.dataSources 配置数据源,效果一样)
|
||||
mybatis-flex.datasource:
|
||||
db1:
|
||||
jdbcUrl: jdbc:mysql://localhost:3306/rock?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: 123456
|
||||
|
||||
# 配置数据源对应的 mybatis 信息(要与 DataSource bean 的名字对上)
|
||||
mybatis.db1:
|
||||
typeAliases: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
||||
mybatis-flex:
|
||||
type-aliases-package: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
||||
- "demo4021.model"
|
||||
- "demo4021.model.*" #这个表达式同上效果
|
||||
typeHandlers: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
||||
type-handlers-package: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
||||
- "demo4021.dso.mybaits.handler"
|
||||
- "demo4021.dso.mybaits.handler.*" #这个表达式同上效果
|
||||
mappers: #支持包名 或 类名(大写开头 或 *)或 xml(.xml结尾)//支持 ** 或 * 占位符
|
||||
mapper-locations: #支持包名 或 类名(大写开头 或 *)或 xml(.xml结尾)//支持 ** 或 * 占位符
|
||||
- "demo4021.**.mapper"
|
||||
- "demo4021.**.mapper.*" #这个表达式同上效果
|
||||
- "classpath:demo4035/**/mapper.xml"
|
||||
@ -58,7 +51,7 @@ mybatis.db1:
|
||||
configuration: #扩展配置(要与 FlexConfiguration 类的属性一一对应)
|
||||
cacheEnabled: false
|
||||
mapUnderscoreToCamelCase: true
|
||||
globalConfig: #全局配置(要与 FlexGlobalConfig 类的属性一一对应)//只是示例,别照抄
|
||||
global-config: #全局配置(要与 FlexGlobalConfig 类的属性一一对应)//只是示例,别照抄
|
||||
printBanner: false
|
||||
keyConfig:
|
||||
keyType: "Generator"
|
||||
@ -70,66 +63,77 @@ mybatis.db1:
|
||||
#
|
||||
```
|
||||
|
||||
> configuration、globalConfig 没有对应属性时,可用代码处理
|
||||
主要支持的属性说明:
|
||||
|
||||
| 支持属性 | 别名(保持与之前的兼容) | 说明 |
|
||||
|-------------------------|----------------------------|----------------------------------|
|
||||
| type-aliases-package | typeAliases | 类型别名 |
|
||||
| type-aliases-super-type | typeAliasesSuperType | 类型别名的父类(用于过滤) |
|
||||
| type-handlers-package | typeHandlers | 类型处理器 |
|
||||
| mapper-locations | mappers | mapper 类或xml文件 |
|
||||
| configuration | configuration | mybatis 配置。对应类:FlexConfiguration |
|
||||
| global-config | globalConfig | 全局部置。对应类:FlexGlobalConfig |
|
||||
|
||||
|
||||
|
||||
##### Mapper 配置注意事项:
|
||||
|
||||
* 通过 mapper 类包名配置。 xml 与 mapper 需同包同名
|
||||
|
||||
```yml
|
||||
mybatis.db1.mappers: "demo4035.dso.mapper"
|
||||
mybatis-flex.mapper-locations: "demo4035.dso.mapper"
|
||||
```
|
||||
|
||||
* 通过 xml 目录进行配置。xml 可以固定在一个资源目录下
|
||||
|
||||
```yml
|
||||
mybatis.db1.mappers: "classpath:mybatis/db1/*.xml"
|
||||
mybatis-flex.mapper-locations: "classpath:mybatis/db1/*.xml"
|
||||
```
|
||||
|
||||
|
||||
#### 4、代码应用
|
||||
|
||||
```java
|
||||
//配置数据源
|
||||
//配置 mf (如果配置不能满足需求,可以进一步代助代码)
|
||||
@Configuration
|
||||
public class Config {
|
||||
//此下的 db1 与 mybatis.db1 将对应在起来 //可以用 @Db("db1") 注入mapper
|
||||
//typed=true,表示默认数据源。@Db 可不带名字注入
|
||||
@Bean(value = "db1", typed = true)
|
||||
public DataSource db1(@Inject("${demo.db1}") HikariDataSource ds) {
|
||||
return ds;
|
||||
@Bean
|
||||
public void ormConfig(@Inject FlexConfiguration cfg,
|
||||
@Inject FlexGlobalConfig globalConfig) {
|
||||
|
||||
cfg.setCacheEnabled(false);
|
||||
|
||||
}
|
||||
|
||||
//@Bean(value = "db2", typed = true)
|
||||
//public DataSource db2(@Inject("${demo.db2}") HikariDataSource ds) {
|
||||
// return ds;
|
||||
//}
|
||||
|
||||
//调整 db1 的配置(如:增加插件)// (配置可以解决的,不需要这块代码)
|
||||
//@Bean
|
||||
//public void db1_cfg(@Db("db1") FlexConfiguration cfg,
|
||||
// @Db("db1") FlexGlobalConfig globalConfig) {
|
||||
|
||||
// cfg.setCacheEnabled(false);
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
//应用
|
||||
@ProxyComponent
|
||||
@Component
|
||||
public class AppService {
|
||||
//可用 @Db 或 @Db("db1") 注入
|
||||
@Db
|
||||
@Inject
|
||||
AppMapper appMapper; //xml sql mapper
|
||||
|
||||
//可用 @Db 或 @Db("db1")
|
||||
@Db
|
||||
@Inject
|
||||
BaseMapper<App> appBaseMapper; //base mapper
|
||||
|
||||
public void test(){
|
||||
public void test0() {
|
||||
App app1 = appMapper.getAppById(12);
|
||||
App app2 = appBaseMapper.selectOneById(12);
|
||||
}
|
||||
|
||||
@UseDataSource("db1")
|
||||
public void test1() {
|
||||
App app1 = appMapper.getAppById(12);
|
||||
App app2 = appBaseMapper.selectOneById(12);
|
||||
}
|
||||
|
||||
public void test2() {
|
||||
try {
|
||||
DataSourceKey.use("db1");
|
||||
App app1 = appMapper.getAppById(12);
|
||||
App app2 = appBaseMapper.selectOneById(12);
|
||||
} finally {
|
||||
DataSourceKey.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>mybatis-solon-plugin</artifactId>
|
||||
<artifactId>solon-data</artifactId>
|
||||
<version>${solon.version}</version>
|
||||
</dependency>
|
||||
|
||||
@ -30,6 +30,13 @@
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>${mybatis-flex.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-aot</artifactId>
|
||||
<version>${solon.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.mybatisflex.core.datasource;
|
||||
|
||||
import org.noear.solon.data.datasource.RoutingDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* @author noear 2024/12/3 created
|
||||
*/
|
||||
public class FlexDataSourceRouting {
|
||||
public static DataSource determineCurrentTarget(DataSource original) {
|
||||
if (original instanceof FlexDataSource) {
|
||||
return ((FlexDataSource) original).getDataSource();
|
||||
}
|
||||
|
||||
if (original instanceof RoutingDataSource) {
|
||||
return ((RoutingDataSource) original).determineCurrentTarget();
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
package com.mybatisflex.solon.aot;
|
||||
|
||||
import com.mybatisflex.solon.integration.XPluginImpl;
|
||||
import com.mybatisflex.solon.mybtais.MybatisAdapterDefault;
|
||||
import org.apache.ibatis.cache.decorators.FifoCache;
|
||||
import org.apache.ibatis.cache.decorators.LruCache;
|
||||
import org.apache.ibatis.cache.decorators.SoftCache;
|
||||
import org.apache.ibatis.cache.decorators.WeakCache;
|
||||
import org.apache.ibatis.cache.impl.PerpetualCache;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.io.DefaultVFS;
|
||||
import org.apache.ibatis.io.JBoss6VFS;
|
||||
import org.apache.ibatis.javassist.util.proxy.ProxyFactory;
|
||||
import org.apache.ibatis.javassist.util.proxy.RuntimeSupport;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
|
||||
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
|
||||
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
|
||||
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
|
||||
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
|
||||
import org.apache.ibatis.logging.stdout.StdOutImpl;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
|
||||
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.noear.solon.aot.NativeMetadataUtils;
|
||||
import org.noear.solon.aot.RuntimeNativeMetadata;
|
||||
import org.noear.solon.aot.RuntimeNativeRegistrar;
|
||||
import org.noear.solon.aot.hint.ExecutableMode;
|
||||
import org.noear.solon.aot.hint.MemberCategory;
|
||||
import org.noear.solon.core.AppContext;
|
||||
import org.noear.solon.core.util.ResourceUtil;
|
||||
import org.noear.solon.core.wrap.MethodWrap;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* mybatis aot 注册 native 元数据
|
||||
*
|
||||
* @author songyinyin
|
||||
* @since 2.3
|
||||
* @link <a href="https://github.com/kazuki43zoo/mybatis-native-demo/blob/main/src/main/java/com/example/nativedemo/MyBatisNativeConfiguration.java">MyBatisNativeConfiguration</a>
|
||||
*/
|
||||
public class MybatisRuntimeNativeRegistrar implements RuntimeNativeRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(AppContext context, RuntimeNativeMetadata metadata) {
|
||||
Stream.of(RawLanguageDriver.class,
|
||||
XMLLanguageDriver.class,
|
||||
RuntimeSupport.class,
|
||||
ProxyFactory.class,
|
||||
Slf4jImpl.class,
|
||||
Log.class,
|
||||
JakartaCommonsLoggingImpl.class,
|
||||
Log4j2Impl.class,
|
||||
Jdk14LoggingImpl.class,
|
||||
StdOutImpl.class,
|
||||
NoLoggingImpl.class,
|
||||
SqlSessionFactory.class,
|
||||
PerpetualCache.class,
|
||||
FifoCache.class,
|
||||
LruCache.class,
|
||||
SoftCache.class,
|
||||
WeakCache.class,
|
||||
ArrayList.class,
|
||||
HashMap.class,
|
||||
TreeSet.class,
|
||||
HashSet.class
|
||||
).forEach(x -> metadata.registerReflection(x, MemberCategory.values()));
|
||||
|
||||
Stream.of(
|
||||
"org/apache/ibatis/builder/xml/.*.dtd",
|
||||
"org/apache/ibatis/builder/xml/.*.xsd"
|
||||
).forEach(metadata::registerResourceInclude);
|
||||
|
||||
metadata.registerJdkProxy(Executor.class);
|
||||
metadata.registerReflection(Executor.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
|
||||
metadata.registerAllDeclaredMethod(Executor.class, ExecutableMode.INVOKE);
|
||||
|
||||
metadata.registerJdkProxy(StatementHandler.class);
|
||||
metadata.registerReflection(StatementHandler.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
|
||||
metadata.registerAllDeclaredMethod(StatementHandler.class, ExecutableMode.INVOKE);
|
||||
|
||||
metadata.registerReflection(BoundSql.class, MemberCategory.DECLARED_FIELDS, MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS);
|
||||
metadata.registerAllDeclaredMethod(BoundSql.class, ExecutableMode.INVOKE);
|
||||
|
||||
metadata.registerReflection(Configuration.class, MemberCategory.DECLARED_FIELDS);
|
||||
metadata.registerAllDeclaredMethod(Configuration.class, ExecutableMode.INVOKE);
|
||||
|
||||
metadata.registerReflection(JBoss6VFS.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||
metadata.registerReflection(DefaultVFS.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||
|
||||
|
||||
registerMybatisAdapter(context, metadata, XPluginImpl.getAdapterFlex());
|
||||
|
||||
}
|
||||
|
||||
protected void registerMybatisAdapter(AppContext context, RuntimeNativeMetadata metadata, MybatisAdapterDefault bean) {
|
||||
if (bean == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//注册 xml 资源
|
||||
for (String res : bean.getMappers()) {
|
||||
if (res.startsWith(ResourceUtil.TAG_classpath)) {
|
||||
res = res.substring(ResourceUtil.TAG_classpath.length());
|
||||
res = res.replace("**", "*");
|
||||
res = res.replace("*", ".*");
|
||||
metadata.registerResourceInclude(res);
|
||||
}
|
||||
}
|
||||
|
||||
//注册 mapper 代理
|
||||
for (Class<?> clz : bean.getConfiguration().getMapperRegistry().getMappers()) {
|
||||
metadata.registerJdkProxy(clz);
|
||||
metadata.registerReflection(clz, MemberCategory.INTROSPECT_PUBLIC_METHODS);
|
||||
Method[] declaredMethods = clz.getDeclaredMethods();
|
||||
for (Method method : declaredMethods) {
|
||||
MethodWrap methodWrap = context.methodGet(method);
|
||||
NativeMetadataUtils.registerMethodAndParamAndReturnType(metadata, methodWrap);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 entity
|
||||
for (Class<?> clz : bean.getConfiguration().getTypeAliasRegistry().getTypeAliases().values()) {
|
||||
metadata.registerReflection(clz, MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||
metadata.registerDefaultConstructor(clz);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.mybatisflex.solon.integration;
|
||||
|
||||
import org.apache.ibatis.solon.MybatisAdapter;
|
||||
import org.apache.ibatis.solon.MybatisAdapterFactory;
|
||||
import org.noear.solon.core.BeanWrap;
|
||||
import org.noear.solon.core.Props;
|
||||
|
||||
/**
|
||||
* MyBatis-Flex 适配器工厂。
|
||||
*
|
||||
* @author noear
|
||||
* @since 2.2
|
||||
*/
|
||||
public class MybatisAdapterFactoryFlex implements MybatisAdapterFactory {
|
||||
|
||||
@Override
|
||||
public MybatisAdapter create(BeanWrap dsWrap) {
|
||||
return new MybatisAdapterFlex(dsWrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MybatisAdapter create(BeanWrap dsWrap, Props dsProps) {
|
||||
return new MybatisAdapterFlex(dsWrap, dsProps);
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,14 +20,15 @@ import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
|
||||
import com.mybatisflex.core.row.RowMapperInvoker;
|
||||
import com.mybatisflex.solon.mybtais.MybatisAdapterDefault;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.solon.integration.MybatisAdapterDefault;
|
||||
import org.noear.solon.Utils;
|
||||
import org.noear.solon.core.BeanWrap;
|
||||
import org.noear.solon.core.Props;
|
||||
import org.noear.solon.core.VarHolder;
|
||||
import org.noear.solon.core.event.EventBus;
|
||||
import org.noear.solon.core.util.ClassUtil;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@ -41,6 +42,7 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
|
||||
private FlexSqlSessionFactoryBuilder factoryBuilderPlus;
|
||||
private FlexGlobalConfig globalConfig;
|
||||
private RowMapperInvoker rowMapperInvoker;
|
||||
private Class<?> typeAliasesBaseType;
|
||||
|
||||
protected MybatisAdapterFlex(BeanWrap dsWrap) {
|
||||
super(dsWrap);
|
||||
@ -70,6 +72,11 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
|
||||
//for configuration section
|
||||
config = new FlexConfiguration(environment);
|
||||
|
||||
String typeAliasesBaseTypeStr = dsProps.get("typeAliasesSuperType");
|
||||
if (Utils.isNotEmpty(typeAliasesBaseTypeStr)) {
|
||||
typeAliasesBaseType = ClassUtil.loadClass(typeAliasesBaseTypeStr);
|
||||
}
|
||||
|
||||
Props cfgProps = dsProps.getProp("configuration");
|
||||
if (cfgProps.size() > 0) {
|
||||
Utils.injectProperties(config, cfgProps);
|
||||
@ -134,4 +141,29 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
|
||||
varH.setValue(rowMapperInvoker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTypeAliasesType(Class<?> type) {
|
||||
//typeAliasesSuperType
|
||||
if (typeAliasesBaseType == null) {
|
||||
return true;
|
||||
} else {
|
||||
return typeAliasesBaseType.isAssignableFrom(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTypeAliasesKey(String key) {
|
||||
return super.isTypeAliasesKey(key) || key.startsWith("typeAliasesPackage[");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTypeHandlersKey(String key) {
|
||||
return super.isTypeHandlersKey(key) || key.startsWith("typeHandlersPackage[");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMappersKey(String key) {
|
||||
return super.isMappersKey(key) || key.startsWith("mapperLocations[");
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,9 +16,28 @@
|
||||
|
||||
package com.mybatisflex.solon.integration;
|
||||
|
||||
import org.apache.ibatis.solon.integration.MybatisAdapterManager;
|
||||
import com.mybatisflex.annotation.UseDataSource;
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.MybatisFlexBootstrap;
|
||||
import com.mybatisflex.core.datasource.DataSourceKey;
|
||||
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||
import com.mybatisflex.core.row.RowMapperInvoker;
|
||||
import com.mybatisflex.solon.aot.MybatisRuntimeNativeRegistrar;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.aot.RuntimeNativeRegistrar;
|
||||
import org.noear.solon.core.AppContext;
|
||||
import org.noear.solon.core.BeanWrap;
|
||||
import org.noear.solon.core.Plugin;
|
||||
import org.noear.solon.core.Props;
|
||||
import org.noear.solon.core.runtime.NativeDetector;
|
||||
import org.noear.solon.core.util.ClassUtil;
|
||||
import org.noear.solon.core.util.TmplUtil;
|
||||
import org.noear.solon.data.datasource.DsUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 配置 MyBatis-Flex 插件。
|
||||
@ -27,9 +46,105 @@ import org.noear.solon.core.Plugin;
|
||||
* @since 2.2
|
||||
*/
|
||||
public class XPluginImpl implements Plugin {
|
||||
private static final String CONFIG_PREFIX = "mybatisFlex";
|
||||
private static final String CONFIG_DS_PREFIX = "mybatisFlex.datasource";
|
||||
|
||||
private static MybatisAdapterFlex adapterFlex;
|
||||
|
||||
public static MybatisAdapterFlex getAdapterFlex() {
|
||||
return adapterFlex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(AppContext context) throws Throwable {
|
||||
// 此插件的 solon.plugin.priority 会大于 mybatis-solon-plugin 的值
|
||||
MybatisAdapterManager.setAdapterFactory(new MybatisAdapterFactoryFlex());
|
||||
// 注册动态数据源的事务路由
|
||||
//TranManager.routing(FlexDataSource.class, new FlexDataSourceRouting());
|
||||
|
||||
// 订阅数据源
|
||||
context.subWrapsOfType(DataSource.class, bw -> {
|
||||
loadDs(context, bw);
|
||||
});
|
||||
|
||||
// aot
|
||||
if (NativeDetector.isAotRuntime() && ClassUtil.hasClass(() -> RuntimeNativeRegistrar.class)) {
|
||||
context.wrapAndPut(MybatisRuntimeNativeRegistrar.class);
|
||||
}
|
||||
|
||||
|
||||
// 构建 mf 配置的数据源
|
||||
Class<?> dsDefClz = ClassUtil.loadClass("com.zaxxer.hikari.HikariDataSource");
|
||||
Props dsProps = context.cfg().getProp(CONFIG_DS_PREFIX);
|
||||
if (dsProps.size() > 0) {
|
||||
Map<String, DataSource> dsMap = DsUtils.buildDsMap(dsProps, dsDefClz);
|
||||
|
||||
for (Map.Entry<String, DataSource> entry : dsMap.entrySet()) {
|
||||
String dsName = entry.getKey();
|
||||
DataSource ds = entry.getValue();
|
||||
BeanWrap bw = context.wrap(dsName, ds);
|
||||
loadDs(context, bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDs(AppContext context, BeanWrap bw) {
|
||||
boolean isInit = MybatisFlexBootstrap.getInstance().getDataSource() == null;
|
||||
MybatisFlexBootstrap.getInstance().addDataSource(bw.name(), bw.raw());
|
||||
|
||||
if (isInit) {
|
||||
initDo(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void initDo(AppContext context) {
|
||||
BeanWrap dsBw = context.wrap(FlexConsts.NAME, MybatisFlexBootstrap.getInstance().getDataSource(), true);
|
||||
MybatisAdapterFlex dsFlex = new MybatisAdapterFlex(dsBw, context.cfg().getProp(CONFIG_PREFIX));
|
||||
dsFlex.mapperPublish();
|
||||
adapterFlex = dsFlex;
|
||||
|
||||
// 注册到管理器(aot 时会用到)
|
||||
//MybatisAdapterManager.register(dsBw, dsFlex);
|
||||
|
||||
//绑定到容器
|
||||
context.beanInjectorAdd(Inject.class, FlexGlobalConfig.class, ((vh, anno) -> {
|
||||
dsFlex.injectTo(vh);
|
||||
}));
|
||||
|
||||
|
||||
context.beanInjectorAdd(Inject.class, FlexConfiguration.class, ((vh, anno) -> {
|
||||
dsFlex.injectTo(vh);
|
||||
}));
|
||||
|
||||
context.beanInjectorAdd(Inject.class, RowMapperInvoker.class, ((vh, anno) -> {
|
||||
dsFlex.injectTo(vh);
|
||||
}));
|
||||
|
||||
context.beanInjectorAdd(Inject.class, SqlSessionFactory.class, ((vh, anno) -> {
|
||||
dsFlex.injectTo(vh);
|
||||
}));
|
||||
|
||||
context.beanInterceptorAdd(UseDataSource.class, inv -> {
|
||||
UseDataSource anno = inv.getMethodAnnotation(UseDataSource.class);
|
||||
|
||||
if (anno == null) {
|
||||
anno = inv.getTargetAnnotation(UseDataSource.class);
|
||||
}
|
||||
|
||||
if (anno == null) {
|
||||
return inv.invoke();
|
||||
} else {
|
||||
//备份
|
||||
String backup = DataSourceKey.get();
|
||||
|
||||
try {
|
||||
String dsName = TmplUtil.parse(anno.value(), inv);
|
||||
|
||||
DataSourceKey.use(dsName);
|
||||
return inv.invoke();
|
||||
} finally {
|
||||
//还原
|
||||
DataSourceKey.use(backup);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,358 @@
|
||||
package com.mybatisflex.solon.mybtais;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
||||
import org.apache.ibatis.executor.ErrorContext;
|
||||
import org.apache.ibatis.io.Resources;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
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.type.TypeHandler;
|
||||
import org.noear.solon.Solon;
|
||||
import org.noear.solon.Utils;
|
||||
import org.noear.solon.core.BeanWrap;
|
||||
import org.noear.solon.core.LifecycleIndex;
|
||||
import org.noear.solon.core.Props;
|
||||
import org.noear.solon.core.VarHolder;
|
||||
import org.noear.solon.core.event.EventBus;
|
||||
import org.noear.solon.core.util.ResourceUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Mybatis 适配器默认实现
|
||||
*
|
||||
* @author noear
|
||||
* @since 1.1
|
||||
*/
|
||||
public class MybatisAdapterDefault {
|
||||
protected static final Logger log = LoggerFactory.getLogger(MybatisAdapterDefault.class);
|
||||
|
||||
protected final BeanWrap dsWrap;
|
||||
protected final Props dsProps;
|
||||
|
||||
//mapper 注解验证启用?
|
||||
protected final boolean mapperVerifyEnabled;
|
||||
|
||||
protected Configuration config;
|
||||
protected SqlSessionFactory factory;
|
||||
protected List<String> mappers = new ArrayList<>();
|
||||
protected SqlSessionFactoryBuilder factoryBuilder;
|
||||
|
||||
/**
|
||||
* 构建Sql工厂适配器,使用默认的 typeAliases 和 mappers 配置
|
||||
*/
|
||||
protected MybatisAdapterDefault(BeanWrap dsWrap) {
|
||||
this(dsWrap, Solon.cfg().getProp("mybatis"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Sql工厂适配器,使用属性配置
|
||||
*/
|
||||
protected MybatisAdapterDefault(BeanWrap dsWrap, Props dsProps) {
|
||||
this.dsWrap = dsWrap;
|
||||
if (dsProps == null) {
|
||||
this.dsProps = new Props();
|
||||
} else {
|
||||
this.dsProps = dsProps;
|
||||
}
|
||||
|
||||
this.mapperVerifyEnabled = dsProps.getBool("configuration.mapperVerifyEnabled", false);
|
||||
this.factoryBuilder = new SqlSessionFactoryBuilder();
|
||||
|
||||
DataSource dataSource = getDataSource();
|
||||
String dataSourceId = dsWrap.name();
|
||||
if (Utils.isEmpty(dataSourceId)) {
|
||||
dataSourceId = "_main";
|
||||
}
|
||||
|
||||
TransactionFactory tf = new SolonManagedTransactionFactory();
|
||||
Environment environment = new Environment(dataSourceId, tf, dataSource);
|
||||
|
||||
initConfiguration(environment);
|
||||
|
||||
//加载插件(通过Bean)
|
||||
dsWrap.context().lifecycle(LifecycleIndex.PLUGIN_BEAN_USES, () -> {
|
||||
dsWrap.context().beanForeach(bw -> {
|
||||
if (bw.raw() instanceof Interceptor) {
|
||||
config.addInterceptor(bw.raw());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//1.分发事件,推给扩展处理
|
||||
EventBus.publish(config);
|
||||
|
||||
//2.初始化(顺序不能乱)
|
||||
initDo();
|
||||
|
||||
dsWrap.context().getBeanAsync(SqlSessionFactoryBuilder.class, bean -> {
|
||||
factoryBuilder = bean;
|
||||
});
|
||||
}
|
||||
|
||||
public List<String> getMappers() {
|
||||
return mappers;
|
||||
}
|
||||
|
||||
protected DataSource getDataSource() {
|
||||
return dsWrap.raw();
|
||||
}
|
||||
|
||||
protected void initConfiguration(Environment environment) {
|
||||
config = new Configuration(environment);
|
||||
|
||||
//for configuration section
|
||||
Props cfgProps = dsProps.getProp("configuration");
|
||||
if (cfgProps.size() > 0) {
|
||||
Utils.injectProperties(config, cfgProps);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isTypeAliasesType(Class<?> type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isTypeAliasesKey(String key){
|
||||
return key.startsWith("typeAliases[") || key.equals("typeAliases");
|
||||
}
|
||||
|
||||
protected boolean isTypeHandlersKey(String key){
|
||||
return key.startsWith("typeHandlers[") || key.equals("typeHandlers");
|
||||
}
|
||||
|
||||
protected boolean isMappersKey(String key){
|
||||
return key.startsWith("mappers[") || key.equals("mappers");
|
||||
}
|
||||
|
||||
protected void initDo() {
|
||||
//for typeAliases & typeHandlers section
|
||||
dsProps.forEach((k, v) -> {
|
||||
if (k instanceof String && v instanceof String) {
|
||||
String key = (String) k;
|
||||
String valStr = (String) v;
|
||||
|
||||
if (isTypeAliasesKey(key)) {
|
||||
for (String val : valStr.split(",")) {
|
||||
val = val.trim();
|
||||
if (val.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//package || type class,转为类表达式
|
||||
for (Class<?> clz : ResourceUtil.scanClasses(dsWrap.context().getClassLoader(), val)) {
|
||||
if (clz.isInterface() == false) {
|
||||
if (isTypeAliasesType(clz)) {
|
||||
getConfiguration().getTypeAliasRegistry().registerAlias(clz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isTypeHandlersKey(key)) {
|
||||
for (String val : valStr.split(",")) {
|
||||
val = val.trim();
|
||||
if (val.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//package || type class,转为类表达式
|
||||
for (Class<?> clz : ResourceUtil.scanClasses(dsWrap.context().getClassLoader(), val)) {
|
||||
if (TypeHandler.class.isAssignableFrom(clz)) {
|
||||
getConfiguration().getTypeHandlerRegistry().register(clz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//todo: 上面的完成后,才能做下面这个
|
||||
|
||||
//for mappers section
|
||||
dsProps.forEach((k, v) -> {
|
||||
if (k instanceof String && v instanceof String) {
|
||||
String key = (String) k;
|
||||
String valStr = (String) v;
|
||||
|
||||
if (isMappersKey(key)) {
|
||||
for (String val : valStr.split(",")) {
|
||||
val = val.trim();
|
||||
if (val.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mappers.add(val);
|
||||
|
||||
if (ResourceUtil.hasClasspath(val)) {
|
||||
//mapper xml, 新方法,替代旧的 *.xml (基于表达式;更自由,更语义化)
|
||||
for (String uri : ResourceUtil.scanResources(val)) {
|
||||
addMapperByXml(uri);
|
||||
}
|
||||
|
||||
//todo: 兼容提醒:
|
||||
compatibilityTipsOfXml(val);
|
||||
} else {
|
||||
//package || type class,转为类表达式
|
||||
for (Class<?> clz : ResourceUtil.scanClasses(dsWrap.context().getClassLoader(), val)) {
|
||||
if (clz.isInterface()) {
|
||||
if (mapperVerifyEnabled) {
|
||||
if (isMapper(clz)) {
|
||||
getConfiguration().addMapper(clz);
|
||||
}
|
||||
} else {
|
||||
getConfiguration().addMapper(clz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (mappers.size() == 0) {
|
||||
if (Utils.isEmpty(dsWrap.name())) {
|
||||
log.warn("Mybatis: Missing mappers configuration!");
|
||||
} else {
|
||||
log.warn("Mybatis: Missing mappers configuration. name='{}'", dsWrap.name());
|
||||
}
|
||||
//throw new IllegalStateException("Please add the mappers configuration!");
|
||||
} else {
|
||||
//如果有配置,但是没有 mapper 注册成功;说明有问题了
|
||||
if (config.getMapperRegistry().getMappers().size() == 0) {
|
||||
//log.warn("Mybatis: Missing mapper registration, please check the mappers configuration!");
|
||||
if (Utils.isEmpty(dsWrap.name())) {
|
||||
throw new IllegalStateException("Missing mapper registration, please check the mappers configuration!");
|
||||
} else {
|
||||
throw new IllegalStateException("Missing mapper registration, please check the mappers configuration. name='" + dsWrap.name() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//for plugins section
|
||||
List<Interceptor> interceptors = MybatisPluginUtils.resolve(dsProps, "plugins");
|
||||
for (Interceptor itp : interceptors) {
|
||||
getConfiguration().addInterceptor(itp);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isMapper(Class<?> clz) {
|
||||
return clz.isAnnotationPresent(Mapper.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置器
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话工厂
|
||||
*/
|
||||
public SqlSessionFactory getFactory() {
|
||||
if (factory == null) {
|
||||
factory = factoryBuilder.build(getConfiguration());//new SqlSessionFactoryProxy(factoryBuilder.build(config));
|
||||
}
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
Map<Class<?>, Object> mapperCached = new HashMap<>();
|
||||
|
||||
public <T> T getMapper(Class<T> mapperClz) {
|
||||
Object mapper = mapperCached.get(mapperClz);
|
||||
|
||||
if (mapper == null) {
|
||||
synchronized (mapperClz) {
|
||||
mapper = mapperCached.get(mapperClz);
|
||||
if (mapper == null) {
|
||||
MybatisMapperInterceptor handler = new MybatisMapperInterceptor(getFactory(), mapperClz);
|
||||
|
||||
mapper = Proxy.newProxyInstance(
|
||||
mapperClz.getClassLoader(),
|
||||
new Class[]{mapperClz},
|
||||
handler);
|
||||
mapperCached.put(mapperClz, mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (T) mapper;
|
||||
}
|
||||
|
||||
public void injectTo(VarHolder vh) {
|
||||
//@Db("db1") SqlSessionFactory factory;
|
||||
if (SqlSessionFactory.class.isAssignableFrom(vh.getType())) {
|
||||
vh.setValue(this.getFactory());
|
||||
return;
|
||||
}
|
||||
|
||||
//@Db("db1") Configuration cfg;
|
||||
if (Configuration.class.isAssignableFrom(vh.getType())) {
|
||||
vh.setValue(this.getConfiguration());
|
||||
return;
|
||||
}
|
||||
|
||||
//@Db("db1") UserMapper userMapper;
|
||||
if (vh.getType().isInterface()) {
|
||||
Object mapper = this.getMapper(vh.getType());
|
||||
|
||||
vh.setValue(mapper);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected void addMapperByXml(String uri) {
|
||||
try {
|
||||
// resource 配置方式
|
||||
ErrorContext.instance().resource(uri);
|
||||
|
||||
//读取mapper文件
|
||||
InputStream stream = Resources.getResourceAsStream(uri);
|
||||
|
||||
//mapper映射文件都是通过XMLMapperBuilder解析
|
||||
XMLMapperBuilder mapperParser = new XMLMapperBuilder(stream, getConfiguration(), uri, getConfiguration().getSqlFragments());
|
||||
mapperParser.parse();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void compatibilityTipsOfXml(String val) {
|
||||
//todo: 兼容提醒:
|
||||
//if (val.endsWith("*.xml") && val.indexOf("*") == val.indexOf("*.xml")) {
|
||||
//@Deprecated //弃用提示
|
||||
// log.warn("Mybatis-新文件表达式提示:'" + val + "' 不包括深度子目录;如有需要可增加'/**/'段");
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public void mapperPublish() {
|
||||
for (Class<?> clz : getConfiguration().getMapperRegistry().getMappers()) {
|
||||
mapperPublishDo(clz);
|
||||
}
|
||||
}
|
||||
|
||||
private void mapperPublishDo(Class<?> clz) {
|
||||
if (clz != null && clz.isInterface()) {
|
||||
Object mapper = getMapper(clz);
|
||||
|
||||
//进入容器,用于 @Inject 注入
|
||||
dsWrap.context().wrapAndPut(clz, mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.mybatisflex.solon.mybtais;
|
||||
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Mybatis Mapper Interceptor
|
||||
*
|
||||
* @author noear
|
||||
* @since 1.6
|
||||
*/
|
||||
public class MybatisMapperInterceptor implements InvocationHandler {
|
||||
private SqlSessionFactory factory;
|
||||
private Class<?> mapperClz;
|
||||
|
||||
public MybatisMapperInterceptor(SqlSessionFactory factory, Class<?> mapperClz) {
|
||||
this.factory = factory;
|
||||
this.mapperClz = mapperClz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
try (SqlSession session = factory.openSession(true)) {
|
||||
Object mapper = session.getMapper(mapperClz);
|
||||
return method.invoke(mapper, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.mybatisflex.solon.mybtais;
|
||||
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.noear.solon.core.Props;
|
||||
import org.noear.solon.core.util.ClassUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 插件解析工具
|
||||
*
|
||||
* @author noear
|
||||
* @since 1.10
|
||||
*/
|
||||
public class MybatisPluginUtils {
|
||||
/**
|
||||
* 解析
|
||||
*
|
||||
* @param prefix 配置前缀
|
||||
*/
|
||||
public static List<Interceptor> resolve(Props configRoot, String prefix) {
|
||||
List<Interceptor> interceptors = new ArrayList<>();
|
||||
|
||||
int index = 0;
|
||||
while (true) {
|
||||
Props props = configRoot.getProp(prefix + "[" + index + "]");
|
||||
if (props.size() == 0) {
|
||||
break;
|
||||
} else {
|
||||
index++;
|
||||
|
||||
String name = null;
|
||||
for (Map.Entry kv : props.entrySet()) {
|
||||
if (kv.getKey() instanceof String) {
|
||||
String key = (String) kv.getKey();
|
||||
if (key.endsWith(".class")) {
|
||||
name = key.split("\\.")[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
props = props.getProp(name);
|
||||
Interceptor plugin = ClassUtil.tryInstance(props.get("class"));
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Mybatis plugin [" + name + "].class load failed");
|
||||
}
|
||||
props.remove("class");
|
||||
|
||||
plugin.setProperties(props);
|
||||
interceptors.add(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return interceptors;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package com.mybatisflex.solon.mybtais;
|
||||
|
||||
import com.mybatisflex.core.datasource.FlexDataSourceRouting;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.noear.solon.data.tran.TranUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author noear
|
||||
* @since 1.6
|
||||
*/
|
||||
public class SolonManagedTransaction implements Transaction {
|
||||
private DataSource dataSource;
|
||||
private Connection connection;
|
||||
|
||||
public SolonManagedTransaction(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
if (connection == null) {
|
||||
DataSource selected = FlexDataSourceRouting.determineCurrentTarget(dataSource);
|
||||
connection = TranUtils.getConnectionProxy(selected);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
if (connection != null) {
|
||||
connection.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
if (connection != null) {
|
||||
connection.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTimeout() throws SQLException {
|
||||
if (connection != null) {
|
||||
return connection.getNetworkTimeout();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.mybatisflex.solon.mybtais;
|
||||
|
||||
import org.apache.ibatis.session.TransactionIsolationLevel;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.apache.ibatis.transaction.TransactionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author noear
|
||||
* @since 1.6
|
||||
*/
|
||||
public class SolonManagedTransactionFactory implements TransactionFactory {
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction newTransaction(Connection connection) {
|
||||
throw new UnsupportedOperationException("New Solon transactions require a DataSource");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel transactionIsolationLevel, boolean b) {
|
||||
return new SolonManagedTransaction(dataSource);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user