使用fluent mybatis可以不用写具体的xml文件,通过java api可以构造出比较复杂的业务sql语句,做到代码逻辑和sql逻辑的合一。 不再需要在dao中组装查询或更新操作,在xml或mapper中再组装参数。

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

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

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

数据字典

create table t_member
(
id bigint(21) unsigned auto_increment primary key comment ‘主键id',
user_name varchar(45) default null comment ‘名字',
is_girl tinyint(1) default 0 comment ‘0:男孩; 1:女孩',
age int default null comment ‘年龄',
school varchar(20) default null comment ‘学校',
gmt_created datetime default null comment ‘创建时间',
gmt_modified datetime default null comment ‘更新时间',
is_deleted tinyint(1) default 0 comment ‘是否逻辑删除'
) engine = innodb
character set = utf8 comment = ‘成员表:女孩或男孩信息';

create table t_member_love
(
id bigint(21) unsigned auto_increment primary key comment ‘主键id',
girl_id bigint(21) not null comment ‘member表外键',
boy_id bigint(21) not null comment ‘member表外键',
status varchar(45) default null comment ‘状态',
gmt_created datetime default null comment ‘创建时间',
gmt_modified datetime default null comment ‘更新时间',
is_deleted tinyint(2) default 0 comment ‘是否逻辑删除'
) engine = innodb
character set = utf8 comment = ‘成员恋爱关系';

create table t_member_favorite
(
id bigint(21) unsigned auto_increment primary key comment ‘主键id',
member_id bigint(21) not null comment ‘member表外键',
favorite varchar(45) default null comment ‘爱好: 电影, 爬山, 徒步…',
gmt_created datetime default null comment ‘创建时间',
gmt_modified datetime default null comment ‘更新时间',
is_deleted tinyint(2) default 0 comment ‘是否逻辑删除'
) engine = innodb
character set = utf8 comment = ‘成员爱好';

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

代码生成

public class appentitygenerator {


static final string url = “jdbc:mysql://localhost:3306/fluent_mybatis_demo?usessl=false&useunicode=true&characterencoding=utf-8”;
/**
* 生成代码的package路径
*/
static final string basepackage = “cn.org.fluent.mybatis.many2many.demo”;

public static void main(string[] args) {
    filegenerator.build(noting.class);
}

@tables(
    /** 数据库连接信息 **/
    url = url, username = "root", password = "password",
    /** entity类parent package路径 **/
    basepack = basepackage,
    /** entity代码源目录 **/
    srcdir = "example/many2many_demo/src/main/java",
    /** 如果表定义记录创建,记录修改,逻辑删除字段 **/
    gmtcreated = "gmt_create", gmtmodified = "gmt_modified", logicdeleted = "is_deleted",
    /** 需要生成文件的表 ( 表名称:对应的entity名称 ) **/
    tables = @table(value = {"t_member", "t_member_love", "t_member_favorite"}, tableprefix = "t_")
)
static class noting {
}}

这样就生成了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
具体代码片段如下, 所有这些操作都可以通过代码生成,这里手工添加仅仅是为了讲解

public class memberentity extends richentity implements ientity {
// …
/**
* 我的爱好列表
*
* @return
*/
@refmethod(“memberid=id”)
public list findmyfavorite() {
return super.loadcache(“findmyfavorite”, memberentity.class);
}
}

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

/**
*

refs:o - 查询器,更新器工厂类单例引用o - 应用所有mapper bean引用o - entity关联对象延迟加载查询实现@author powered by fluentmybatis
*/
public abstract class refs extends entityrefquery {
public list findmyfavoriteofmemberentity(memberentity entity) {
return memberfavoritemapper.listentity(new memberfavoritequery()
.where.memberid().eq(entity.getid())
.end());
}
}

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

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

@service
public class allrelationquery extends refs {
}

老套路,写个测试验证下

@runwith(springrunner.class)
@springboottest(classes = application.class)
public class findmemberfavoritetest {
@autowired
private membermapper membermapper;

@before
public void setup() {
    // 省略数据准备部分
}

@test
public void findmyfavorite() {
    memberentity member = membermapper.findbyid(1l);
    list<memberfavoriteentity> favorites = member.findmyfavorite();
    system.out.println("爱好项: " + favorites.size());
}


}

查看控制台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表一样,具体定义如下:

public class memberentity extends richentity implements ientity {
@refmethod(“memberid=id && isdeleted=isdeleted”)
public list findmyfavorite() {
return super.loadcache(“findmyfavorite”, memberentity.class);
}
}

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

public abstract class refs extends entityrefquery {
public list findmyfavoriteofmemberentity(memberentity entity) {
return memberfavoritemapper.listentity(new memberfavoritequery()
.where.isdeleted().eq(entity.getisdeleted())
.and.memberid().eq(entity.getid())
.end());
}
}

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

跑测试,看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 is_deleted = ?
and member_id = ?
debug - > parameters: false(boolean), 1(long)
debug - < total: 2
爱好项: 2

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

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

在memberentity中定义方法: exfriends()

public class memberentity extends richentity implements ientity {
/**
* 前任男(女)朋友列表
*
* @return
*/
@refmethod
public list findexfriends() {
return super.loadcache(“findexfriends”, memberentity.class);
}
}

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

public abstract class refs extends entityrefquery {
/**
* 返回前任男(女)朋友列表
*/
public abstract list findexfriendsofmemberentity(memberentity entity);
}

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

select * from t_member
where is_deleted=0
and id in (select boy_id from t_memeber_love
where status = ‘前任'
and girl_id = ? – 女孩id
and is_deleted = 0
)

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

select * from t_member
where is_deleted=0
and id in (select girl_id from t_memeber_love
where status = ‘前任'
and boy_id= ? – 男孩id
and is_deleted = 0
)

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

@service
public class allrelationquery extends refs {
@override
public list findexfriendsofmemberentity(memberentity entity) {
memberquery query = new memberquery()
.where.isdeleted().isfalse()
.and.id().in(memberlovequery.class, q -> q
.select(entity.getisgirl() ? boyid.column : girlid.column)
.where.status().eq(“前任”)
.and.isdeleted().isfalse()
.and.girlid().eq(entity.getid(), o -> entity.getisgirl())
.and.boyid().eq(entity.getid(), o -> !entity.getisgirl())
.end())
.end();
return membermapper.listentity(query);
}
}

写测试看log

@runwith(springrunner.class)
@springboottest(classes = application.class)
public class findexfriendstest {
@autowired
private membermapper membermapper;

@test
public void findexboyfriends() {
    memberentity member = membermapper.findbyid(1l);
    system.out.println("是否女孩:" + member.getisgirl());
    list<memberentity> boyfriends = member.findexfriends();
    system.out.println(boyfriends);
}


}

控制台日志

debug - ==> preparing: select id, …, user_name from t_member where id = ?
debug - > parameters: 1(long)
debug - < total: 1
是否女孩:true
debug - ==> preparing: select id, …, user_name from t_member
where is_deleted = ?
and id in (select boy_id
from t_member_love
where status = ?
and is_deleted = ?
and girl_id = ?)
debug - > parameters: false(boolean), 前任(string), false(boolean), 1(long)
debug - < total: 1
[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入门内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!