mybatis-动态sql动态bean实例

这里是一个针对开源项目若依框架的使用我们动态sql的改造过程的描述,但是其中主要还是介绍了如何使用本框架来实现sql的动态查询和动态bean,如果您使用的是自身的系统,这里对外部框架的改造过程,也可以作为参考。

首先,我们以一个功能的改造,来简单说明:

mybatis-动态sql动态bean实例

上图为该项目的通知管理,初始为一个单表的操作,显示的字段为序号、公告标题、公告类型、状态、创建人、创建时间。

下图为SysNotice的所有属性;(同时在baseEntity中有4个公共属性:createBy、createTime、updateBy、updateTime)

mybatis-动态sql动态bean实例

这是该项目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字段,效果如下

mybatis-动态sql动态bean实例

这时的request会增加 一个叫做deptId的字段 ,如果输入了值,那么就会有内容,因此我们需要调用notice=notice.initDynaMap(request);将request中的值自动copy到notice中,这时我们看到的notice的效果如下:

mybatis-动态sql动态bean实例

我们可以看到,多了几个动态属性,其中一个是deptId

 

接下来详细代码:

public TableDataInfo list(SysNotice notice,HttpServletRequest request)

{

startPage();

notice=notice.initDynaMap(request);//ruquest中全部字段copynotice中,如果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

mybatis-动态sql动态bean实例

形成的页面效果如下:

mybatis-动态sql动态bean实例

而我们的查询结果可以看到多了这个字段:

 

mybatis-动态sql动态bean实例

 

  • 接下来,我想增加一个带有函数的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");//在原有sqlselect中新加一个自定义的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拼接成一个字符串。

mybatis-动态sql动态bean实例

----------------------------------------------------------------------------------------------------------------------

  • 接下来,我想做一个分组的函数,同样是这两张表的关联,按照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

我们可以看到结果集为:

 

mybatis-动态sql动态bean实例

  • 接下来,演示一个子查询的关联(注意:这里只是演示,可能与实际业务无关)

先看写法如下:

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!

 

但是要调用此函数,需要对您的项目做如下几个配置的更改:

  1. 引入我们的tengjie-base工程编译后的jar包。这里我们用的是druid的1.11.14,是因为该项目用的是这个版本 ,也可以用更老的版本
  2. 需要在mybatis-config.xml中增加如下几个拦截器:

mybatis-动态sql动态bean实例

在若依这个项目中,我们没有使用分页拦截器,使用的是它自带的,所以只需要配置后面两个即可。但是我无法修改她的分页拦截器,她的分页拦截器会将实体对象转为map对象。

  1. 使用我们的代码生成器 ,我们可以生成bean和mapper文件,但是实际只需要我们的mapper文件即可。
  2. 像这个例子,是在他已经有的一个管理功能上改造,因为只需要将其原来的bean继承我们的tjBaseEntity即可,同时我也将他原有的BaseEntity中内容copy到了tjBaseEntity中

mybatis-动态sql动态bean实例

3.另外,我们的mapper是主子结构,即子mapper继承主mapper,主mapper就是根据数据库表生成的mapper文件,子mapper是用来手工增加方法的,这样当数据库变化时,直接覆盖主mapper即可,但是目前为止 ,我们还没有需要手工增加方法的必要。为了不 影响这个功能的现有使用,我直接生成了一个主mapper,将他原有mapper继承主mapper即可;同时dao层也是与mapper文件对应的继承关系。

SysNoticeMapper是他原来的mapper。SysNoticeMainMapper这个dao是我生成,

mybatis-动态sql动态bean实例

SysNoticeMainMapper,需要继承至crudDao

mybatis-动态sql动态bean实例

 

4.另外,如果不是在原有项目的功能上改造,比如在他的项目上做一个新功能,实际是不需要再去手工修改什么,生成后直接使用即可。

最后,我们的开源项目的地址为:

https://github.com/kevinliang-123/mybatis-topspeed

如果想对整体演变过程进行了解,请参考https://blog.****.net/kevin_liang_feng/article/details/103014240