在mybatis-plus中,难免会进行自定义sql语句,而在mybatis-plus中,之前自定义SQL使用customSqlSegment时,where条件是支持指定表的别名的,方法为:customSqlSegment(String alias)。

使用示例:
select a.name,b.name
from table_a as a
left join table_b as b on b.a_id = a.id
${ew.customSqlSegment("a")}

但是此方法被关闭掉了,现已无法使用,后来在知乎中找到mysql-plus在自定义sql中注重层级关系方法,但也只能是暂时解决方法,现引入如下:

背景

以往的SQL分页查询,需要程序员手动编写一个count语句和一个查询语句(两者是成对出现的),这两个语句会经过MyBatis映射为两个Java方法,在Java的DAO层调用。这些都是机械的重复劳动,count语句是可以由框架自动生成的,没必要每次都人工去写。人只要写查询语句这一个语句就可以了。

在MyBatis基础上实现自动分页的框架有2个,都是国产的,一是MyBatis Page Helper插件,二是MyBatis Plus。MyBatis Page Helper只是自动分页,只涉及select,而不涉及insert, update。而MyBatis Plus除了自动分页之外,还能自动insert, update,不编写任何SQL代码就能做数据库增删查改的一整套操作,很适合快速开发、快速搭建项目原型。

MyBatis Plus的自动分页,只能用于最简单的SQL语句,遇到稍微复杂一点的SQL语句就会报错。这时候你的第一念头或许是,复杂的语句我不用MyBatis Plus,不让它自动分页,用原生的MyBatis不就行了?这样当然可以,但是肯定没有我今天介绍的方法来得快。我今天介绍的独家原创方法,只要把mapper.xml里的SQL语句稍微调整一下,只改xml,不改Java,就可以让MyBatis Plus支持复杂语句的自动分页。

 

什么样的SQL语句会让MyBatis Plus无法分页

结果列用到了别名的语句,且查询条件也用到别名,则MyBatis Plus无法分页,形如

SELECT 
       UPPER(T.NAME) UPPER_NAME  -- 把字段转换成大写
  FROM PERSON T
        <if test="ew.emptyOfWhere == false">
            ${ew.customSqlSegment}
        </if>

当customSqlSegment里面带有UPPER_NAME的查询条件时,会报错。

原因是因为MyBatis Plus会直接把查询条件附在语句后面,变成这样,

SELECT       
       UPPER(T.NAME) UPPER_NAME  -- 把字段转换成大写
  FROM PERSON T
WHERE UPPER_NAME = 'HAOYU'

而这个语句本身是不符合SQL以及Oracle语法的。Where条件后面不能用别名,尽管人的直觉看上去这么写也很合理。我们要让它符合语法,要在外面包一层SELECT * FROM,则mapper.xml改成这种写法,

SELECT * FROM 
(SELECT 
       UPPER(T.NAME) UPPER_NAME  -- 把字段转换成大写
  FROM PERSON T) T1
        <if test="ew.emptyOfWhere == false">
            ${ew.customSqlSegment}
        </if>

就这样包一层就可以了。目的是为了让别名和customSqlSegment分别位于语句的两个不同的层次,别名在里层,customSqlSegment在外层。 虽然改起来很简单,但MyBatis Plus的作者原本应该考虑到这一点,把这一层逻辑写在框架里面的,而不是每次都要程序员去写。

 

更复杂的情况

或许MyBatis Plus的作者确实考虑到了,也想过要这么做,但因为还有更复杂的情况,并不能很容易地确定到底包在哪一层。

例如这种情况,语句有group by,customSqlSegment夹在select和group by中间,

SELECT H.ID,
				H.CREATE_BY,
				H.CREATE_TIME,
				H.UPDATE_BY,
				H.UPDATE_TIME,
				H.SYS_ORG_CODE,
				H.BUILDING_ID,
				H.HOURSE_TYPE,
				H.HOURSE_AREA,
				H.HOURSE_PURPOSE,
				H.HOURSE_NUM,
				H.YE_TAI,
				H.IF_INVENTED,
				H.FLOOR_NUM,
		        R.NAME RESIDENTIAL_AREA_NAME,
		        COUNT(O.RESIDENTS_FK_ID) OWNER_NAME,
                B.COMMUNITY_ID
		   FROM BUSS_HOUSE_INFO H
		   LEFT JOIN BUSS_BUILDING_INFO B
		     ON H.BUILDING_ID = B.ID
		   LEFT JOIN BUSS_RESIDENTIAL_AREA R
		     ON B.RESIDENTIAL_AREA_ID = R.ID
		   LEFT JOIN BUSS_RESIDENTS_RESOURCES O
		     ON O.Resources_Id = H.ID
		    AND O.RESOURCES_TYPE = 'house'
		    AND O.LIVE_TYPE IN ('A03', 'A05', 'A12','A13', 'A14', 'A15')
        <if test="ew.emptyOfWhere == false">
            ${ew.customSqlSegment}
        </if>
	        GROUP BY H.ID,
	          H.CREATE_BY,
	          H.CREATE_TIME,
	          H.UPDATE_BY,
	          H.UPDATE_TIME,
	          H.SYS_ORG_CODE,
	          H.BUILDING_ID,
	          H.HOURSE_TYPE,
	          H.HOURSE_AREA,
	          H.HOURSE_PURPOSE,
	          H.HOURSE_NUM,
	          H.YE_TAI,
	          H.IF_INVENTED,
	          H.FLOOR_NUM,
	          R.NAME,
	          B.DOOR_ADDRESS,
	          B.COMMUNITY_ID

 

假如在这个语句基础上,再把其中一个结果列加上别名,那么还能在外面包一层select * from吗? 如果包的话,要不要把customSqlSegment也包进去呢? 如果不包进去,意味着group by子句被单独隔在外面了,语法很难调整。 如果包进去,那么外面包一层等于没包,别名和customSqlSegment还是位于同一层的。这种情况建议改写查询条件,即查询条件中不要出现别名。

转载于:https://zhuanlan.zhihu.com/p/268119101

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐