Skip to content

Commit 8ab8f08

Browse files
committed
- spring Boot 3和myabtis-plus集成并支持 native
1 parent fe1bca2 commit 8ab8f08

File tree

9 files changed

+183
-66
lines changed

9 files changed

+183
-66
lines changed

example-aot-native-mybatis-plus/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ dependencies {
3030
implementation 'org.springframework.boot:spring-boot-starter-web'
3131
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
3232
implementation 'com.baomidou:mybatis-plus:3.5.2'
33+
implementation 'org.mybatis:mybatis:3.5.11'
34+
implementation 'org.mybatis:mybatis-spring:3.0.1'
3335
implementation 'org.postgresql:postgresql:42.5.1'
3436
testImplementation 'org.springframework.boot:spring-boot-starter-test'
3537
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.baomidou.mybatisplus.core.toolkit;
2+
3+
4+
import com.baomidou.mybatisplus.core.metadata.TableInfo;
5+
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
6+
import com.baomidou.mybatisplus.core.toolkit.support.*;
7+
8+
import java.lang.invoke.SerializedLambda;
9+
import java.lang.reflect.Method;
10+
import java.lang.reflect.Proxy;
11+
import java.util.Map;
12+
import java.util.concurrent.ConcurrentHashMap;
13+
14+
import static java.util.Locale.ENGLISH;
15+
16+
/**
17+
* Lambda 解析工具类
18+
*
19+
* @author HCL, MieMie
20+
* @since 2018-05-10
21+
*/
22+
public final class LambdaUtils {
23+
24+
/**
25+
* 字段映射
26+
*/
27+
private static final Map<String, Map<String, ColumnCache>> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();
28+
29+
/**
30+
* 该缓存可能会在任意不定的时间被清除
31+
*
32+
* @param func 需要解析的 lambda 对象
33+
* @param <T> 类型,被调用的 Function 对象的目标类型
34+
* @return 返回解析后的结果
35+
*/
36+
public static <T> LambdaMeta extract(SFunction<T, ?> func) {
37+
System.out.println("~~~LambdaUtils~~~" + func);
38+
// 1. IDEA 调试模式下 lambda 表达式是一个代理
39+
if (func instanceof Proxy) {
40+
System.out.println("stage-1");
41+
return new IdeaProxyLambdaMeta((Proxy) func);
42+
}
43+
// 2. 反射读取
44+
try {
45+
System.out.println("stage-2");
46+
Method method = func.getClass().getDeclaredMethod("writeReplace");
47+
System.out.println("stage-2-1");
48+
ReflectLambdaMeta reflectLambdaMeta = new ReflectLambdaMeta((SerializedLambda) ReflectionKit.setAccessible(method).invoke(func));
49+
System.out.println(reflectLambdaMeta.getImplMethodName());
50+
System.out.println(reflectLambdaMeta.getInstantiatedClass());
51+
return reflectLambdaMeta;
52+
} catch (Throwable e) {
53+
System.out.println("stage-3");
54+
// 3. 反射失败使用序列化的方式读取
55+
return new ShadowLambdaMeta(com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda.extract(func));
56+
}
57+
}
58+
59+
/**
60+
* 格式化 key 将传入的 key 变更为大写格式
61+
*
62+
* <pre>
63+
* Assert.assertEquals("USERID", formatKey("userId"))
64+
* </pre>
65+
*
66+
* @param key key
67+
* @return 大写的 key
68+
*/
69+
public static String formatKey(String key) {
70+
return key.toUpperCase(ENGLISH);
71+
}
72+
73+
/**
74+
* 将传入的表信息加入缓存
75+
*
76+
* @param tableInfo 表信息
77+
*/
78+
public static void installCache(TableInfo tableInfo) {
79+
COLUMN_CACHE_MAP.put(tableInfo.getEntityType().getName(), createColumnCacheMap(tableInfo));
80+
}
81+
82+
/**
83+
* 缓存实体字段 MAP 信息
84+
*
85+
* @param info 表信息
86+
* @return 缓存 map
87+
*/
88+
private static Map<String, ColumnCache> createColumnCacheMap(TableInfo info) {
89+
Map<String, ColumnCache> map;
90+
91+
if (info.havePK()) {
92+
map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size() + 1);
93+
map.put(formatKey(info.getKeyProperty()), new ColumnCache(info.getKeyColumn(), info.getKeySqlSelect()));
94+
} else {
95+
map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size());
96+
}
97+
98+
info.getFieldList().forEach(i ->
99+
map.put(formatKey(i.getProperty()), new ColumnCache(i.getColumn(), i.getSqlSelect(), i.getMapping()))
100+
);
101+
return map;
102+
}
103+
104+
/**
105+
* 获取实体对应字段 MAP
106+
*
107+
* @param clazz 实体类
108+
* @return 缓存 map
109+
*/
110+
public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {
111+
return CollectionUtils.computeIfAbsent(COLUMN_CACHE_MAP, clazz.getName(), key -> {
112+
TableInfo info = TableInfoHelper.getTableInfo(clazz);
113+
return info == null ? null : createColumnCacheMap(info);
114+
});
115+
}
116+
117+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example.demo;
2+
3+
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
4+
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
5+
import com.example.demo.rest.MpController;
6+
import org.graalvm.nativeimage.hosted.Feature;
7+
import org.graalvm.nativeimage.hosted.RuntimeSerialization;
8+
9+
/**
10+
* @author apple
11+
*/
12+
public class RuntimeRegistrationFeature implements Feature {
13+
@Override
14+
public void duringSetup(DuringSetupAccess access) {
15+
System.out.println("duringSetup register MpController.");
16+
RuntimeSerialization.registerLambdaCapturingClass(MpController.class);
17+
RuntimeSerialization.register(SerializedLambda.class, SFunction.class);
18+
}
19+
}

example-aot-native-mybatis-plus/src/main/java/com/example/demo/config/MybatisPlusConfiguration.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
1010
import com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList;
1111
import com.baomidou.mybatisplus.core.config.GlobalConfig;
12+
import com.baomidou.mybatisplus.core.toolkit.support.ReflectLambdaMeta;
1213
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
1314
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
15+
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
1416
import com.baomidou.mybatisplus.extension.service.IService;
1517
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
1618
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
@@ -25,6 +27,7 @@
2527
import org.apache.ibatis.session.SqlSessionFactory;
2628
import org.mybatis.spring.SqlSessionTemplate;
2729
import org.slf4j.Logger;
30+
import org.springframework.aot.hint.ExecutableMode;
2831
import org.springframework.aot.hint.RuntimeHints;
2932
import org.springframework.aot.hint.RuntimeHintsRegistrar;
3033
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
@@ -35,17 +38,19 @@
3538
import org.springframework.core.io.Resource;
3639

3740
import javax.sql.DataSource;
41+
import java.lang.reflect.Method;
42+
import java.util.ArrayList;
3843
import java.util.Properties;
3944

4045
/**
4146
* @author apple
47+
* <a href="https://github.com/quarkiverse/quarkus-mybatis/issues/184">lamda</a>
4248
*/
43-
//TODO https://github.com/quarkiverse/quarkus-mybatis/issues/184 此问题还未解决, 其他方式是ok的.
4449
@Configuration(proxyBeanMethods = false)
45-
@RegisterReflectionForBinding(classes = {Slf4jImpl.class, Logger.class, SerializedLambda.class, SFunction.class,
50+
@RegisterReflectionForBinding(classes = {Slf4jImpl.class, Logger.class, SerializedLambda.class, SFunction.class, User.class, ArrayList.class,
4651
UserMapper.class, IService.class, ServiceImpl.class, SqlSessionTemplate.class, ProxyFactory.class, User.class, NormalSegmentList.class,
47-
XMLLanguageDriver.class, RawLanguageDriver.class, SystemMetaObject.class, OgnlRuntime.class, MybatisXMLLanguageDriver.class,
48-
LambdaQueryWrapper.class, AbstractLambdaWrapper.class, AbstractWrapper.class, ISqlSegment.class, Wrapper.class})
52+
XMLLanguageDriver.class, RawLanguageDriver.class, SystemMetaObject.class, OgnlRuntime.class, MybatisXMLLanguageDriver.class, java.lang.invoke.SerializedLambda.class,
53+
LambdaQueryWrapper.class, LambdaQueryChainWrapper.class, AbstractLambdaWrapper.class, AbstractWrapper.class, ISqlSegment.class, Wrapper.class, ReflectLambdaMeta.class})
4954
@ImportRuntimeHints(MybatisPlusConfiguration.MybatisRuntimeHints.class)
5055
public class MybatisPlusConfiguration {
5156

@@ -90,6 +95,16 @@ static class MybatisRuntimeHints implements RuntimeHintsRegistrar {
9095
@Override
9196
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
9297
hints.proxies().registerJdkProxy(UserMapper.class);
98+
try {
99+
Method nonEmptyOfWhere = Wrapper.class.getMethod("nonEmptyOfWhere");
100+
Method nonEmptyOfEntity = Wrapper.class.getMethod("nonEmptyOfEntity");
101+
Method isEmptyOfWhere = Wrapper.class.getMethod("isEmptyOfWhere");
102+
hints.reflection().registerMethod(nonEmptyOfWhere, ExecutableMode.INVOKE);
103+
hints.reflection().registerMethod(nonEmptyOfEntity, ExecutableMode.INVOKE);
104+
hints.reflection().registerMethod(isEmptyOfWhere, ExecutableMode.INVOKE);
105+
} catch (NoSuchMethodException e) {
106+
throw new RuntimeException(e);
107+
}
93108
hints.resources().registerPattern("mapper/UserMapper.xml");
94109
}
95110
}

example-aot-native-mybatis-plus/src/main/java/com/example/demo/rest/DemoController.java

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -73,60 +73,20 @@ static class DemoControllerRuntimeHints implements RuntimeHintsRegistrar {
7373
@Override
7474
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
7575
hints.reflection().registerConstructor(org.apache.ibatis.builder.CacheRefResolver.class.getConstructors()[0], ExecutableMode.INTROSPECT);
76-
7776
hints.reflection().registerType(org.apache.ibatis.builder.CacheRefResolver.class,
78-
MemberCategory.PUBLIC_FIELDS,
79-
MemberCategory.DECLARED_FIELDS,
80-
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS,
81-
MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
82-
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
83-
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
84-
MemberCategory.INTROSPECT_PUBLIC_METHODS,
85-
MemberCategory.INVOKE_PUBLIC_METHODS,
86-
MemberCategory.INVOKE_DECLARED_METHODS,
87-
MemberCategory.PUBLIC_CLASSES,
88-
MemberCategory.DECLARED_CLASSES,
89-
MemberCategory.INTROSPECT_DECLARED_METHODS);
90-
hints.reflection().registerType(org.apache.ibatis.parsing.XNode.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
91-
hints.reflection().registerType(org.apache.ibatis.builder.annotation.MethodResolver.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
92-
hints.reflection().registerType(org.apache.ibatis.mapping.ResultFlag.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
93-
hints.reflection().registerType(org.apache.ibatis.builder.ResultMapResolver.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
94-
95-
96-
hints.reflection().registerType(org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.class, MemberCategory.PUBLIC_FIELDS,
97-
MemberCategory.DECLARED_FIELDS,
98-
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS,
99-
MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
100-
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
101-
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
102-
MemberCategory.INTROSPECT_PUBLIC_METHODS,
103-
MemberCategory.INVOKE_PUBLIC_METHODS,
104-
MemberCategory.INVOKE_DECLARED_METHODS,
105-
MemberCategory.PUBLIC_CLASSES,
106-
MemberCategory.DECLARED_CLASSES,
107-
MemberCategory.INTROSPECT_DECLARED_METHODS);
108-
109-
hints.reflection().registerType(MapperBuilderAssistant.class, MemberCategory.PUBLIC_FIELDS,
110-
MemberCategory.DECLARED_FIELDS,
111-
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS,
112-
MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
113-
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
114-
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
115-
MemberCategory.INTROSPECT_PUBLIC_METHODS,
116-
MemberCategory.INVOKE_PUBLIC_METHODS,
117-
MemberCategory.INVOKE_DECLARED_METHODS,
118-
MemberCategory.PUBLIC_CLASSES,
119-
MemberCategory.DECLARED_CLASSES,
120-
MemberCategory.INTROSPECT_DECLARED_METHODS);
121-
77+
MemberCategory.values());
78+
hints.reflection().registerType(org.apache.ibatis.parsing.XNode.class, MemberCategory.values());
79+
hints.reflection().registerType(org.apache.ibatis.builder.annotation.MethodResolver.class, MemberCategory.values());
80+
hints.reflection().registerType(org.apache.ibatis.mapping.ResultFlag.class, MemberCategory.values());
81+
hints.reflection().registerType(org.apache.ibatis.builder.ResultMapResolver.class, MemberCategory.values());
82+
hints.reflection().registerType(org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.class, MemberCategory.values());
83+
hints.reflection().registerType(MapperBuilderAssistant.class, MemberCategory.values());
12284

12385
hints.reflection()
12486
.registerConstructor(SimpleHelloService.class.getConstructors()[0], ExecutableMode.INVOKE)
12587
.registerMethod(ReflectionUtils.findMethod(SimpleHelloService.class, "sayHello", String.class), ExecutableMode.INVOKE);
12688

127-
12889
hints.resources().registerPattern("hello.txt");
129-
13090
}
13191

13292
}

example-aot-native-mybatis-plus/src/main/java/com/example/demo/rest/MpController.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
package com.example.demo.rest;
22

3+
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
34
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
45
import com.example.demo.mp.User;
56
import com.example.demo.mp.service.UserService;
67
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
79
import org.springframework.web.bind.annotation.GetMapping;
810
import org.springframework.web.bind.annotation.PostMapping;
911
import org.springframework.web.bind.annotation.RequestParam;
1012
import org.springframework.web.bind.annotation.RestController;
1113

12-
import java.sql.Wrapper;
1314
import java.util.UUID;
1415

1516
/**
1617
* @author apple
1718
*/
19+
@Slf4j
1820
@RestController
1921
@RequiredArgsConstructor
2022
public class MpController {
@@ -37,9 +39,10 @@ public User addUser() {
3739

3840
@GetMapping("/mp/user")
3941
public User getUser(@RequestParam String ssn) {
40-
User user = userService.getOne(
41-
Wrappers.<User>lambdaQuery().eq(User::getSsn, ssn));
42-
return user;
42+
log.info("this class :[{}]", this.getClass().getName());
43+
LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.lambdaQuery();
44+
return userService.getOne(
45+
userLambdaQueryWrapper.eq(User::getSsn, ssn));
4346
}
4447

4548
@GetMapping("/mp/user/wrapper")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Args =--features=com.example.demo.RuntimeRegistrationFeature
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[
2+
{
3+
"name": "java.util.ArrayList",
4+
"methods": [
5+
{
6+
"name": "<init>",
7+
"parameterTypes": []
8+
}
9+
]
10+
}
11+
]

example-aot-native-mybatis-plus/src/main/resources/META-INF/native-image/serialization-config.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)