经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MyBatis » 查看文章
Fluent Mybatis快速入门详细教程
来源:jb51  时间:2021/8/4 17:55:42  对本文有异议

使用fluent mybatis可以不用写具体的xml文件,通过java api可以构造出比较复杂的业务sql语句,做到代码逻辑和sql逻辑的合一。 不再需要在Dao中组装查询或更新操作,在xml或mapper中再组装参数。喜欢的朋友可以阅读这篇文章   http://www.jb51.net/article/218884.htm

对底层数据表关联关系的处理,我们总是绕不开什么一对一,一对多,多对多这里比较烦人的关系。 业界优秀的ORM框架也都给出了自己的答案,简单来说就以下几种方式:

hibernate和JPA对开发基本屏蔽了底层数据的处理,只需要在model层设置数据级联关系即可。但这种设置也往往是噩梦的开始。
mybatis 提供了简单的@One @Many注解,然后编写xml映射关系来提供级联处理。
还有一种就是干脆不依赖框架,直接应用自己掌控。
因为FluentMybatis是基于mybatis上做封装和扩展的,所以这里主要聊聊mybatis处理的方式,以及给出FluentMybatis的解放方案。

那么就可以建以下3张表:

数据字典

  1. CREATE TABLE t_member
  2. (
  3. id bigint(21) unsigned auto_increment primary key COMMENT ‘主键id',
  4. user_name varchar(45) DEFAULT NULL COMMENT ‘名字',
  5. is_girl tinyint(1) DEFAULT 0 COMMENT 0:男孩; 1:女孩',
  6. age int DEFAULT NULL COMMENT ‘年龄',
  7. school varchar(20) DEFAULT NULL COMMENT ‘学校',
  8. gmt_created datetime DEFAULT NULL COMMENT ‘创建时间',
  9. gmt_modified datetime DEFAULT NULL COMMENT ‘更新时间',
  10. is_deleted tinyint(1) DEFAULT 0 COMMENT ‘是否逻辑删除'
  11. ) ENGINE = InnoDB
  12. CHARACTER SET = utf8 COMMENT = ‘成员表:女孩或男孩信息';
  13.  
  14. CREATE TABLE t_member_love
  15. (
  16. id bigint(21) unsigned auto_increment primary key COMMENT ‘主键id',
  17. girl_id bigint(21) NOT NULL COMMENT member表外键',
  18. boy_id bigint(21) NOT NULL COMMENT ‘member表外键',
  19. status varchar(45) DEFAULT NULL COMMENT ‘状态',
  20. gmt_created datetime DEFAULT NULL COMMENT ‘创建时间',
  21. gmt_modified datetime DEFAULT NULL COMMENT ‘更新时间',
  22. is_deleted tinyint(2) DEFAULT 0 COMMENT ‘是否逻辑删除'
  23. ) ENGINE = InnoDB
  24. CHARACTER SET = utf8 COMMENT = ‘成员恋爱关系';
  25.  
  26. CREATE TABLE t_member_favorite
  27. (
  28. id bigint(21) unsigned auto_increment primary key COMMENT ‘主键id',
  29. member_id bigint(21) NOT NULL COMMENT member表外键',
  30. favorite varchar(45) DEFAULT NULL COMMENT ‘爱好: 电影, 爬山, 徒步…',
  31. gmt_created datetime DEFAULT NULL COMMENT ‘创建时间',
  32. gmt_modified datetime DEFAULT NULL COMMENT ‘更新时间',
  33. is_deleted tinyint(2) DEFAULT 0 COMMENT ‘是否逻辑删除'
  34. ) ENGINE = InnoDB
  35. CHARACTER SET = utf8 COMMENT = ‘成员爱好';

添加项目Maven依赖
具体pom.xml文件

代码生成

  1. public class AppEntityGenerator {
  2.  
  3.  
  4. static final String url = jdbc:mysql://localhost:3306/fluent_mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8”;
  5. /**
  6. * 生成代码的package路径
  7. */
  8. static final String basePackage = cn.org.fluent.mybatis.many2many.demo”;
  9.  
  10. public static void main(String[] args) {
  11. FileGenerator.build(Noting.class);
  12. }
  13.  
  14. @Tables(
  15. /** 数据库连接信息 **/
  16. url = url, username = "root", password = "password",
  17. /** Entity类parent package路径 **/
  18. basePack = basePackage,
  19. /** Entity代码源目录 **/
  20. srcDir = "example/many2many_demo/src/main/java",
  21. /** 如果表定义记录创建,记录修改,逻辑删除字段 **/
  22. gmtCreated = "gmt_create", gmtModified = "gmt_modified", logicDeleted = "is_deleted",
  23. /** 需要生成文件的表 ( 表名称:对应的Entity名称 ) **/
  24. tables = @Table(value = {"t_member", "t_member_love", "t_member_favorite"}, tablePrefix = "t_")
  25. )
  26. static class Noting {
  27. }}

这样就生成了3个Entity类: MemberEntity, MemberFavoriteEntity, MemberLoveEntity。

关系分析

现在我们来理一理这里面的关系

一对多: 一个成员可以有多个爱好
多对多: 一个成员可以有多个男女朋友(前任+现任)
一对一: 一个成员只能有一个现任男女朋友
mybatis处理手法
mybatis提供了@One 和 @Many的注解来处理简单(只有主键和外键依赖)的一对一,和一对多的关系 具体到上面的关系,mybatis只能关联查询成员的爱好,对带条件的(不是只通过外键)现任男女朋友的一对一也没有办法处理。

我这里就不具体展开mybatis的配置语法了,感兴趣读者可以看下下面文章:

Mybatis一对多、多对一处理

Mybatis传递多个参数进行SQL查询的用法

MyBatis注解 & 多对一、一对多

MyBatis系列4:一对一,一对多,多对多查询及延迟加载(N+1问题)分析

鉴于mybatis只能处理简单的关联关系,fluent mybatis就没有直接封装mybatis的处理方式,那fluent mybatis是如何处理上述的关联关系的。 我们先从mybatis也可以处理的一对多的爱好列表入手

一对多的爱好列表处理
fluent mybatis要根据MemberEntity自动返回对应的爱好列表,需要下面几个设置:

MemberEntity继承RichEntity基类
在MemberEntity类里面增加方法 findMyFavorite()
给findMyFavorite方法加上注解 @RefMethod
在注解中增加关联关系: “memberId=id”,意思是 MemberFavoriteEntity.memberId等于MemberEntity.id
具体代码片段如下, 所有这些操作都可以通过代码生成,这里手工添加仅仅是为了讲解

  1. public class MemberEntity extends RichEntity implements IEntity {
  2. // …
  3. /**
  4. * 我的爱好列表
  5. *
  6. * @return
  7. */
  8. @RefMethod(“memberId=id”)
  9. public List findMyFavorite() {
  10. return super.loadCache(“findMyFavorite”, MemberEntity.class);
  11. }
  12. }

好了,我们已经建立好通过Member实例查询爱好列表的功能了,重新编译项目 在generated-sources目录下面,会多出一个文件: Refs

  1. /**
  2. *
  3.  
  4. Refs:o - 查询器,更新器工厂类单例引用o - 应用所有Mapper Bean引用o - Entity关联对象延迟加载查询实现@author powered by FluentMybatis
  5. */
  6. public abstract class Refs extends EntityRefQuery {
  7. public List findMyFavoriteOfMemberEntity(MemberEntity entity) {
  8. return memberFavoriteMapper.listEntity(new MemberFavoriteQuery()
  9. .where.memberId().eq(entity.getId())
  10. .end());
  11. }
  12. }

在这个类里面自动生成了一个方法: findMyFavoriteOfMemberEntity, 入参是MemberEntity, 出参是List, 实现里面根据member的id查询了成员的所有爱好。

增加Spring Bean
我们新建一个类: AllRelationQuery (名称根据你的喜好和业务随便取), 继承Refs, 并把AllRelationQuery加入Spring管理即可。

  1. @Service
  2. public class AllRelationQuery extends Refs {
  3. }

老套路,写个测试验证下

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = Application.class)
  3. public class FindMemberFavoriteTest {
  4. @Autowired
  5. private MemberMapper memberMapper;
  6.  
  7. @Before
  8. public void setup() {
  9. // 省略数据准备部分
  10. }
  11.  
  12. @Test
  13. public void findMyFavorite() {
  14. MemberEntity member = memberMapper.findById(1L);
  15. List<MemberFavoriteEntity> favorites = member.findMyFavorite();
  16. System.out.println("爱好项: " + favorites.size());
  17. }
  18.  
  19.  
  20. }

查看控制台log输出

DEBUG - ==> Preparing: SELECT id, …, user_name FROM t_member WHERE id = ?
DEBUG - > Parameters: 1(Long)
DEBUG - < Total: 1
DEBUG - ==> Preparing: SELECT id, …, member_id FROM t_member_favorite WHERE member_id = ?
DEBUG - > Parameters: 1(Long)
DEBUG - < Total: 2
爱好项: 2

如日志所示,Fluent Mybatis按照预期返回了爱好列表。

给一对多关系添点油加点醋
做过业务系统的同学都知道,数据库中业务数据一般会有一个逻辑删除标识,按照上述逻辑查询出来的数据,我们会把已经废弃(逻辑删除掉)的爱好也一并查询出来了,那我们如何只查询出未逻辑删除(is_deleted=0)的爱好列表呢。

如果采用mybatis的方案,那我们只能耸耸肩,摊开双手说: “爱莫能助,你自己写SQL实现吧”, 但fluent mybatis对这类场景的支持的很好,我们只要给@RefMethod注解值加点条件就可以了, MemberFavoriteEntity.memberId=MemberEntity.id并且Favorite的逻辑删除标识和Member表一样,具体定义如下:

  1. public class MemberEntity extends RichEntity implements IEntity {
  2. @RefMethod(“memberId=id && isDeleted=isDeleted”)
  3. public List findMyFavorite() {
  4. return super.loadCache(“findMyFavorite”, MemberEntity.class);
  5. }
  6. }

重新编译项目,观察Refs代码

  1. public abstract class Refs extends EntityRefQuery {
  2. public List findMyFavoriteOfMemberEntity(MemberEntity entity) {
  3. return memberFavoriteMapper.listEntity(new MemberFavoriteQuery()
  4. .where.isDeleted().eq(entity.getIsDeleted())
  5. .and.memberId().eq(entity.getId())
  6. .end());
  7. }
  8. }

查询条件上带上了逻辑删除条件

跑测试,看log

  1. DEBUG - ==> Preparing: SELECT id, …, user_name FROM t_member WHERE id = ?
  2. DEBUG - > Parameters: 1(Long)
  3. DEBUG - < Total: 1
  4. DEBUG - ==> Preparing: SELECT id, …, member_id FROM t_member_favorite
  5. WHERE is_deleted = ?
  6. AND member_id = ?
  7. DEBUG - > Parameters: false(Boolean), 1(Long)
  8. DEBUG - < Total: 2
  9. 爱好项: 2

FluentMybatis轻松处理了多条件关联的一对多关系, 这个在业务中不仅仅限定于逻辑删除, 还可以推广到部署环境标识(deploy_env), 租户关系等条件上,还有只有你业务中才用到的状态相关的关系上。

Fluent Mybatis对多对多关系处理
fluent mybatis可以轻松处理一对一,一对多的简单和多条件的关联关系,但对多对多也没有提供自动化代码生成的处理手段。 因为多对多,本质上涉及到3张表, A表, B表,AB关联表。 但fluent mybatis还是提供了半自动手段,对这类场景进行了支持,比如我们需要MemberEntity中返回所有前任恋人列表。

在MemberEntity中定义方法: exFriends()

  1. public class MemberEntity extends RichEntity implements IEntity {
  2. /**
  3. * 前任男(女)朋友列表
  4. *
  5. * @return
  6. */
  7. @RefMethod
  8. public List findExFriends() {
  9. return super.loadCache(“findExFriends”, MemberEntity.class);
  10. }
  11. }

和上面的自动化的一对多关系有个区别,@RefMethod上没有设置查询条件,我们重新编译项目。 我们观察Refs类,除了刚才的findMyFavoriteOfMemberEntity方法实现外,还多出一个抽象方法: findExFriendsOfMemberEntity

  1. public abstract class Refs extends EntityRefQuery {
  2. /**
  3. * 返回前任男(女)朋友列表
  4. */
  5. public abstract List findExFriendsOfMemberEntity(MemberEntity entity);
  6. }

在动手实现代码前,我们先分析一下混乱的男女朋友关系
在member表上,我们使用了一个性别字段 is_girl来区别是男的还是女的, 在恋爱关系表上,分别有2个外键girl_id, boy_id来标识一对恋人关系。 这样,如果member是女的,要查询所有前任男朋友,那么sql语句就如下:

  1. select * from t_member
  2. where is_deleted=0
  3. and id in (select boy_id from t_memeber_love
  4. where status = ‘前任'
  5. and girl_id = ? – 女孩id
  6. and is_deleted = 0
  7. )

如果member是男的,要查询所有前任女朋友,那么sql语句条件就要倒过来:

  1. select * from t_member
  2. where is_deleted=0
  3. and id in (select girl_id from t_memeber_love
  4. where status = ‘前任'
  5. and boy_id= ? – 男孩id
  6. and is_deleted = 0
  7. )

实现查询前男(女)朋友列表功能
一般来说,为了实现上面的分支查询,在mybatis的xml文件中需要配置 这样的标签代码分支, 或者在java代码中实现 if(…){}else{}的代码逻辑分支。 那我们来看看fluent mybatis时如何实现上述查询的呢?我们就可以在刚才定义的Refs子类上实现findExFriendsOfMemberEntity自己的逻辑。

  1. @Service
  2. public class AllRelationQuery extends Refs {
  3. @Override
  4. public List findExFriendsOfMemberEntity(MemberEntity entity) {
  5. MemberQuery query = new MemberQuery()
  6. .where.isDeleted().isFalse()
  7. .and.id().in(MemberLoveQuery.class, q -> q
  8. .select(entity.getIsGirl() ? boyId.column : girlId.column)
  9. .where.status().eq(“前任”)
  10. .and.isDeleted().isFalse()
  11. .and.girlId().eq(entity.getId(), o -> entity.getIsGirl())
  12. .and.boyId().eq(entity.getId(), o -> !entity.getIsGirl())
  13. .end())
  14. .end();
  15. return memberMapper.listEntity(query);
  16. }
  17. }

写测试看log

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = Application.class)
  3. public class FindExFriendsTest {
  4. @Autowired
  5. private MemberMapper memberMapper;
  6.  
  7. @Test
  8. public void findExBoyFriends() {
  9. MemberEntity member = memberMapper.findById(1L);
  10. System.out.println("是否女孩:" + member.getIsGirl());
  11. List<MemberEntity> boyFriends = member.findExFriends();
  12. System.out.println(boyFriends);
  13. }
  14.  
  15.  
  16. }

控制台日志

  1. DEBUG - ==> Preparing: SELECT id, …, user_name FROM t_member WHERE id = ?
  2. DEBUG - > Parameters: 1(Long)
  3. DEBUG - < Total: 1
  4. 是否女孩:true
  5. DEBUG - ==> Preparing: SELECT id, …, user_name FROM t_member
  6. WHERE is_deleted = ?
  7. AND id IN (SELECT boy_id
  8. FROM t_member_love
  9. WHERE status = ?
  10. AND is_deleted = ?
  11. AND girl_id = ?)
  12. DEBUG - > Parameters: false(Boolean), 前任(String), false(Boolean), 1(Long)
  13. DEBUG - < Total: 1
  14. [MemberEntity(id=2, gmtModified=Sun Nov 08 12:31:57 CST 2020, isDeleted=false, age=null, gmtCreated=null, isGirl=false, school=null, userName=mike)]

如日志所示,在查询前男友列表是,条件会根据Member的是否是女孩进行分支切换,这也是fluent mybatis动态条件强大的地方。

到此这篇关于FluentMybatis快速入门详细教程的文章就介绍到这了,更多相关Fluent Mybatis入门内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号