在写一个涉及单表操作较多的系统。
框架组合,spring boot + mybatis plus 。 代码结构,entity -> mapper -> service -> controller
但是要创建非常多的空白 mapper/service/controller 的 java 文件。
mapper 是个纯接口的空文件,不想写这么多空文件在代码里,如何做到动态注入“接口”文件?
@Mapper
public interface EntityMapper
}
甚至,service ,和 Iservice ,也希望通过实体动态生成,直接映射出来接口。
或者有其他方案?可以尽可能的专注业务功能。
1
geligaoli 2022-01-23 23:32:01 +08:00
那你得按照 mybatis 的方式,建立 Mapper 的代理对象.实际反而更麻烦. mybatisplus 有很好的代码生成器干嘛不用.
|
2
alva0 2022-01-23 23:52:33 +08:00 via Android
Graphql 可能满足你的要求
|
3
jptx 2022-01-24 00:02:16 +08:00
service 之类的可以省掉的,直接用 ActiveRecord 模式即可,也可以顺着往下看 SimpleQuery 工具类,但是这两个都没法省下 mapper 。想省 mapper 的话还得另外想办法。
https://baomidou.com/pages/49cc81/#activerecord-%E6%A8%A1%E5%BC%8F |
5
EscYezi 2022-01-24 03:32:46 +08:00 via iPhone 1
@lawler 统一放一个包里(比如 generated ),只通过插件生成,不去碰那个包。眼不见心不烦
|
6
sagaxu 2022-01-24 06:25:12 +08:00 via Android
ClassLoader 八股文背的那么溜,总算有机会用了
|
7
xuanbg 2022-01-24 08:03:53 +08:00 1
我都是只用 mybatis ,从来不用 plus 。个人认为楼主也可以抛弃 mybatis plus ,手写 sql 它不香吗?
|
9
zliea 2022-01-24 08:57:38 +08:00
Graphql+1
BaseMapper ??? |
10
thetbw 2022-01-24 09:04:14 +08:00
直接前端传 sql 执行吧,我就见过这种😅
|
11
ic2y 2022-01-24 09:20:06 +08:00
封装一个通用级的动态 mapper , 支持动态传递表名、字段、where order 等。
|
12
Suaxi 2022-01-24 09:34:54 +08:00
写个通用 BaseMapper ,枚举里加一个[表名].class 字段,用的时候传具体的表名,反射出对应表的 IService 就可以了
|
13
lawler OP |
14
lawler OP |
15
makinomura 2022-01-24 10:22:50 +08:00 1
1. 自定义注解处理器编译时自动生成接口文件
2. asm 运行时动态生成 class |
16
lawler OP @makinomura #15
都做过了。看我 14 楼的恢复,注入 bean 时机翻了很多资料,没找到的。。 1 ,2 步之后得到 cls(接口类),然后注册 bean 。 BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) SpringUtil.getBeanFactory(); RootBeanDefinition bean = new RootBeanDefinition(cls); beanFactory.registerBeanDefinition(className, bean); 问题是,在注册依赖 mapper 时报错。注册依赖改为 lazy ,可以不报错,但是 bean 是接口类,不能实例化为 bean 。lazy 首次加载时就报错了。 接下来,考虑通过 mybatisplus 的类,自动实现生成 mapper 接口的实现类,于是有了下边的代码。 MybatisConfiguration mmr=new MybatisConfiguration(); mmr.addMapper(cls); // 这个方法是通过接口类,实现实现类动态生成并加载的。经测试无效。 所以,思路应该没错,或许是时机或者方法没找对。 |
17
wolfie 2022-01-24 10:53:29 +08:00 1
1. 定义一个通用的 BaseService 、BaseMapper 。
2. 根据 DO 动态创建一堆 BaseService 3. 重写 com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#currentModelClass 4. 通用 BaseService 方法内自定义环绕,搭配动态表名 https://baomidou.com/pages/2a45ff/#dynamictablenameinnerinterceptor |
18
lawler OP @wolfie #17 这个文档我看过,是一种思路,但是没有尝试,一来要做大量的改造工作,二来,看到参数是 map 不利于维护。而且跟我实际想要实现的效果不太一样。
本意是,spring 容器可以通过类型推导加载 bean 。如通过 @Autowired 注入 List<User>、List<Account>.. List 和 BaseMapper 是一样的接口类。 我只需要, @Autowired BaseMaper<User>、BaseMaper<Account>就可以拿到对应动态生成的实现类。 换句话说,我理想中的效果是 BaseMaper<T>/Service<T>/Controller <T>,T 是任意表对象,就可以实现,一套 MVC 控制模板。 |
19
micean 2022-01-24 11:16:58 +08:00
用 jdbctemplate
自己实现 resultset 的 handler 就可以了 |
20
Suaxi 2022-01-24 11:51:17 +08:00
@lawler 直接 BaseMaper<T>/Service<T>/Controller <T>好像不行,项目组里目前用的是这种实现方式
![]( https://s6.jpg.cm/2022/01/24/LpUXnT.png) ![]( https://s6.jpg.cm/2022/01/24/LpUctE.png) ![]( https://s6.jpg.cm/2022/01/24/LpU8j6.png) 新增表的时候手动填一个 xxxDao ,枚举里再加上对应新增的表 |
21
monkeyWie 2022-01-24 12:02:38 +08:00
@lawler #18 应该可以实现,但是不能按 type 注入,然后你得找到 mybatis-plus mapper 的实现,批量注册 bean 就行了
|
22
makinomura 2022-01-24 12:03:51 +08:00
@lawler BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 这个里面注册 beanDefinition 即可,注意要使用 MapperFactoryBean
|
23
aguesuka 2022-01-24 17:09:22 +08:00
你是不是想只要有实体类 Entity, 就可以 @Autowired 注入 BaseMapper<Entity> 然后统一使用 LambdaQueryWrapper?
这样的话有两个步骤, 根据 Entity.class 生成 EntityWapper implements BaseMapper<Entity>, 将 EntityWapper 注入到 Spring. 现在的进展如何了? |
24
lawler OP @makinomura #22 尝试很久,搞不会了,XY 问题太多。麻烦大佬再指点一下。
1 ,BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry - 注册过程中,我需要写大量逻辑判断,来确认哪些是数据库实体。 - 或者,我需要一个自定义注解来过滤上个问题? 2 ,MapperFactoryBean 该如何使用? - new 一个不可能,因为有上下文环境。 - 不 new 的时候,他还在第 1 步中,需要我写逻辑判断处理摘出来。再使用他? - 鉴于以上两个问题,我不知道怎么使用他。 |
25
wolfie 2022-01-25 14:34:18 +08:00 1
https://github.com/wolfiesonfire/dynamic-mapper
使用 byte buddy 运行时创建 mapper + service 。 但是 ServiceImpl 的 baseMapper, Autowired 有点问题。 |
26
lawler OP |
27
keshawnvan 2022-01-25 16:49:01 +08:00
看下 tkMybatis 提供的 ActiveRecord 模式
|
28
makinomura 2022-01-26 10:37:31 +08:00
@lawler #24 我简单写了个 demo
|
29
makinomura 2022-01-26 10:38:16 +08:00
public class DomainScanner extends ClassPathBeanDefinitionScanner {
public DomainScanner(BeanDefinitionRegistry registry) { super(registry, false); } @Override protected boolean isCandidateComponent(MetadataReader metadataReader) { return true; } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan( basePackages); for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanDefinitionHolder .getBeanDefinition(); String beanClassName = beanDefinition.getBeanClassName(); Class<?> entityClazz = null; try { entityClazz = Class.forName(beanClassName); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(beanClassName); } DynamicMapperCreator dynamicMapperCreator = new DynamicMapperCreator(); Class<?> mapperClazz = dynamicMapperCreator .getOrCreateMapperClazz(entityClazz); beanDefinition.setBeanClass(MapperFactoryBean.class); ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); constructorArgumentValues.addIndexedArgumentValue(0, mapperClazz); beanDefinition .setConstructorArgumentValues(constructorArgumentValues); beanDefinition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference("sqlSessionFactory")); } return beanDefinitionHolders; } } public class MapperRegister implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry( BeanDefinitionRegistry registry) throws BeansException { new DomainScanner(registry).scan("umoo.wang.domain"); } @Override public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { } } |
30
Chinsung 2022-01-26 11:28:02 +08:00
可以用 java agent 的方式,去扫描你自定义某个包下的所有 do 类,然后通过字节码框架,比如 bytebuddy 这种,生成所有的 mapper 类
|
32
lawler OP 回复一下说下现状。
#25 #29 方案都试过,可以是可以。但还是存在我强调的加载时机问题。 主要问题是,当代码中存在,@PostConstruct 时,因未被编译并注册,所以找不到依赖。 目前已经放弃了这个想法。但计划空闲的时候,参考 lombok 的方式,注解生成。 |