三、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,这个参数可以随意命名。

原始类型或简单数据类型(比如 IntegerString)因为没有其它属性,会用它们的值来作为参数

对象类型参数

<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 使用结果值来决定使用哪个resultMap
    • case 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

2、 自动映射

  • resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来。
  • 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 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,通过日志来查看自己的错误

参考文献
狂神说
http://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=3&sn=456dc4d66f0726730757e319ffdaa23e&scene=18#wechat_redirect

mybatis3官方文档
https://mybatis.org/mybatis-3/zh/index.html

posted @ 2020-08-26 16:32  梁三哥  阅读(164)  评论(0)    收藏  举报