博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一款基于Mybatis的编译期SQL生成器
阅读量:5944 次
发布时间:2019-06-19

本文共 8137 字,大约阅读时间需要 27 分钟。

介绍

TgDao是一款基于Mybatis的编译期SQL生成器,利用注解来表达SQL,能根据你的方法签名生成对应的Mapper.xml文件。 它能减少你日常开发中大量简单SQL的编写,由于它只是生成Mapper.xml文件,因此对于复杂的查询场景, 你同样可以自己编写来完成一些工具所无法生成的SQL。

@Table(name = "T_User")public class User {    @Id("id")    private int id;    private String username;    private int age;}复制代码

上面的model定义了模型和数据库表的关系,看到下面这些方法的签名,聪明的你肯定能猜出每个方法的sql吧,这就是这个库要做的工作。

@DaoGen(model = User.class)public interface UserDao {    @Select    @OrderBy("id desc")    List
queryUser(@Condition(criterion = Criterions.EQUAL, column = "username") String name, @Condition(criterion = Criterions.GREATER, attach = Attach.OR) int age, @Limit int limit, @OffSet int offset); @Select List
queryUser2(@Condition(criterion = Criterions.GREATER, column = "age") int min, @Condition(criterion = Criterions.LESS, column = "age") int max); @Select List
queryUser3(@Condition(criterion = Criterions.EQUAL, column = "username") String name, @Condition(attach = Attach.OR, column = "id", criterion = Criterions.IN) String[] ids); @Insert(useGeneratedKeys = true, keyProperty = "id") int insert(User user); @BatchInsert(columns = "username,age") int batchInsert(List
users); @Update @ModelConditions({ @ModelCondition(field = "id") }) int update(User user); @Delete int delete(@Condition(criterion = Criterions.GREATER, column = "age") int min, @Condition(criterion = Criterions.LESS, column = "age") int max);}复制代码

项目地址


文档

引入如下依赖:

com.github.twogoods
tgdao-core
0.1.3
复制代码

Table与Model关联

@Table记录数据表的名字 @Id记录主键信息 @Column映射了表字段和属性的关系,如果表字段和类属性同名,那么可以省略这个注解 @Ingore忽略这个类属性,没有哪个表字段与它关联

@Table(name = "T_User")public class User {    @Id("id")    private int id;    private String username;    private String password;    private int age;    @Column("old_address")    private String oldAddress;    @Column("now_address")    private String nowAddress;    private int state;    @Column("created_at")    private Timestamp createdAt;    @Column("updated_at")    private Timestamp updatedAt;    @Ignore    private String remrk;复制代码

查询

@Select@OrderBy("id desc")List
queryUser(@Condition(criterion = Criterions.EQUAL, column = "username") String name, @Condition(criterion = Criterions.GREATER, attach = Attach.OR) int age, @Condition(column = "id", criterion = Criterions.IN) String[] ids, @Limit int limit, @OffSet int offset);复制代码
@Select
  • columns:默认 select *可以配置columns("username,age")选择部分字段;
  • SqlMode:有两个选择,SqlMode.SELECTIVE 和 SqlMode.COMMON,区别是selective会检查查询条件的字段是否为null来实现动态的查询, 即<if test="name != null">username = #{name}</if>
@Condition
  • criterion:查询条件,=,<,>,in等,具体见Criterions
  • column:与表字段的对应,若与字段名相同可不配置
  • attach:连接 and,or, 默认是and
  • test:selective下的判断表达式,即<if test="username != null">里的test属性

@Limit@OffSet为分页字段。 方法的参数不加任何注解一样会被当做查询条件,如下面两个函数效果是一样的:

@Select()List
queryUser(Integer age);@Select()List
queryUser(@Condition(criterion = Criterions.EQUAL, column = "age") Integer age);复制代码

查询Model

上面的例子在查询条件比较多时方法参数会比较多,我们可以把查询条件封装到一个类里,使用@ModelConditions来注解查询条件,注意被@ModelConditions只能有一个参数。

@Select@Page@ModelConditions({       @ModelCondition(field = "username", criterion = Criterions.EQUAL),       @ModelCondition(field = "minAge", column = "age", criterion = Criterions.GREATER),       @ModelCondition(field = "maxAge", column = "age", criterion = Criterions.LESS),       @ModelCondition(field = "ids", column = "id", criterion = Criterions.IN),       @ModelCondition(field = "idArr", column = "id", criterion = Criterions.IN, paramType = InType.ARRAY)})List
queryUser5(UserSearch userSearch);复制代码
@ModelCondition
  • field:必填,查询条件中类对应的属性
  • column:对应的表字段
  • paramType:in 查询下才需要配置,数组为array,List为collection类型
  • test:selective下的判断表达式,即<if test="username != null">里的test属性

@Page只能用在ModelConditions下的查询,并且方法参数的那个类应该有offsetlimit这两个属性。

注:

@Select(columns = "username,age")List
queryUser(Integer age);@Select(columns = "username,age")List
queryUser2param(Integer age, String username);
复制代码

两个函数生成的sql如上,@Select的属性SqlMode默认是Selective,所以两个都有条件判断,但是这里第一个函数的sql, Mybatis不支持,执行会报错,类似no age getter in java.lang.Interger,Mybatis会把这唯一的一个参数当做对象来取里面的值。 解决方法:函数签名里强加@Param()注解,或者@Select里使用sqlMode = SqlMode.COMMON去掉生成sql里的if判断。 这个问题只会在方法只有一个参数的情况下发生,第二个函数生成的sql是ok的。

分页

查询参数里@Limit@OffSet或查询model里@Page的分页功能都比较原始,TgDao只是一款SQL生成器而已,因此你可以使用各种插件, 或者与其他框架集成。对于分页,可以无缝与整合。

@SelectList
queryUser2(@Condition(criterion = Criterions.GREATER, column = "age") int min, @Condition(criterion = Criterions.LESS, column = "age") int max);@Testpublic void testQueryUser2() throws Exception { PageHelper.offsetPage(1, 10); List
users = mapper.queryUser2(12, 30); PageInfo page = new PageInfo<>(users); System.out.println(page.getTotal()); Assert.assertTrue(page.getList().size() > 0);}复制代码

插入

@Insert(useGeneratedKeys = true, keyProperty = "id")//获取自增idint insert(User user);@BatchInsert(columns = "username,age")//插入的列int batchInsert(List
users);复制代码

BatchInsert强烈建议写columns,因为生成的语句并不会过滤null字段,数据库中插入null易报错。


更新

@Update(columns = "username,age")//选择更新某几个列@ModelConditions({       @ModelCondition(field = "id")})int update(User user);复制代码

删除

@Deleteint delete(@Condition(criterion = Criterions.GREATER, column = "age") int min,          @Condition(criterion = Criterions.LESS, column = "age") int max);@Delete@ModelConditions({       @ModelCondition(attach = Attach.AND, field = "minAge", column = "age", criterion = Criterions.GREATER),       @ModelCondition(attach = Attach.AND, field = "maxAge", column = "age", criterion = Criterions.LESS)})int delete2(UserSearch userSearch);复制代码

selective

@Select@Count@Update@Delete都有selective这个属性,这个属性有两个值,分别是SqlMode.COMMONSqlMode.SELECTIVE。 它们的区别在下面这段生成的xml里显示的很清楚,SqlMode.SELECTIVE引入了Mybatis的动态SQL能力。

复制代码

@Select@Count默认的selective属性是SqlMode.SELECTIVE,这样查询语句可以充分利用Mybatis的动态SQL能力。 而@Update@Delete默认是SqlMode.COMMON,这样做的原因是:selective模式下如果参数全是null会使得where语句里没有任何条件, 最终变成全表的更新和删除,这是一个极其危险的动作。所以@Update@Delete慎用SqlMode.SELECTIVE模式。

@Params

在介绍这个注解时要先介绍一下Mybatis自己的@Param注解,@Param注解在方法的参数上,给参数定义了一个名字, 这样可以在xml的sql里使用这个名字来取得参数所对应的值。如下:

List
queryUser(@Param("name") String name);
复制代码

明明参数就叫name,为什么还要@Param注解一个名字name呢?这是因为Java编译完,会丢掉参数名,以至于运行期mybatis不知道这个参数叫什么,所以需要注解一个名字。 在运行时看到mybatis报错如:Parameter 'XXX' not found. Available parameters are... 这就是没有这个注解导致的问题。 但是在Java8里我们已经可以通过给javac 添加-parameters参数来保留参数名字信息,这样mybatis会利用这个信息,这样就不需要加@Param注解了。 maven可以通过如下方式设置:

org.apache.maven.plugins
maven-compiler-plugin
3.1
-parameters
复制代码

然而有一种情况-parameters也无能为力,List<User> queryUser4(List ids);当参数是collection或者数组类型时,mybatis依旧无法认出ids这个参数,只认collectionarray。 而@Params注解是Mybatis自身注解@Param-parameters外的另外一种解决方案。@Params可以注解在类和方法上, 被它注解的类和方法会在编译期自动给所有方法参数加上@Param注解,它借鉴了lombok的方式在编译期修改抽象语法树从而改变编译生成的字节码文件。

@Select(columns = "username,age")    @Params    List
queryUser(Integer age, String username); //编译后 List
queryUser(@Param("age") Integer var1, @Param("username") String var2);复制代码

更多请看


说明

  • 编译生成的XML文件与Mapper接口在同一个包下
  • 只支持Java8和MySql
  • 修改了源代码中方法的定义或者model里和数据表的映射关系,发现编译出来的xml却没有改变,这是增量编译的原因。生成一个xml同时需要model和mapper interface两个部分, 如果你只修改了其中一个的代码,那么另一个未修改的代码编译器就不做处理,这样这一次编译就无法得到全部的信息,所以TgDao无法生成最新版本的xml。 解决方法是每次mvn clean compile先清除一下编译目录,更好的方案正在寻找...

转载地址:http://twkxx.baihongyu.com/

你可能感兴趣的文章
FB正在大规模重构React Native,预计今年发布
查看>>
从0到1:腾讯Yoo视频底层页推荐系统实践
查看>>
推荐10个CI/CD工具,用于云平台集成交付
查看>>
云平台宕机引发的系列思考,企业如何自救?
查看>>
Java EE跟JCP说再见
查看>>
整洁代码之道——重构
查看>>
Oracle加入CNCF,发布Kubernetes on Oracle Linux以及Terraform Kubernetes Cloud Installer
查看>>
Scrum指南更新:Ken Schwaber、Jeff Sutherland访谈
查看>>
在瑞士最大银行驱动创新
查看>>
CRI Shimv2:一种 Kubernetes 集成容器运行时的新思路
查看>>
机器人操作系统来到Windows
查看>>
通过规模化Scrum创造最新技术的打印机
查看>>
时序数据库DolphinDB和TimescaleDB 性能对比测试报告
查看>>
准备好了?测试人员迟早会被要求测试包含区块链技术的解决方案
查看>>
用户故事 | 刷算法面试题的4种思考方式
查看>>
Visual Studio 2017 15.9 Previews扩展C++调试功能
查看>>
宜人贷CTO段念:透明与面向目标是管理理念的核心
查看>>
理解HTTPS
查看>>
linux环境下apache配置虚拟站点
查看>>
ACM — 辗转相除法(Euclidean algorithm)求最大公因数(GCD)
查看>>