在写一个涉及单表操作较多的系统。
框架组合,spring boot + mybatis plus 。 代码结构,entity -> mapper -> service -> controller
但是要创建非常多的空白 mapper/service/controller 的 java 文件。
maper 是个纯接口的空文件,不想写这么多空文件在代码里,如何做到动态注入“接口”文件?
@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 ![]() @lawler 统一放一个包里(比如 generated ),只通过插件生成,不去碰那个包。眼不见心不烦 |
![]() | ClassLoader 八股文背的那么溜,总算有机会用了 |
![]() | 7 xuanbg 2022-01-24 08:03:53 +08:00 ![]() 我都是只用 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 @alva0 #2 @ztechstack #9 @thetbw #10 一个单体应用,并不是一个接口程序,虽然说都是 from 操作,但更多的还是数据的读取展示。 @jptx #3 有考虑过,但依然还是要生成很多空包。 @EscYezi #5 会增加包体积,增量发布时更是一言难尽,因为要人工 review 后留档发布代码的 class 图片快照。 @xuanbg #7 懒 |
14 lawler OP |
15 makinomura 2022-01-24 10:22:50 +08:00 ![]() 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. 定义一个通用的 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>好像不行,项目组里目前用的是这种实现方式    新增表的时候手动填一个 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 ![]() 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 的方式,注解生成。 |