网站首页 > 博客文章 正文
——从Mybatis到Hibernate、Spring data JPA,再到QueryDSL,用过了这些框架后,对各种框架的毛病已不胜其烦。在看到Mybatis Plus这样的框架后,突发奇想,不如自己使用mybatis 定制一个数据库持久层框架,实现SQL语句的自动生成。
一个Mybatis增强工具包。新增了5种SQL语句自动生成注解:ExecuteInsert, ExecuteSelect, ExecuteCount, ExecuteUpdate, ExecuteDelete。 实现了增删改查语句自动生成。
特点
- 自动按需关联表。自动收集实体类中声明的关联表、以及方法签名中的参数注解声明的关联表,根据查询参数引用到的关联表、及查询结果视图中 用到的关联表,自动对用到的表进行Join。支持左连接,内连接,右连接。
- 查询结果支持视图,类似于Jackson Json中的JsonView,可在ExecuteSelect注解中声明查询结果的视图。生成的语句会根据查询结果视图中的字段引用 的表,自动进行Join。如果视图中包含1对1、1对多的关联属性,自动通过二次查询设置属性值。
- 支持拦截器对Mapper中方法的参数进行修改,以及添加额外的过滤条件和插入值(或更新值)。
- 支持默认过滤条件、默认插入值(或更新值)、默认排序。具体参考ExecuteInsert等语句自动生成注解。
实现思路
MybatisGenerator在监听到ContextRefreshedEvent事件后,在所有已注入的扩展了BaseMapper或EmptyMapper的Mapper中查找使用ExecuteInsert等 注解标注的方法,生成动态SqlSource,注册MappedStatement。
快速开始
插入
ExecuteInsert方法严格限定只能声明1个参数,可以是单个实例,也可以是集合类型(List或数组)。参数类型可以是实体类型、Map类型及任意的java bean类型。生成插入语句时 根据实体类字段名查找参数中的同名字段进行插入。
插入实体类实例:
@ExecuteInsert int insert(Entity entity);
插入数组:
@ExecuteInsert int insertArray(T[] array);
插入列表(使用默认插入值):
@ExecuteInsert(columnValue = {@ColumnValue(field = "updateTime", placeholder = "now()"), @ColumnValue(field = "createTime", placeholder = "now()")}) int insertList(List<User> users);
查询
使用查询类:
@ExecuteSelect List<T> selectList(Object query);
查询条数
@ExecuteCount long selectCount(Object query);
使用Map查询条件
@ExecuteSelect List<User> searchByCondition(@FilterParam Map<String, Object> condition, @Limit int limit);
查询1条:
@ExecuteSelect(selectOne = true) T selectOne(Object query);
使用ID查询:
@ExecuteSelect(view = "detail") User getById(@IdParam long id); @ExecuteSelect(view = "detail") List<User> getByIds(@IdParam List<Long> ids);
使用默认参数:
@ExecuteSelect(filter = {@Filter(field = "type", placeholder = "1", conflict = ConflictAction.discard)}) List<User> selectByType(@FilterParam(field = "type") Integer type);
使用关联表字段查询:
@ExecuteSelect(orderBy = "id desc") List<User> search(@FilterParam(field = "type") int type, @FilterParam(table = "car", column = "license", type = FilterType.preMatch, join = @Join(table = "car", joinColumns = @JoinColumn(column = "driver_id", referTable = "user", referColumn = "id"))) String license);
更新
根据ID更新:
@ExecuteUpdate(columnValue = @ColumnValue(field = "updateTime", placeholder = "now()")) int changeName(@IdParam long id, @ValueParam("name") String name); @ExecuteUpdate(columnValue = @ColumnValue(field = "updateTime", placeholder = "now()")) int changeNames(@IdParam List<Long> id, @ValueParam("name") String name);
使用查询类:
@ExecuteUpdate int updateByCondition(@FilterParam Object condition, @ValueParam Object val);
删除
使用查询参数:
@ExecuteDelete(filter = @Filter(field = "type", placeholder = "1")) int deleteByCompanyName(@FilterParam(field = "companyName", type = FilterType.preMatch) String companyName);
使用查询类:
@ExecuteDelete int deleteByCondition(Object condition);
使用ID:
@ExecuteDelete int deleteByIds(@IdParam List<IdType> keys);
使用说明
实体类
类似于Spring Data JPA,BaseMapper、EmptyMapper的类型参数声明了实体类类型,主键类型。多字段组合主键的实体类,只需将多个主键字段使用@Id标注即可,Mapper中的主键类型参数 可以是包含的所有主键字段的Java bean或Map<String, Object>类型。实体类中的字段可以引用其它表中的列,引用的表必须在@Entity类中声明。
实体类可以包含1对1、1对多字段,分别使用HasOne,HasMany标注,支持映射结果视图参数。HasOne支持排序参数及强制返回1条。HasMany支持排序参数及指定返回条数。
查询类
可以将所有支持的过滤字段使用一个查询类来声明。过滤字段可以引用其它表,如果过滤字段引用了其它表,且实体类@Entity注解没有声明该表,必须在查询类使用Query标注声明该表关联条件。 过滤字段可以使用@Filter注解指定过滤条件运算符(=, >, <, in等),也可指定对应的实体类字段或数据表中列名,或者声明忽略本字段。查询类可以声明分页及排序的字段或get方法。 分页使用@Limit、@Offset注解,排序使用@Sort注解。
Mapper方法参数
Mapper方法参数可以使用@FilterParam, @IdParam, @ValueParam进行标注。FilterParam标明查询参数或查询bean。IdParam标明id过滤参数,当实体类使用组合主键时,参数应当是 java bean或Map类型,如果参数类型是List或数组,自动使用in过滤类型。ValueParam标明参数是需要更新的字段值,可以是单个字段(需要标明field属性或column属性)或java bean。
默认值
ExecuteSelect、ExecuteCount、ExecuteUpdate、ExecuteDelete可以指定默认过滤条件,ExecuteInsert、ExecuteUpdate可以指定指定数据表列的默认插入(更新)值。 ExecuteSelect可以指定默认排序。
拦截器
ExecuteInsert, ExecuteSelect, ExecuteCount, ExecuteUpdate, ExecuteDelete均可指定拦截器。拦截器必须是当前Mapper接口的静态方法名或默认方法名,接收1个参数,参数类型为: InterceptorContext。
同时,可以指定一个全局的拦截器,拦截器必须是GeneratorInterceptor的实例。全局拦截器只有方法没有指定拦截器时有效,不过可以通过静态方法MybatisGenerator.intercept来调用全局拦截器。 全局拦截器必须通过静态方法MybatisGenerator.setDefaultInterceptor(GeneratorInterceptor defaultInterceptor)指定。
排序参数
- 使用实体类字段:query.setSort("createTime desc, id asc")
- 使用实体类字段表达式:query.setSort("$[companyName] desc")
- 使用Table表达式:query.setSort("$(car).create_time desc")
placeholder
@Filter, @FilterParam, @ColumnValue, @ValueParam均可指定placeholder。placeholder用于过滤条件运算符右侧或插入值或更新值。可以有以下形式:
- 使用字面常量。如:1,(true),'男'。如果字面常量是1个有效的java标识符或属性,应当使用圆括号包围,防止自动加上#{},如前面(true),如果没有圆括号,会解析成#{true}。
- 引用当前参数或参数字段。如:#{$@} + INTERVAL 1 DAY。$@自动替换成当前参数或参数字段。
- 引用实体类字段。如:$[amount] + 1。$[amount]自动替换为实体类中amount字段对应的表、列名。解析后形式类似于:t0.amount + 1。
- 引用表。如:$(product).amount + 1。$(product)自动替换为表名。解析后形式类似于:t0.amount + 1。
HasOne和HasMany
HasOne和HasMany字段必须是关联的实体类类型或实体类的集合类(数组或List)。HasOne和HasMany标注必须指定过滤条件(filter属性)。 且过滤条件必须指定placeholder,placeholder可以是当前实体类的其它字段,也可以是常量。如果placeholder是一个表达式,引用实体类中的字段时, 使用$[fieldName]格式或$(tableName).columnName或$(tableName.tableAlias).columnName。表名及表别名组合必须是当前实体类对 应的表或@Entity注解声明的关联表。
在实现多态表自Join时,需要表别名,通常表别名保持为空字符串即可。如user表中包含员工和老板时,查询王老板的员工时,需要用到自Join。
过滤字段的重复处理
方法参数、默认值、拦截器三者都可以生成过滤条件。这些过滤条件引用的数据表字段重复时,根据ConflictAction类型按照以下规则进行:
- 如果新加入的过滤条件是coexist类型,则移除同字段的discard类型的过滤条件,加入新过滤条件。
- 如果新加入的过滤条件是override类型,则移除同字段的discard和override类型的过滤条件,加入新过滤条件。
- 如果新加入的过滤条件是discard类型,如果同字段的过滤条件存在则忽略,否则加入。
方法参数、默认值、拦截器处理顺序
- 拦截器对方法参数进行处理,拦截器可以修改方法参数。
- 解析方法参数,生成过滤条件列表、数据表列值对列表。方法参数或参数字段必须不为null才加入过滤条件列表,如果指定ignoreOnZero为true时,必须不为0时才加入过滤条件列表。
- 将方法注解中的默认过滤条件、默认列值对合并入第2步中的列表。
- 将拦截器中的额外的过滤条件、列值对合并入第2步中的列表。
猜你喜欢
- 2024-10-24 持久层框架JPA与Mybatis该如何选型
- 2024-10-24 Elasticsearch 6.0.0官方参考指南翻译
- 2024-10-24 推荐一个高效美观易用的服务器运维工具
- 2024-10-24 Java 操作之RestHighLevelClient查询详解
- 2024-10-24 如何在 Elasticsearch 上应用机器学习排序插件
- 2024-10-24 「开源资讯」Apache Solr 8.6.0 发布,Java 全文搜索服务器
- 2024-10-24 最轻量级的Kubernetes云原生日志框架Loki
- 2024-10-24 ElasticSearch学习系列 - (3) Python操作es
- 2024-10-24 全功能orm工具sorms 1.0.10 发布,合使用Spring,Spring boot用户
- 2024-10-24 ES6.0.0官方参考指南翻译~入门指南~探索数据
你 发表评论:
欢迎- 05-30springboot 集成redisson 以及分布式锁的使用
- 05-30去哪儿技术面:10亿数据如何最快速插入MySQL?
- 05-30redis介绍
- 05-30redission YYDS
- 05-30手把手教你springboot集成mybatis
- 05-30mybatis根据表逆向自动化生成代码:自动生成实体类、mapper文件
- 05-30越来越大的微信小程序
- 05-30SpringBoot之数据访问——访问SQL数据库!
- 403℃手把手教程「JavaWeb」优雅的SpringMvc+Mybatis整合之路
- 401℃用AI Agent治理微服务的复杂性问题|QCon
- 386℃初次使用IntelliJ IDEA新建Maven项目
- 382℃IT全明星|IntelliJ IDEA学习笔记(四、idea中怎么创建maven项目)
- 378℃Maven技术方案最全手册(mavena)
- 377℃IntelliJ IDEA 2018版本和2022版本创建 Maven 项目对比
- 374℃InfoQ 2024 年趋势报告:架构篇(infoq+2024+年趋势报告:架构篇分析)
- 371℃从头搭建 IntelliJ IDEA 环境(intellij idea建包)
- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)