mybatis-动态sql动态bean实例
这里是一个针对开源项目若依框架的使用我们动态sql的改造过程的描述,但是其中主要还是介绍了如何使用本框架来实现sql的动态查询和动态bean,如果您使用的是自身的系统,这里对外部框架的改造过程,也可以作为参考。
首先,我们以一个功能的改造,来简单说明:
上图为该项目的通知管理,初始为一个单表的操作,显示的字段为序号、公告标题、公告类型、状态、创建人、创建时间。
下图为SysNotice的所有属性;(同时在baseEntity中有4个公共属性:createBy、createTime、updateBy、updateTime)
这是该项目SysNoticeController.java钟list方法的原写法:根据查询通知信息,是最简单的功能
public TableDataInfo list(SysNotice notice)
{
startPage();
List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list);
}
-
首先我们想与用户表关联,并在列表区显示该用户所属部门的id,为了测试,我们先随便用noticeId与用户表关联(发现他们id值有相同的,这里只是为了测试),ok,写法如下:
public TableDataInfo list(SysNotice notice)
{
startPage();
List<SysNotice> list = noticeService.selectNoticeList(notice);
//使用SysNotice表的noticeId字段与sysUser表的userId进行关联
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(notice);//将构建好的userJtb放入notice这个查询实体中
userJtb.putSelect("deptId");//关联完成后,增加一个sysUser表的select字段。
List<SysNotice> list = noticeService.findList(notice);//调用findList方法查询数据
TableDataInfo tdi=getDataTable(list);//装载到分页信息中
return tdi;
}
我们可以看到打印后的sql:
拼接参数后sql____________:[com.ruoyi.system.mapper.SysNoticeMainMapper.findList]:
SELECT a.notice_id AS "noticeId", a.notice_title AS "noticeTitle", a.notice_type AS "noticeType", a.notice_content AS "noticeContent", a.status AS "status"
, a.create_by AS "createBy", a.create_time AS "createTime", a.update_by AS "updateBy", a.update_time AS "updateTime", a.remark AS "remark"
, sysuser57.dept_id AS "deptId"
FROM sys_notice a
LEFT JOIN sys_user sysuser57 ON notice_id = sysuser57.user_id
-
然后这时我们想增加一个查询条件deptId,需要注意的是deptId实际在SysNotice中是不存在该属性的,他是在SysUser表中,下面详细介绍:
首先我在notice.html页面的查询条件区,增加一个查询条件deptId字段,效果如下
这时的request会增加 一个叫做deptId的字段 ,如果输入了值,那么就会有内容,因此我们需要调用notice=notice.initDynaMap(request);将request中的值自动copy到notice中,这时我们看到的notice的效果如下:
我们可以看到,多了几个动态属性,其中一个是deptId
接下来详细代码:
public TableDataInfo list(SysNotice notice,HttpServletRequest request)
{
startPage();
notice=notice.initDynaMap(request);//将ruquest中全部字段copy到notice中,如果notice中不存在该属性,则会创建一个虚拟字段,并将值赋值过去。
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(notice);
userJtb.putSelect("deptId","deptId");
userJtb.putWhere("deptId");//为user表增加一个deptId的查询条件,注意,如果deptId输入了值,那么在sql中就会自动形成deputId=?
List<SysNotice> list = noticeService.findList(notice);
TableDataInfo tdi=getDataTable(list);
return tdi;
}
我们可以看到打印后的sql:
SELECT a.notice_id AS "noticeId", a.notice_title AS "noticeTitle", a.notice_type AS "noticeType", a.notice_content AS "noticeContent", a.status AS "status"
, a.create_by AS "createBy", a.create_time AS "createTime", a.update_by AS "updateBy", a.update_time AS "updateTime", a.remark AS "remark"
, sysuser31.dept_id AS "deptId"
FROM sys_notice a
LEFT JOIN sys_user sysuser31 ON notice_id = sysuser31.user_id
WHERE sysuser31.dept_id = '103'
-
接下来,我想为这个查询额外增加一个查询条件:
比如说:dept_Id is not null,当然你可以写的很复杂的,最终会追加到where条件后面
写法如下:
public TableDataInfo list(SysNotice notice,HttpServletRequest request)
{
startPage();
notice=notice.initDynaMap(request);
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(notice);
userJtb.putSelect("deptId","deptId");
userJtb.putWhere("deptId");
notice.appendWhereCondSql("dept_Id is not null");//为当前sql增加一个额外自定义的任意的查询条件,当然这里可以根据业务写的很复杂
List<SysNotice> list = noticeService.findList(notice);
TableDataInfo tdi=getDataTable(list);
return tdi;
}
我们可以看到打印后的sql
SELECT a.notice_id AS "noticeId", a.notice_title AS "noticeTitle", a.notice_type AS "noticeType", a.notice_content AS "noticeContent", a.status AS "status"
, a.create_by AS "createBy", a.create_time AS "createTime", a.update_by AS "updateBy", a.update_time AS "updateTime", a.remark AS "remark"
, sysuser89.dept_id AS "deptId"
FROM sys_notice a
LEFT JOIN sys_user sysuser89 ON notice_id = sysuser89.user_id
WHERE sysuser89.dept_id = '103'
AND dept_Id IS NOT NULL
-
接下来,我想为查询结果,即每条记录增加一个字段,然后在回调中我可以自由的处理该字段的内容,写法如下:
public TableDataInfo list(SysNotice notice,HttpServletRequest request)
{
startPage();
notice=notice.initDynaMap(request);
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(notice);
userJtb.putSelect("deptId","deptId");
userJtb.putWhere("deptId");
notice.appendWhereCondSql("dept_Id is not null");
notice.putAppendFieldS("someField").putFieldCallBack("dealSomeField");//增加一个字段叫someField,并为该字段增加一个回调方法dealSomeField,在回调方法中处理字段内容
List<SysNotice> list = noticeService.findList(notice);
TableDataInfo tdi=getDataTable(list);
return tdi;
}
public String dealSomeField(Object obj,ResultSet rs) {
//在这里可以根据rs中不同字段进行组合运算、再次查询数据库处理等等,根据实际业务需要,最终的处理结果会体现在someField
return "hahah";
}
同时我在页面notice.html的数据显示区增加上这个字段someField:
形成的页面效果如下:
而我们的查询结果可以看到多了这个字段:
-
接下来,我想增加一个带有函数的select字段
写法如下:
public TableDataInfo list(SysNotice notice,HttpServletRequest request)
{
startPage();
notice=notice.initDynaMap(request);
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(notice);
userJtb.putSelect("deptId","deptId");
userJtb.putWhere("deptId");
notice.putAddSelectField("concat(noticeId,noticeTitle) ","ctest");//在原有sql的select中新加一个自定义的select字段
notice.appendWhereCondSql("dept_Id is not null");
notice.putAppendFieldS("someField").putFieldCallBack("dealSomeField");
List<SysNotice> list = noticeService.findList(notice);
TableDataInfo tdi=getDataTable(list);
return tdi;
}
最终我们看到,查询的结果集中增加了这个ctest字段,是将id和title拼接成一个字符串。
----------------------------------------------------------------------------------------------------------------------
-
接下来,我想做一个分组的函数,同样是这两张表的关联,按照deptId分组,并计算数量值,我们重新来写个方法演示:
写法如下:
public void testGroupby() {
SysNotice groupby=new SysNotice();
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(groupby) ;
groupby.putGroupBySelectField("count(1) as aaa");//分组的select字段
groupby.putGroupByField(userJtb.getTableAlias()+".deptId",true);//按照deptId来作为分组条件
List<SysNotice> gb= noticeService.findGroupByList(groupby);
}
形成的sql:
拼接参数后sql____________:[com.ruoyi.system.mapper.SysNoticeMainMapper.findGroupByList]:
SELECT COUNT(1) AS aaa, sysuser46.dept_id AS deptId
FROM sys_notice a
LEFT JOIN sys_user sysuser46 ON notice_id = sysuser46.user_id
GROUP BY sysuser46.dept_id
我们可以看到结果集为:
-
接下来,演示一个子查询的关联(注意:这里只是演示,可能与实际业务无关)
先看写法如下:
public void testSubQuery() {
SysNotice sub=new SysNotice();
sub.setNoticeContent("提醒");//模拟设置一个查询条件的内容
sub.putWhereLike("noticeContent");//表示对noticeContent进行like处理
JoinTableBean userJtb=JoinTableBean.fastGetJtb("sysUser", "noticeId", "userId");
userJtb.joinInto(sub);
userJtb.putSelect("deptId","deptId");
sub.putModifyAlias("noticeId", "notice_Id");//修改noticeId字段的别名为notice_Id,这是因为这段查询我作为一个子查询,因此修改其别名为数据库字段名,以便后续作为关联条件
String sql=noticeService.findMybatisSql(sub, "findList");//不执行实际查询,将findList方法的sql拿出,以便作为子查询
System.out.println("********"+sql);
//-----------------------------------------------------------------------------
SysNotice main=new SysNotice(); //创建一个主查询
String subTable=sql;
JoinTableBean selfJtb=JoinTableBean.fastGetJtb(subTable, "a.noticeId", "noticeId");//将上面的子查询sql(普通sql语句而已,也可以自行手写)作为关联表内容,同时设置关联条件,由于两张表的关联字段相同,因此为主表的关联条件加上别名(默认主表别名为a)
selfJtb.joinInto(main);
List<SysNotice> result=noticeService.findList(main);
}
形成的sql如下:
子查询部分sql:
******** select a.notice_id AS notice_Id,a.notice_title AS "noticeTitle",a.notice_type AS "noticeType",a.notice_content AS "noticeContent",a.status AS "status",a.create_by AS "createBy",a.create_time AS "createTime",a.update_by AS "updateBy",a.update_time AS "updateTime",a.remark AS "remark",sysuser11.dept_id as "deptId" from sys_notice a left join sys_user sysuser11 on notice_id = sysuser11.user_id where a.notice_content LIKE '%提醒%'
整体sql:
拼接参数后sql____________:[com.ruoyi.system.mapper.SysNoticeMainMapper.findList]:
SELECT a.notice_id AS "noticeId", a.notice_title AS "noticeTitle", a.notice_type AS "noticeType", a.notice_content AS "noticeContent", a.status AS "status"
, a.create_by AS "createBy", a.create_time AS "createTime", a.update_by AS "updateBy", a.update_time AS "updateTime", a.remark AS "remark"
FROM sys_notice a
LEFT JOIN (
SELECT a.notice_id AS notice_Id, a.notice_title AS "noticeTitle", a.notice_type AS "noticeType", a.notice_content AS "noticeContent", a.status AS "status"
, a.create_by AS "createBy", a.create_time AS "createTime", a.update_by AS "updateBy", a.update_time AS "updateTime", a.remark AS "remark"
, sysuser11.dept_id AS "deptId"
FROM sys_notice a
LEFT JOIN sys_user sysuser11 ON notice_id = sysuser11.user_id
WHERE a.notice_content LIKE '%提醒%'
) subtable6
ON a.notice_id = subtable6.notice_id
至此,一个在若依项目上使用的,基于动态sql的简单介绍就到这里,在整个过程中,所用到的所有bean、mapper文件,均用生成工具生成的,没有改动过一个地方,这就是真正的动态bean、动态sql!
但是要调用此函数,需要对您的项目做如下几个配置的更改:
- 引入我们的tengjie-base工程编译后的jar包。这里我们用的是druid的1.11.14,是因为该项目用的是这个版本 ,也可以用更老的版本
- 需要在mybatis-config.xml中增加如下几个拦截器:
在若依这个项目中,我们没有使用分页拦截器,使用的是它自带的,所以只需要配置后面两个即可。但是我无法修改她的分页拦截器,她的分页拦截器会将实体对象转为map对象。
- 使用我们的代码生成器 ,我们可以生成bean和mapper文件,但是实际只需要我们的mapper文件即可。
- 像这个例子,是在他已经有的一个管理功能上改造,因为只需要将其原来的bean继承我们的tjBaseEntity即可,同时我也将他原有的BaseEntity中内容copy到了tjBaseEntity中
3.另外,我们的mapper是主子结构,即子mapper继承主mapper,主mapper就是根据数据库表生成的mapper文件,子mapper是用来手工增加方法的,这样当数据库变化时,直接覆盖主mapper即可,但是目前为止 ,我们还没有需要手工增加方法的必要。为了不 影响这个功能的现有使用,我直接生成了一个主mapper,将他原有mapper继承主mapper即可;同时dao层也是与mapper文件对应的继承关系。
SysNoticeMapper是他原来的mapper。SysNoticeMainMapper这个dao是我生成,
SysNoticeMainMapper,需要继承至crudDao
4.另外,如果不是在原有项目的功能上改造,比如在他的项目上做一个新功能,实际是不需要再去手工修改什么,生成后直接使用即可。
最后,我们的开源项目的地址为:
https://github.com/kevinliang-123/mybatis-topspeed
如果想对整体演变过程进行了解,请参考https://blog.****.net/kevin_liang_feng/article/details/103014240