2021-04-25 MyBatis Plus疑难问题:customSqlSegment在自定义sql语句中的层级关系以及表别名使用
在mybatis-plus中,难免会进行自定义sql语句,而在mybatis-plus中,之前自定义SQL使用customSqlSegment时,where条件是支持指定表的别名的,方法为:customSqlSegment(String alias)。使用示例:select a.name,b.namefrom table_a as aleft join table_b as b on b.a_id
在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还是位于同一层的。这种情况建议改写查询条件,即查询条件中不要出现别名。
更多推荐
所有评论(0)