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
|
```xml
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
@ -10,47 +12,38 @@
|
|||||||
数据扩展插件,为 Solon Data 提供基于 mybatis-flex([代码仓库](https://gitee.com/mybatis-flex/mybatis-flex))的框架适配,以提供ORM支持。
|
数据扩展插件,为 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...
|
| Mapper.class | 注入 Mapper。例:`@Inject UserMapper userMapper` |
|
||||||
* 强调带 name 的 DataSource Bean
|
| FlexConfiguration | 注入 FlexConfiguration,一般仅用于配置。例:`@Inject FlexConfiguration db1Cfg` |
|
||||||
* 强调使用 @Db("name") 的数据源注解
|
| FlexGlobalConfig | 注入 FlexGlobalConfig,一般仅用于配置。例:`@Inject FlexGlobalConfig db1Gc` |
|
||||||
|
| SqlSessionFactory | 注入 SqlSessionFactory。例:`@Inject SqlSessionFactory db1` (不推荐直接使用) |
|
||||||
|
| RowMapperInvoker | 注入 RowMapperInvoker。例:`@Inject RowMapperInvoker rowMapper` |
|
||||||
@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` |
|
|
||||||
|
|
||||||
|
|
||||||
#### 3、数据源配置
|
#### 3、数据源配置
|
||||||
|
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
# 配置数据源
|
# 配置数据源(或者使用 solon.dataSources 配置数据源,效果一样)
|
||||||
demo.db1:
|
mybatis-flex.datasource:
|
||||||
schema: rock
|
db1:
|
||||||
jdbcUrl: jdbc:mysql://localhost:3306/rock?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
|
jdbcUrl: jdbc:mysql://localhost:3306/rock?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
username: root
|
username: root
|
||||||
password: 123456
|
password: 123456
|
||||||
|
|
||||||
# 配置数据源对应的 mybatis 信息(要与 DataSource bean 的名字对上)
|
# 配置数据源对应的 mybatis 信息(要与 DataSource bean 的名字对上)
|
||||||
mybatis.db1:
|
mybatis-flex:
|
||||||
typeAliases: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
type-aliases-package: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
||||||
- "demo4021.model"
|
- "demo4021.model"
|
||||||
- "demo4021.model.*" #这个表达式同上效果
|
- "demo4021.model.*" #这个表达式同上效果
|
||||||
typeHandlers: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
type-handlers-package: #支持包名 或 类名(大写开头 或 *)//支持 ** 或 * 占位符
|
||||||
- "demo4021.dso.mybaits.handler"
|
- "demo4021.dso.mybaits.handler"
|
||||||
- "demo4021.dso.mybaits.handler.*" #这个表达式同上效果
|
- "demo4021.dso.mybaits.handler.*" #这个表达式同上效果
|
||||||
mappers: #支持包名 或 类名(大写开头 或 *)或 xml(.xml结尾)//支持 ** 或 * 占位符
|
mapper-locations: #支持包名 或 类名(大写开头 或 *)或 xml(.xml结尾)//支持 ** 或 * 占位符
|
||||||
- "demo4021.**.mapper"
|
- "demo4021.**.mapper"
|
||||||
- "demo4021.**.mapper.*" #这个表达式同上效果
|
- "demo4021.**.mapper.*" #这个表达式同上效果
|
||||||
- "classpath:demo4035/**/mapper.xml"
|
- "classpath:demo4035/**/mapper.xml"
|
||||||
@ -58,7 +51,7 @@ mybatis.db1:
|
|||||||
configuration: #扩展配置(要与 FlexConfiguration 类的属性一一对应)
|
configuration: #扩展配置(要与 FlexConfiguration 类的属性一一对应)
|
||||||
cacheEnabled: false
|
cacheEnabled: false
|
||||||
mapUnderscoreToCamelCase: true
|
mapUnderscoreToCamelCase: true
|
||||||
globalConfig: #全局配置(要与 FlexGlobalConfig 类的属性一一对应)//只是示例,别照抄
|
global-config: #全局配置(要与 FlexGlobalConfig 类的属性一一对应)//只是示例,别照抄
|
||||||
printBanner: false
|
printBanner: false
|
||||||
keyConfig:
|
keyConfig:
|
||||||
keyType: "Generator"
|
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 配置注意事项:
|
||||||
|
|
||||||
* 通过 mapper 类包名配置。 xml 与 mapper 需同包同名
|
* 通过 mapper 类包名配置。 xml 与 mapper 需同包同名
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
mybatis.db1.mappers: "demo4035.dso.mapper"
|
mybatis-flex.mapper-locations: "demo4035.dso.mapper"
|
||||||
```
|
```
|
||||||
|
|
||||||
* 通过 xml 目录进行配置。xml 可以固定在一个资源目录下
|
* 通过 xml 目录进行配置。xml 可以固定在一个资源目录下
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
mybatis.db1.mappers: "classpath:mybatis/db1/*.xml"
|
mybatis-flex.mapper-locations: "classpath:mybatis/db1/*.xml"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### 4、代码应用
|
#### 4、代码应用
|
||||||
|
|
||||||
```java
|
```java
|
||||||
//配置数据源
|
//配置 mf (如果配置不能满足需求,可以进一步代助代码)
|
||||||
@Configuration
|
@Configuration
|
||||||
public class Config {
|
public class Config {
|
||||||
//此下的 db1 与 mybatis.db1 将对应在起来 //可以用 @Db("db1") 注入mapper
|
@Bean
|
||||||
//typed=true,表示默认数据源。@Db 可不带名字注入
|
public void ormConfig(@Inject FlexConfiguration cfg,
|
||||||
@Bean(value = "db1", typed = true)
|
@Inject FlexGlobalConfig globalConfig) {
|
||||||
public DataSource db1(@Inject("${demo.db1}") HikariDataSource ds) {
|
|
||||||
return ds;
|
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{
|
public class AppService {
|
||||||
//可用 @Db 或 @Db("db1") 注入
|
@Inject
|
||||||
@Db
|
|
||||||
AppMapper appMapper; //xml sql mapper
|
AppMapper appMapper; //xml sql mapper
|
||||||
|
|
||||||
//可用 @Db 或 @Db("db1")
|
@Inject
|
||||||
@Db
|
|
||||||
BaseMapper<App> appBaseMapper; //base mapper
|
BaseMapper<App> appBaseMapper; //base mapper
|
||||||
|
|
||||||
public void test(){
|
public void test0() {
|
||||||
App app1 = appMapper.getAppById(12);
|
App app1 = appMapper.getAppById(12);
|
||||||
App app2 = appBaseMapper.selectOneById(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>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.noear</groupId>
|
<groupId>org.noear</groupId>
|
||||||
<artifactId>mybatis-solon-plugin</artifactId>
|
<artifactId>solon-data</artifactId>
|
||||||
<version>${solon.version}</version>
|
<version>${solon.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@ -30,6 +30,13 @@
|
|||||||
<artifactId>mybatis-flex-core</artifactId>
|
<artifactId>mybatis-flex-core</artifactId>
|
||||||
<version>${mybatis-flex.version}</version>
|
<version>${mybatis-flex.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-aot</artifactId>
|
||||||
|
<version>${solon.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</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.FlexConfiguration;
|
||||||
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
|
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
|
||||||
import com.mybatisflex.core.row.RowMapperInvoker;
|
import com.mybatisflex.core.row.RowMapperInvoker;
|
||||||
|
import com.mybatisflex.solon.mybtais.MybatisAdapterDefault;
|
||||||
import org.apache.ibatis.mapping.Environment;
|
import org.apache.ibatis.mapping.Environment;
|
||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
import org.apache.ibatis.solon.integration.MybatisAdapterDefault;
|
|
||||||
import org.noear.solon.Utils;
|
import org.noear.solon.Utils;
|
||||||
import org.noear.solon.core.BeanWrap;
|
import org.noear.solon.core.BeanWrap;
|
||||||
import org.noear.solon.core.Props;
|
import org.noear.solon.core.Props;
|
||||||
import org.noear.solon.core.VarHolder;
|
import org.noear.solon.core.VarHolder;
|
||||||
import org.noear.solon.core.event.EventBus;
|
import org.noear.solon.core.event.EventBus;
|
||||||
|
import org.noear.solon.core.util.ClassUtil;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
|
|||||||
private FlexSqlSessionFactoryBuilder factoryBuilderPlus;
|
private FlexSqlSessionFactoryBuilder factoryBuilderPlus;
|
||||||
private FlexGlobalConfig globalConfig;
|
private FlexGlobalConfig globalConfig;
|
||||||
private RowMapperInvoker rowMapperInvoker;
|
private RowMapperInvoker rowMapperInvoker;
|
||||||
|
private Class<?> typeAliasesBaseType;
|
||||||
|
|
||||||
protected MybatisAdapterFlex(BeanWrap dsWrap) {
|
protected MybatisAdapterFlex(BeanWrap dsWrap) {
|
||||||
super(dsWrap);
|
super(dsWrap);
|
||||||
@ -70,6 +72,11 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
|
|||||||
//for configuration section
|
//for configuration section
|
||||||
config = new FlexConfiguration(environment);
|
config = new FlexConfiguration(environment);
|
||||||
|
|
||||||
|
String typeAliasesBaseTypeStr = dsProps.get("typeAliasesSuperType");
|
||||||
|
if (Utils.isNotEmpty(typeAliasesBaseTypeStr)) {
|
||||||
|
typeAliasesBaseType = ClassUtil.loadClass(typeAliasesBaseTypeStr);
|
||||||
|
}
|
||||||
|
|
||||||
Props cfgProps = dsProps.getProp("configuration");
|
Props cfgProps = dsProps.getProp("configuration");
|
||||||
if (cfgProps.size() > 0) {
|
if (cfgProps.size() > 0) {
|
||||||
Utils.injectProperties(config, cfgProps);
|
Utils.injectProperties(config, cfgProps);
|
||||||
@ -134,4 +141,29 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
|
|||||||
varH.setValue(rowMapperInvoker);
|
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;
|
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.AppContext;
|
||||||
|
import org.noear.solon.core.BeanWrap;
|
||||||
import org.noear.solon.core.Plugin;
|
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 插件。
|
* 配置 MyBatis-Flex 插件。
|
||||||
@ -27,9 +46,105 @@ import org.noear.solon.core.Plugin;
|
|||||||
* @since 2.2
|
* @since 2.2
|
||||||
*/
|
*/
|
||||||
public class XPluginImpl implements Plugin {
|
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
|
@Override
|
||||||
public void start(AppContext context) throws Throwable {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
pom.xml
2
pom.xml
@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
<spring.version>5.3.27</spring.version>
|
<spring.version>5.3.27</spring.version>
|
||||||
<spring-boot.version>2.7.11</spring-boot.version>
|
<spring-boot.version>2.7.11</spring-boot.version>
|
||||||
<solon.version>2.9.3</solon.version>
|
<solon.version>3.0.1</solon.version>
|
||||||
|
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
<testcontainers.version>1.19.3</testcontainers.version>
|
<testcontainers.version>1.19.3</testcontainers.version>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user