三、XML映射器
三、XML映射器
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文
件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95%
的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
-
cache– 该命名空间的缓存配置。 -
cache-ref– 引用其它命名空间的缓存配置。 -
resultMap– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。 -
parameterMap– 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。 -
sql– 可被其它语句引用的可重用语句块。 -
insert– 映射插入语句。 -
update– 映射更新语句。 -
delete– 映射删除语句。 -
select– 映射查询语句。注:这里每一个元素的可选属性不一个个的说明,具体可看官网
1、select 查询
-
select标签是mybatis中最常用的标签之一
-
select语句有很多属性可以详细配置每一条SQL语句
-
- SQL语句返回值类型。【完整的类名或者别名】
- 传入SQL语句的参数类型 。【万能的Map,可以多尝试使用】
- 命名空间中唯一的标识符
- 接口中的方法名与映射文件中的SQL语句ID 一一对应
- id
- parameterType
- resultType
示例 1 :查询所有用户
UserMapper 接口
public interface UserMapper {
List<User> findUsers();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserMapper">
<select id="findUsers" resultType="pojo.User">
select * from user
</select>
</mapper>
测试
@Test
public void findUsers(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.findUsers();
for (User user : users) {
System.out.println(user);
}
session.close();
}
示例 2: 根据 id 查用户
UserMapper 接口
public interface UserMapper {
User findById1(int id); //传入一个 int 类型的参数
User findById2(Map<String,Object> map); //通过Map集合 传入参数
}
UserMapper.xml
parameterType 是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数
<select id="findById1" resultType="pojo.User">
select * from user where id = #{id}
</select>
<select id="findById2" parameterType="map" resultType="pojo.User">
select * from user where id = #{id}
</select>
测试
@Test
public void findById1(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.findById1(1);
session.close();
System.out.println(user);
}
@Test
public void findById2(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("id",2);
User user = mapper.findById2(map);
session.close();
System.out.println(user);
}
示例 3 :模糊查询
UserMapper 接口
List<User> findByNameLike(Map<String,Object> map);
第1种:在Java代码中添加sql通配符。
UserMapper.xml
<select id="findByNameLike" parameterType="map" resultType="pojo.User">
select * from `user` where `name` like ${name}
</select>
测试
@Test
public void findByNameLike(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("name","'%2%'");
List<User> userList = mapper.findByNameLike(map);
session.close();
for (User user : userList) {
System.out.println(user);
}
}
第2种:在sql语句中拼接通配符,会引起sql注入
UserMapper.xml
<select id="findByNameLike" parameterType="map" resultType="pojo.User">
select * from user where name like "%"#{name}"%"
</select>
测试
@Test
public void findByNameLike(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("name","2");
List<User> userList = mapper.findByNameLike(map);
session.close();
for (User user : userList) {
System.out.println(user);
}
}
总结:如果参数过多,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可
示例 4 :分页
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行
查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这
样对数据库压力就在可控范围内。
方式一: 使用Limit在SQL层面实现分页
#语法
SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
#如果只给定一个参数,它表示返回最大的记录行数目:
SELECT * FROM table LIMIT 5; //检索前 5 个记录行
#换句话说,LIMIT n 等价于 LIMIT 0,n。
步骤:
1、 Mapper接口,参数为map
List<User> findUsers2(Map<String,Object> map);
2、修改Mapper文件
<select id="findUsers2" resultType="user" parameterType="map">
select * from user limit #{startIndex},#{pageSize}
</select>
3、测试
起始位置 = (当前页面 - 1 ) * 页面大小
@Test
public void findUser2(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int pageSize = 2;//每页几个
int startIndex = 0;//第几页
Map<String, Object> map = new HashMap<>();
map.put("startIndex",startIndex);
map.put("pageSize",pageSize);
List<User> userList = mapper.findUsers2(map);
for (User user : userList) {
System.out.println(user);
}
session.close();
}
方式二: 使用RowBounds在Java代码层面实现分页
步骤:
1、 Mapper接口,参数为map
//选择全部用户RowBounds实现分页
List<User> getUserByRowBounds();
2、修改Mapper文件
<select id="getUserByRowBounds" resultType="user">
select * from user
</select>
3、测试
起始位置 = (当前页面 - 1 ) * 页面大小
@Test
public void findUser3(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int pageSize = 2;
int startIndex = 0;
RowBounds rowBounds = new RowBounds(startIndex, pageSize);
List<User> users = session.selectList("mapper.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : users) {
System.out.println(user);
}
session.close();
}
方式三: 使用分页插件 PageHelper
官方文档:https://pagehelper.github.io/

2、insert 插入
我们一般使用insert标签进行插入操作,它的配置和select标签差不多!
注意点:增、删、改操作 改变数据库 所以需要 提交事务!
示例 1 :插入一个用户
UserMapper 接口
public interface UserMapper {
int addUser(User user); //直接传入 User
int addUser1(Map<String,Object> map); //通过Map集合传入
}
UserMapper.xml
<insert id="addUser" parameterType="pojo.User" >
insert into user(id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
<insert id="addUser1" parameterType="map">
insert into user(id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
测试
@Test
public void insertUser(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int user = mapper.addUser(new User(5, "5", "5"));
session.commit();//提交事务
session.close();
System.out.println(user);
}
@Test
public void insertUser1(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("id",12);
map.put("name","liang");
map.put("pwd","liang");
int i = mapper.addUser1(map);
session.commit();//提交事务
session.close();
System.out.println(i);
}
3、 update 更新
我们一般使用update标签进行更新操作 ,它的配置和insert标签差不多!
示例 : 修改用户的信息
UserMapper 接口
int updateUser(User user);
UserMapper.xml
<update id="updateUser" parameterType="pojo.User">
update user set name = #{name},pwd=#{pwd} where id=#{id}
</update>
测试
@Test
public void updateUser(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.updateUser(new User(5, "55555", "555555"));
session.commit();
session.close();
System.out.println(i);
}
4、delete 删除
我们一般使用delete标签进行删除操作,它的配置和insert标签差不多!
示例 :根据 id 删除用户
UserMapper 接口
int deteleUser(int id);
UserMapper.xml
<delete id="deteleUser" >
delete from user where id=#{id}
</delete>
测试
@Test
public void deleteUser(){
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.deteleUser(0);
session.commit();
session.close();
System.out.println(i);
}
小结:
- 所有的增删改操作都需要提交事务!
- 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
- 有时候根据业务的需求,可以考虑使用map传递参数!
- 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!
5、参数
参数是 MyBatis 非常强大的元素
简单参数传递
<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>
鉴于参数类型(parameterType)会被自动设置为 int,这个参数可以随意命名。
原始类型或简单数据类型(比如 Integer 和 String)因为没有其它属性,会用它们的值来作为参数
对象类型参数
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
User 类型的参数对象传递到了语句中,会查找 id、username 和 password 属性,然后将它们的值
传入预处理语句的参数中。
字符串替换
在SQL中引用这些参数的时候,可以使用两种方式:
#{parameterName}
${parameterName}
这两种引用参数时的区别,使用#引用参数的时候,Mybatis会把这个参数认为是
一个字符串,并自动加上''
如下例子
传入的参数 id 为 1
select * from user where id = #{id}
结果就是
select * from user where id = '1'
select * from user where id = ${id}
结果就是
select * from user where id = 1
简单说 #{}是经过预编译的,是安全的。
而${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。
#{} 这种取值是编译好SQL语句再取值
${} 这种是取值以后再去编译SQL语句
6、resultMap 结果集映射
1、resultMap 元素的概念视图
- constructor 用于在实例化类时,注入结果到构造方法中
idArg- ID 参数;标记出作为 ID 的结果可以帮助提高整体性能arg- 将被注入到构造方法的一个普通结果
id– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能result– 注入到字段或 JavaBean 属性的普通结果association一个复杂类型的关联;许多结果将包装成这种类型- 嵌套结果映射 – 关联可以是
resultMap元素,或是对其它结果映射的引用
- 嵌套结果映射 – 关联可以是
collection一个复杂类型的集合- 嵌套结果映射 – 集合可以是
resultMap元素,或是对其它结果映射的引用
- 嵌套结果映射 – 集合可以是
discriminator使用结果值来决定使用哪个resultMapcase基于某些值的结果映射- 嵌套结果映射 –
case也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
- 嵌套结果映射 –
2、 自动映射
resultMap元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBCResultSets数据提取代码中解放出来。- 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份
resultMap能够代替实现同等功能的长达数千行的代码。 - ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
你已经见过简单映射语句的示例了,但并没有显式指定 resultMap。比如:
<select id="selectUserById" resultType="map">
select id , name , pwd
from user
where id = #{id}
</select>
上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部
分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或
POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。
ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。
3、 手动映射
1、返回值类型为resultMap
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
2、编写resultMap,实现手动映射!
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后
会使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解。
4、多对一
我们以一个例子为例
学生 老师 之间的关系 多个学生对应一个老师 一个老师对应多个学生
1、先编写实体类
这里使用了lombok 插件生成 get、set、toString方法,不会请自行百度
Student.class
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
Teacher.class
@Data
public class Teacher {
private int id;
private String name;
}
2、 编写实体类对应的Mapper接口 【两个】
public interface StudentMapper {
}
public interface TeacherMapper {
}
3、 编写Mapper接口对应的 mapper.xml配置文件 【两个】
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.StudentMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.TeacherMapper">
</mapper>
4、获取所有学生及对应老师的信息
这是一个多对一的关系
我们用两种方法实现
1)按查询嵌套处理
思路:
-
获取所有学生的信息
-
根据获取的学生信息的老师ID->获取该老师的信息
-
查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
StudentMapper接口
public interface StudentMapper {
List<Student> findStudentList();
}
StudentMapper.xml
<select id="findStudentList" resultMap="studentTeacher">
select * from student
</select>
<resultMap id="studentTeacher" type="student">
<!--association关联属性 property属性名 javaType属性类型 column是外键名-->
<!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值
association中column多参数配置:
column="{key=value,key=value}"
其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。
-->
<association property="teacher" column="tid" javaType="Teacher" select="findTeacher"/>
</resultMap>
<select id="findTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
编写完毕去Mybatis配置文件中,注册Mapper!
测试
@Test
public void findStudentList(){
SqlSession session = MybatisUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> studentList = mapper.findStudentList();
for (Student student : studentList) {
System.out.println(student);
}
session.close();
}
2) 按结果嵌套处理
StudentMapper接口
public interface StudentMapper {
List<Student> findStudentList2();
}
StudentMapper.xml
<!-- 按照查询结果嵌套处理 -->
<select id="findStudentList2" resultMap="studentTeacher2">
select s.id sid,s.name sname,s.tid tid,t.name tname from student s,teacher t where s.tid = t.id
</select>
<resultMap id="studentTeacher2" type="student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
编写完毕去Mybatis配置文件中,注册Mapper!
测试
@Test
public void findStudentList2(){
SqlSession session = MybatisUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> studentList = mapper.findStudentList2();
for (Student student : studentList) {
System.out.println(student);
}
session.close();
}
小结
-
按照查询进行嵌套处理就像SQL中的子查询
-
按照结果进行嵌套处理就像SQL中的联表查询
5、一对多
一个老师对应多个学生
1、先编写实体类
这里使用了lombok 插件生成 get、set、toString方法,不会请自行百度
Student.class
@Data
public class Student {
private int id;
private String name;
private int tid;
}
Teacher.class
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
2、 编写实体类对应的Mapper接口 【两个】
public interface StudentMapper {
}
public interface TeacherMapper {
}
3、 编写Mapper接口对应的 mapper.xml配置文件 【两个】
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.StudentMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.TeacherMapper">
</mapper>
4、获取老师及对应学生的信息
也是用两种方式实现
1)按查询嵌套处理
TeacherMapper 接口
public interface TeacherMapper {
List<Teacher> findTeachers(int id);
}
TeacherMapper.xml
<!--按查询嵌套处理-->
<select id="findTeachers" resultMap="studentTeachers">
select * from teacher where id = #{id}
</select>
<resultMap id="studentTeachers" type="teacher">
<collection property="students" ofType="Student" javaType="ArrayList"
column="id" select="findStudentByTid"/>
</resultMap>
<select id="findStudentByTid" resultType="student">
select * from student where tid = #{id}
</select>
编写完毕去Mybatis配置文件中,注册Mapper!
测试
@Test
public void findTeachers(){
SqlSession session = MybatisUtil.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.findTeachers(1);
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
session.close();
}
2) 按结果嵌套处理
TeacherMapper 接口
public interface TeacherMapper {
List<Teacher> findTeachers2(int id);
}
TeacherMapper.xml
<!--按结果嵌套处理-->
<select id="findTeachers2" resultMap="studentTeachers2">
select s.id sid,s.name sname,t.id tid,t.name tname from teacher t,student s
where t.id = s.tid and t.id = #{id}
</select>
<resultMap id="studentTeachers2" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
编写完毕去Mybatis配置文件中,注册Mapper!
测试
@Test
public void findTeachers(){
SqlSession session = MybatisUtil.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.findTeachers2(1);
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
session.close();
}
小结:
1、关联-association
2、集合-collection
3、所以association是用于一对一和多对一,而collection是用于一对多的关系
4、JavaType和ofType都是用来指定对象类型的
- JavaType是用来指定pojo中属性的类型
- ofType指定的是映射到list集合属性中pojo的类型。
注意说明:
1、保证SQL的可读性,尽量通俗易懂
2、根据实际要求,尽量编写性能更高的SQL语句
3、注意属性名和字段不一致的问题
4、注意一对多和多对一 中:字段和属性对应的问题
5、尽量使用Log4j,通过日志来查看自己的错误
mybatis3官方文档
https://mybatis.org/mybatis-3/zh/index.html

浙公网安备 33010602011771号