mybatis 笔记
不支持该SQL转换成分页查询
发生场景是一次分页改造,将原来某方法中查询全部结果再在方法中手动分页获取部分结果的逻辑改造成使用startPage
从数据库查询进行分页,伪代码逻辑如下:
public void foo(Entity entity){
startPage();
var list1 = myMapper.selectQuery(entity);
startPage();
var list2 = myMapper.selectQuery2(entity); // 该语句抛出不支持该SQL转换成分页查询异常
}
起初以为连续使用startPage
造成错误,将selectQuery2
改成selectQuery
测试,顺利通过,证明连续使用startPage
本身不会引起错误。经同事提醒,结合错误提示,分析是selectQuery2
中定义的sql语句有问题,mybatis不支持把sql转换成分页查询。
经查,由于数据源是SqlServer,有链接服务器能力,因此selectQuery2
中查询语句写成了[ip].[数据库].[架构].[视图]
的方式,其在xml
定义并在查询中使用了“特殊”sql和符号,由于myBatis无法对from
中的语法进行正确分析解释,造成异常,sql定义如下:
<sql>
select DISTINCT *
FROM [19.XX.XX.XX].[数据库名].[dbo].[视图名]
</sql>
解决方法把from
中的带中括号的定义想办法移除,比如使用多数据源方式定义新的Mapper直接去访问目标数据源,即可将sql改为如下:
<sql>
select DISTINCT *
FROM 视图名
</sql>
补充:由于没有测试,尚不确定mybatis到底是不支持[ip].[数据库].[架构].[视图]
的写法,还是仅仅不支持中括号字符
查询结果被合并
mapper 中定义的 sql 查询结果有3条,但执行 mapper 接口方法返回的实体列表只有1条,数据数量不符。这有可能是由于 xml 中的定义的 resultMap
有缺陷,如没有明确的定义一个用作主键的列,这分两种情况分别说明。
// reusltMap 定义
<resultMap id="vo" type="ProjectCentersStatisticsVo">
<association property="entityA" javaType ="domain.entityA">
<id property="id" column="id" />
</association>
</resultMap>
<select id="selectSql" resultMap="vo">
select id, name from t where xxx
</select>
第一种,resultMap
的定义中没有任何 result
或列,有的是 association
或collection
,association
中有id
定义,此时,不会出现该问题,sql语句中出现的唯一的id
被映射到entityA
中的id
;
第二种,resultMap
中定义了一个result
,但它并不是id或者它在sql查询结果中并不是唯一的,但此时mybatis会将其看作主键来使用,值相同的结果被合并留下最后一条,会造成问题发生。
<resultMap id="vo" type="ProjectCentersStatisticsVo">
<result property="name" column="name" />
<association property="entityA" javaType ="domain.entityA">
<id property="id" column="id" />
</association>
</resultMap>
在上例中,resultMap
中定义了一个name
列,mybatis在处理查询结果时会将其作为主键,name
相同的记录(行)将替代上一个,直至留下最后一个name
不同的行。例如查询结果有3条,其中两条name
是tom
,一条name
是jerry
,mybatis处理后将返回2条记录,最后一条tom
和唯一的一条jerry
。
第三种,为了避免发生上述问题,人为明确的定义一个主键在resultMap
中,默认名字是id
,如下
<resultMap id="vo" type="ProjectCentersStatisticsVo">
<result property="pk" column="pk" />
<result property="name" column="name" />
<association property="entityA" javaType ="domain.entityA">
<id property="id" column="id" />
</association>
</resultMap>
<select id="selectSql" resultMap="vo">
select id, id pk, name from t where xxx
</select>
在上例中,通过给resultMap
增加一个pk
字段来表示主键,在sql
中增加一个pk
列并为了保证其值的唯一用真的id填充,这样的好处是原本查询结果中的id
字段还能正确映射到entityA
中的id