您的位置:首页 > 其它

Mybatis 使用技巧总结

2016-02-15 09:52 363 查看


1、 区分 #{} 和 ${}的不同应用场景

1)#{} 会生成预编译SQL,会正确的处理数据的类型,而${}仅仅是文本替换。

对于SQL: select * from student where xCode = ‘S123456’;

如果使用#{}

那么生成的SQL为:

select * from student where xCode = ? 传的值为’S123456’;

如果使用${}

那么生成的SQL为:select * from student where xCode = S123456

如果xCode的数据类型为varchar,那么使用${}就会报错。

2)${}一般用在order by, limit, group by等场所。

假设我们使用#{} 来指定order by字段,比如

select * from student order by #{xCode},

那么产生的SQL为

select * from student order by ?, 替换值后为

select * from student order by ‘xCode’

Mybatis对xCode加了引号导致排序失败


2、Spring环境用Mybatis-Spring的接口而不是Mybatis的原生接口

在spring 环境使用mybatis-spring的好处有:

1)我们可以使用Sping的声明式事务处理模型(@Transactional),而不用手动回归事务。

2)mybatis-spring会优雅的关闭SqlSession,而不用手动关闭

3)可以将数据库连接池交给spring管理,当程序停止的时候,spring会合适的关闭连接


3、返回Map<ID, Entity>而不是List便于查找

有时太多的表连接(join)性能太差,我们会将该SQL拆为多个SQL,然后在代码中组装起来。比如学生表和班级表,需要查询的结果为”学号,班级,姓名”,我们可以先查询“学号,班级ID,姓名”以及“班级ID,班级名次”,我们可以在查询班级表的时候返回Map<班级ID, 班级>, 然后迭代学生表的结果集,用班级ID到Map<班级ID, 班级>中查找对应的班级信息,然后用班级名称替换班级ID。

接口声明为SqlSession.selectMap(String statement, String mapKey)


4、使用Map封装查询的结果

有时我们厌倦了为每个查询写一个Entity类,这时Map开始发挥它的功效。

对于要返回“学号,班级,姓名”结果的查询,可以这样写Mapper:

<select id="selectStudent">

select s.code as sNo , s.name as sName, c.name as cName
from xStudent s, xClass c
where s.cID = c.ID
</select>


如下声明我们的dao方法:

123
publicList<Map<String,Object>>selectStudent(Map<String,Object>parameter){
returngetSqlSession().selectList(getStatement("selectStudent"),parameter);
}

如果要将该查询结果转为JSON字符串返回,那么我们就可以直接将List<Map<String, Object>转为JSON,逻辑层不需要任何代码。
如果返回的结果集需要按select中的字段顺序返回,那么将resultType=”java.util.HashMap” 换为resultType=”java.util.LinkedHashMap”

5、使用Map封装查询结果时注意数据的类型映射

对于如下的Mapper

1

2

3

4

<select id="selectStudent">
select s.code as sNo , concat(s.firstName, s.lastName) as sName
from xStudent s
</select>


Mybatis会傻傻的将sName的数据类型映射为byte[], 因为我们没有提供entity,mybatis也不知道我们想要什么类型,而sName是计算出来的值,mybatis也没有办法从数据库中获取字段的值,所以它就将其封装为byte[],解决办法很简单,加一个cast 函数

1234
<selectid="selectStudent">
selects.codeassNo,cast(concat(s.firstName,s.lastName)ASCHAR)assName
fromxStudents
</select>

6、正确的配置Mybatis 的Log

1)一个应用一般会使用很多的jar,各个jar依赖的log 实现不一样,Mybatis查找Log的顺序为(SLF4J,Apache Commons Logging,Log4j 2,Log4j,JDK logging),如果classpath中有slf4j记得添加相应的桥接jar,比如slf4j-log4j。许多web 服务器的classpath 会含有Apache Commons Logging,因此如果要使用Log4j,要么使用SLF4J桥接Log4j,要么在配置中强制指定使用Log4J。

1

2

3

4

5

6

7

<configuration>
<settings>
...
<setting name="logImpl" value="LOG4J"/>
...
</settings>
</configuration>



7、警惕Mybatis的Foreach的的副作用

对于如下SQL:

假设有如下的mapper:

123456789101112131415
<selectid=”testForeach”  parameterType=”map”  resultType=”Student”>
Select*fromstudent
<where>
<iftest=”ID!=nullandID!=‘’”>
ID=#{ID}
</if>
<iftest=”IDArr!=nullandIDArr.size()>0”>
AndIDIN
<foreachcollection="IDArr"open="("
separator=","close=")"item="ID">
${ID}
</foreach>
</if>
</where>
</select>

当我们传入的IDArr时,最后产生的SQL为:
Select * from student where ID = ‘998’ AND ID IN ( ‘123’, ’234’,…..,’998’)
解决办法:
解决办法有
1) 将红色的ID 换成别的名称,比如“item”。
2) 这两个if 是对同一个字段判断,改为choose… when 结构

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<select id=”testForeach”  parameterType=”map”  resultType=”Student”>
Select * from student
<where>
<choose>
<when test=”ID != null and ID != ‘’ ”>
ID = #{ID}
</when>
<when test=” IDArr != null and IDArr.size()>0”>
And ID IN
<foreach collection="IDArr" open="("
separator="," close=")" item="ID">
${ID}
</foreach>
</when>
</choose>
</where>



8、使用原生的SQL操作数据以提高效率

对于一次插入多条数据,将其组装成 insert into xxx values (), () ()格式一次插入多行数据往往能极大的提高性能。

12345678910111213141516171819202122
publicintexecute(Stringsql){
intaffectedCount=0;
Connectionconn=null;
Statementstm=null;
try{
conn=getSqlSession().getConnection();
stm=conn.createStatement();
stm.execute(sql);
affectedCount=stm.getUpdateCount();
}catch(SQLExceptione){
thrownewRuntimeException(“execute[" + sql + "]failed”);
}finally{
try{
if(stm!=null&&!stm.isClosed()){
stm.close();
}
//conn will be released by mybatis framework
}catch(SQLExceptione){
}
}
returnaffectedCount;
}

9、警惕MyBatis封装数据时性能损耗

对于如下的mapper

1

2

3

4

5

6

7

<select id="test" resultType="Student">
select s.code, s.firstName,
s.lastName, s.birthDate, s.sex, s.checkIn, s.phoneNumber,
s.classNo
from student
where ...
</select>


在一个批处理程序中循环的调用了该方法250次,每次返回大概1w条记录,发现这个程序运行的很慢,用jrofiler 查看各个方法耗费的时间,居然80%的时间花在了student的setter上了,在这个过程中大概产生了250w个对象,而mybatis是利用发射封装Entity,代码大致如下:

1

2

3

4

5

6

Classc=Class.forName("cn.javacoder.testmybatis.Student");
Objecto=c.newInstance();
for(eachcolum){
Methodm=c.getMethod("get"+colum);
m.invoke(o,value);
}


解决的办法是让返回的行数和返回的字段尽量的少。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: