
在写一个涉及单表操作较多的系统。
框架组合,spring boot + mybatis plus 。 代码结构,entity -> mapper -> service -> controller
但是要创建非常多的空白 mapper/service/controller 的 java 文件。
mapper 是个纯接口的空文件,不想写这么多空文件在代码里,如何做到动态注入“接口”文件?
@Mapper public interface EntityMapper
}
甚至,service ,和 Iservice ,也希望通过实体动态生成,直接映射出来接口。
或者有其他方案?可以尽可能的专注业务功能。
1 geligaoli Jan 23, 2022 那你得按照 mybatis 的方式,建立 Mapper 的代理对象.实际反而更麻烦. mybatisplus 有很好的代码生成器干嘛不用. |
2 alva0 Jan 23, 2022 via Android Graphql 可能满足你的要求 |
3 jptx Jan 24, 2022 service 之类的可以省掉的,直接用 ActiveRecord 模式即可,也可以顺着往下看 SimpleQuery 工具类,但是这两个都没法省下 mapper 。想省 mapper 的话还得另外想办法。 https://baomidou.com/pages/49cc81/#activerecord-%E6%A8%A1%E5%BC%8F |
6 sagaxu Jan 24, 2022 via Android ClassLoader 八股文背的那么溜,总算有机会用了 |
7 xuanbg Jan 24, 2022 我都是只用 mybatis ,从来不用 plus 。个人认为楼主也可以抛弃 mybatis plus ,手写 sql 它不香吗? |
9 zliea Jan 24, 2022 Graphql+1 BaseMapper ??? |
10 thetbw Jan 24, 2022 直接前端传 sql 执行吧,我就见过这种 |
11 ic2y Jan 24, 2022 封装一个通用级的动态 mapper , 支持动态传递表名、字段、where order 等。 |
12 Suaxi Jan 24, 2022 写个通用 BaseMapper ,枚举里加一个[表名].class 字段,用的时候传具体的表名,反射出对应表的 IService 就可以了 |
13 lawler OP |
14 lawler OP |
15 makinomura Jan 24, 2022 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 Jan 24, 2022 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 Jan 24, 2022 用 jdbctemplate 自己实现 resultset 的 handler 就可以了 |
20 Suaxi Jan 24, 2022 @lawler 直接 BaseMaper<T>/Service<T>/Controller <T>好像不行,项目组里目前用的是这种实现方式    新增表的时候手动填一个 xxxDao ,枚举里再加上对应新增的表 |
21 monkeyWie Jan 24, 2022 @lawler #18 应该可以实现,但是不能按 type 注入,然后你得找到 mybatis-plus mapper 的实现,批量注册 bean 就行了 |
22 makinomura Jan 24, 2022 @lawler BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 这个里面注册 beanDefinition 即可,注意要使用 MapperFactoryBean |
23 aguesuka Jan 24, 2022 你是不是想只要有实体类 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 Jan 25, 2022 https://github.com/wolfiesonfire/dynamic-mapper 使用 byte buddy 运行时创建 mapper + service 。 但是 ServiceImpl 的 baseMapper, Autowired 有点问题。 |
26 lawler OP |
27 keshawnvan Jan 25, 2022 看下 tkMybatis 提供的 ActiveRecord 模式 |
28 makinomura Jan 26, 2022 @lawler #24 我简单写了个 demo |
29 makinomura Jan 26, 2022 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 Jan 26, 2022 可以用 java agent 的方式,去扫描你自定义某个包下的所有 do 类,然后通过字节码框架,比如 bytebuddy 这种,生成所有的 mapper 类 |
32 lawler OP 回复一下说下现状。 #25 #29 方案都试过,可以是可以。但还是存在我强调的加载时机问题。 主要问题是,当代码中存在,@PostConstruct 时,因未被编译并注册,所以找不到依赖。 目前已经放弃了这个想法。但计划空闲的时候,参考 lombok 的方式,注解生成。 |