mybatis-2-基础

#{}和${}的区别是什么

#{}是预编译处理,对应的变量自动加上单引号 ‘’
${}是字符串替换,对应的变量不会加上单引号 ‘’

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。

使用#{}可以有效的防止SQL注入,提高系统安全性。
预编译语句的优势在于归纳 为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止sql注入。

什么是MyBatis的接口绑定?有哪些实现方式?

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,
这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。

接口绑定有两种实现方式:
1 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;

2 通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。

当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用****标签,逐一定义列名和对象属性名之间的映射关系。

第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

Mybatis一对一、一对多的查询

比如班级学生老师 一个Class类中有Teacher属性和List属性 如下例子

MyBatis 中使用collection标签来解决一对多的关联查询,collection标签可用的属性如下:
property:指的是集合属性的值. ofType:指的是集合中元素的类型. column:所对应的外键字段名称. select:使用另一个查询封装的结果.

MyBatis 中使用association标签来解决一对一的关联查询,association标签可用的属性如下:
property:对象属性的名称. javaType:对象属性的类型. column:所对应的外键字段名称. select:使用另一个查询封装的结果!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<mapper namespace="com.lcb.mapping.userMapper">  

<!--collection 一对多关联查询 -->
<select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">
select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<collection property="student" ofType="com.lcb.user.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>

<!--association 一对一关联查询 -->
<select id="getClass" parameterType="int" resultMap="ClassesResultMap">
select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap">
<!-- 实体类的字段名和数据表的字段名映射 -->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
</mapper>
<!-- 参考自:https://www.cnblogs.com/xdp-gacl/p/4264440.html -->

Mybatis的分页原理

Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

分页插件的原理就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的SQL,然后根据设置的dialect(方言),和设置的分页参数,重写SQL ,生成带有分页语句的SQL,执行重写后的SQL,从而实现分页。

举例:select ※ from student,拦截sql后重写为:select t.※ from (select ※ from student)t limit 0,10

1、内存分页(逻辑分页)

逻辑分页利用游标分页,好处是所有数据库都统一,坏处就是效率低。

使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。

MyBatis使用RowBounds实现的分页是内存分页,也就是先把数据记录全部查询出来,
然在再根据offset和limit截断记录返回(数据量大的时候会造成内存溢出

1
2
3
4
5
6
7
8
9
//mapper接口方法
List<Honor> getHonorList(HashMap<String, Object> maps,RowBounds rowBounds);


RowBounds(offset, pageSize); // offset起始行 // pageSize(limit)是当前页显示多少条数据

//例如
RowBounds rowBounds = new RowBounds(2, 2);
List<Honor> honors = studentMapper.getHonorList(maps,rowBounds);

2、物理分页

物理分页就是数据库本身提供了分页方式,如MySQL的limit,oracle的rownum ,好处是效率高,不好的地方就是不同数据库有不同的搞法。

自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。


Mybatis动态SQL

传统的JDBC的方法,在组合SQL语句的时候需要去拼接,稍微不注意就会少少了一个空格,标点符号,都会导致系统错误。
Mybatis的动态SQL就是为了解决这种问题而产生的;
Mybatis的动态SQL语句值基于OGNL表达式的,方便在SQL语句中实现某些逻辑;
可以使用标签组合成灵活的sql语句,提供开发的效率。

Mybatis的动态SQL标签主要由以下几类:

If语句(简单的条件判断)

**Choose(when/otherwise)**,相当于java语言中的switch,

Trim(对包含的内容加上prefix,或者suffix)

Where(主要是用来简化SQL语句中where条件判断,能智能的处理and/or 不用担心多余的语法导致的错误)

Set(主要用于更新时候)

Foreach(一般使用在mybatis in语句查询时特别有用)


Mybatis的批量操作数据的方法

1、使用for循环在java代码中insert (不推荐)

2、foreach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!-- 批量新增-->
<insert id="batchSave" parameterType="java.util.List">
INSERT INTO lp_user_test_batch
(
id,
user_id,
user_name,
user_age,
type,
create_time,
update_time
)
VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id,jdbcType=BIGINT},
#{item.userId,jdbcType=VARCHAR},
#{item.userName,jdbcType=VARCHAR},
#{item.userAge,jdbcType=INTEGER},
#{item.type,jdbcType=INTEGER},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateTime,jdbcType=TIMESTAMP}
)
</foreach>
</insert>

<select id="getList" resultType="com.epeit.api.model.Device">
SELECT *
FROM devcie
WHERE 1=1
<if test="ids != null and ids.size > 0">
AND id IN
<foreach collection="ids" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</select>

批量新增或更新方式
注:需要给唯一主键添加唯一索引,update才会生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 批量新增或更新-->
<insert id="batchSaveOrUpdate" parameterType="java.util.List">
INSERT INTO lp_user_test_batch
(
id,
user_id,
user_name,
user_age,
type,
create_time,
update_time
)
VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id,jdbcType=BIGINT},
#{item.userId,jdbcType=VARCHAR},
#{item.userName,jdbcType=VARCHAR},
#{item.userAge,jdbcType=INTEGER},
#{item.type,jdbcType=INTEGER},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateTime,jdbcType=TIMESTAMP}
)
</foreach>
ON DUPLICATE KEY UPDATE
user_name = VALUES(user_name),
user_age = VALUES(user_age),
type = VALUES(type),
update_time = VALUES(update_time)
</insert>

优点:速度快
缺点:使用特殊语法 on duplicate key update 语法 增加sql难度性