MyBatis框架高级应用以及特性分析

本文深入探讨了MyBatis框架的高级应用,包括ORM映射失效的解决办法、动态SQL的实现(如if、where、set、foreach标签)以及缓存机制。此外,还分析了为何使用MyBatis,提到了Durid连接池和PageHelper分页插件的整合,以及MyBatis注解开发和处理关联关系的方法。通过对这些特性的理解与实践,可以更高效地利用MyBatis进行数据库操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

MyBatis

一 ORM概述

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。

ORM

二 什么是 mybatis

概述

ORM框架,对象与关系的映射,对象是程序中的对象,关系是它与数据库之间的关系,即程序对象的与关系型数据库之间的映射关系

功能架构

图片描述

三 为什么使用 mybatis

3.1 底层的原理是什么
  1. 底层原理流程

    img

  2. SqlSessionFactorybuilder

    用来构建SqlSessionFactory, 存在于方法的局部

  3. SqlSessionFactory(单例)

    用来构建SqlSession, 每次应用程序访问数据库,都需要创建一个会话,作用域是应用作用域

  4. SqlSession(粗粒度)

    表示一个会话,线程不安全,不能线程共享,在请求开始创建,结束后关闭。一次请求或者操作中。(可以表示为一个select或者insert,update语句)

    SqlSession 的作用类似于一个 JDBC 中的 Connection 对象,代表着一个连接资源的启用。具体而言,它的作用有 3 个:

    • 获取 Mapper 接口。
    • 发送 SQL 给数据库。
    • 控制数据库事务。

    两种发送 SQL 的方式,一种用 SqlSession 直接发送,

    另外一种通过 SqlSession 获取 Mapper 接口再发送。

  5. Mapper(代理对象,细粒度)

    发送sql操作数据库数据,sqlsession事务方法之内

3.2 有什么特点特性

高度封装(JDBC),重复代码,资源管理,结果集处理,sql耦合

3.2.1 MyBatis自动ORM失效(ORM映射)

MyBatis只能自动维护库表“列名”与“属性名”相同时的一一对应关系,二者不同时,无法自动ORM。

自动ORM失效
007
3.2.1.1 解决方式一:列的别名

在SQL中使用 as 为查询字段添加列别名,以匹配属性名。

<mapper namespace="com.mylifes1110.dao.ManagerDao">
    <select id="selectManagerByIdAndPwd" resultType="com.mylifes1110.bean.Manager">
        SELECT mgr_id AS id , mgr_name AS username , mgr_pwd AS password
        FROM t_managers
        WHERE mgr_id = #{id} AND mgr_pwd = #{pwd}
    </select>
</mapper>
3.2.1.2 解决方式二:结果映射

使用<resultMap id=“别名” type=“实体类对象名” >标签来映射,匹配列名与属性名。

注意: property设置属性,column设置别名

<mapper namespace="com.mylifes1110.dao.ManagerDao">

    <!--定义resultMap标签-->
    <resultMap id="managerResultMap" type="com.mylifes1110.bean.Manager">
      	<!--关联主键与列名-->
        <id property="id" column="mgr_id" />
      
      	<!--关联属性与列名-->
        <result property="username" column="mgr_name" />
        <result property="password" column="mgr_pwd" />
    </resultMap>
  
     <!--使用resultMap作为ORM映射依据-->
    <select id="selectAllManagers" resultMap="managerResultMap">
        SELECT mgr_id , mgr_name , mgr_pwd
        FROM t_managers
    </select>
</mapper>
3.2.2 MyBatis处理关联关系
3.2.2.1 映射关系

实体间的关系: 关联关系(拥有 has、属于 belong)

  • OneToOne: 一对一关系(Passenger— Passport)

  • OneToMany: 一对多关系(Employee — Department)

  • ManyToMany: 多对多关系(Student — Subject)

3.2.2.2 映射表分析
Table建立外键关系
008
Entity添加关系属性
009_2
Mapper中将属性与列名对应
010
3.2.2.3 映射关系应用

标签说明

  • 结果映射标签: <resultMap id=“结果映射别名” type=“实体类对象”>

    • 双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用<ResultMap >完成多表映射
  • id映射标签: <id property=“ID名” column=“ID别名” />

  • 属性映射标签: <result property=“属性名” column=“别名” />

  • 映射单一对象标签: <association property=“对象属性名” javaType=“实体类包含的单一对象”>

    • 持有对象关系属性使用<association>标签来完成映射,此标签是写在<resultMap>标签内
  • 映射集合对象标签: <collection property=“集合属性名” ofType=“集合泛型内单一对象”>

    • 持有集合关系属性,使用<collection>标签来完成映射,此标签是写在<resultMap>标签内
  • 查询标签: <select id=“接口方法名” resultMap=“结果映射别名”>

    • 查询标签中resultMap属性内填入的是结果映射别名
3.2.2.4 一对一关系应用

在一对一关系中,如果实体类中包含需要查询的对象,则需要在<resultMap>标签内添加<association>标签来映射实体类中的单一对象

创建表

// 旅客表
create table passenger
(
    id       int auto_increment
        primary key,
    name     varchar(50) null,
    sex      tinyint     null,
    birthday date        null
);

// 护照表
create table passport
(
    id           int auto_increment
        primary key,
    nationlity   varchar(100) null,
    expire       date         null,
    passenger_id int          null
);

创建实体类对象

package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Passport {
    private int id;
    private String nationlity;
    private Date expire;
    private int passengerId;
    private Passenger passenger;

    public Passport(String nationlity, Date expire, int passengerId) {
        this.nationlity = nationlity;
        this.expire = expire;
        this.passengerId = passengerId;
    }
}

package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Passenger {
    private int id;
    private String name;
    private boolean sex;
    private Date birthday;
    private Passport passport;

    public Passenger(String name, boolean sex, Date birthday) {
        this.name = name;
        this.sex = sex;
        this.birthday = birthday;
    }
}

创建接口

package com.mylifes1110.dao;

import com.mylifes1110.bean.Passenger;
import org.apache.ibatis.annotations.Param;

public interface PassengerDao {
    Passenger selectPassengerById(@Param("id") int id);
}

创建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">

<!--namespace:所需实现的接口全限定名-->
<mapper namespace="com.mylifes1110.dao.PassengerDao">
    <!--封装结果映射-->
    <resultMap id="passenger_passport" type="com.mylifes1110.bean.Passenger">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
    	<!--封装类中对象-->
        <association property="passport" javaType="com.mylifes1110.bean.Passport">
            <id column="id" property="id"></id>
            <result column="nationlity" property="nationlity"></result>
            <result column="expire" property="expire"></result>
        </association>
    </resultMap>

<!--查询-->
<select id="selectPassengerById" resultMap="passenger_passport">
    select passenger.id,
       passenger.name,
       passenger.sex,
       passenger.birthday,
       passport.id pid,
       passport.nationlity,
       passport.expire,
       passport.passenger_id
from passenger
         join passport on passenger.id = passport.passenger_id
where passenger.id = #{id}
</select>
</mapper>

注册Mapper

<!--Mapper注册-->
<mappers>
	<mapper resource="mappers/PassengerMapper.xml"/>
<mappers>

测试类

@Test
public void selectPassengerById() {
    PassengerDao passengerDao = MyBatisUtils.getMapper(PassengerDao.class);
    System.out.println(passengerDao.selectPassengerById(1));
}
3.2.2.5 一对多关系应用

在一对多关系中,可能会出现查询一个对象(实体)的信息,还有可能会出现查询好多对象(实体集合)的信息。那么我们可以判断在查询一个对象的信息是可以使用<association>标签,而查询集合对象的信息可以使用<collection>标签

创建表

// 部门
create table department
(
    id       int auto_increment
        primary key,
    name     varchar(100) null,
    location varchar(200) null
);

// 员工
create table employee
(
    id      int auto_increment
        primary key,
    name    varchar(100) null,
    salary  double       null,
    dept_id int          null
);

实体类

package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private int id;
    private String name;
    private double salary;
    private int deptId;
    private List<Department> departments;
}

package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private int id;
    private String name;
    private String location;
    private Employee employee;
}

Dao层接口

package com.mylifes1110.dao;

import com.mylifes1110.bean.Employee;

import java.util.List;

public interface EmployeeDao {
    List<Employee> selectAllEmployee();
}

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">

<!--namespace:所需实现的接口全限定名-->
<mapper namespace="com.mylifes1110.dao.EmployeeDao">
    <resultMap id="selectEmployeeAll" type="com.mylifes1110.bean.Employee">
        <id property="id" column="eid"></id>
        <result property="name" column="ename"></result>
        <result property="salary" column="salary"></result>
        <collection property="departments" ofType="com.mylifes1110.bean.Department">
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
            <result property="location" column="location"></result>
        </collection>
    </resultMap>
<select id="selectAllEmployee" resultMap="selectEmployeeAll">
    select department.id, department.name, department.location, employee.id eid, employee.name ename, employee.salary
from department
         join employee on department.id = employee.dept_id;
</select>
</mapper>

测试类

@Test
public void selectAllEmployee() {
    EmployeeDao employeeDao = MyBatisUtils.getMapper(EmployeeDao.class);
    System.out.println(employeeDao.selectAllEmployee());
}
3.2.2.6 多对多关系应用

关于多对多在映射中还算是有挑战性的,接下来是一个三表联查来实现多对多映射

创建表

// 科目表
create table subject(
   id int primary key auto_increment,
   name varchar(100),
   grade int
)default charset =utf8;

// 学生表
create table student(
    id int primary key  auto_increment,
    name varchar(50),
    sex tinyint
)default charset =utf8;

// 学生和科目的中间管理表
create table student_subject(
  student_id int references student(id),
  subject_id int references subject(id),
  primary key (student_id,subject_id)
)default charset =utf8;
建立第三张关系表
011_2

实体类

package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int id;
    private String name;
    private boolean sex;
    private List<Subject> subjects;
}

package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Subject {
    private int id;
    private String name;
    private int grade;
    private List<Student> students;
}

Dao层接口

package com.mylifes1110.dao;

import com.mylifes1110.bean.Subject;

import java.util.List;

public interface StudentDao {
    List<Subject> selectAllStudent();
}

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">

<!--namespace = 所需实现的接口全限定名-->
<mapper namespace="com.mylifes1110.dao.StudentDao">
<resultMap id="subject_student" type="com.mylifes1110.bean.Subject">
    <id property="id" column="id"></id>
    <result property="name" column="name"></result>
    <result property="grade" column="grade"></result>
<collection property="students" ofType="com.mylifes1110.bean.Student">
    <id property="id" column="stuid"></id>
    <result property="name" column="stuname"></result>
    <result property="sex" column="sex"></result>
</collection>
</resultMap>
<select id="selectAllStudent" resultMap="subject_student">
select student.id stuid, student.name stuname, student.sex, subject.id, subject.name, subject.grade
from student
         join student_subject on student.id = student_subject.student_id
         join subject
              on student_subject.subject_id = subject.id;
</select>
</mapper>

测试类

@Test
public void selectAllStudent() {
    StudentDao studentDao = MyBatisUtils.getMapper(StudentDao.class);
    System.out.println(studentDao.selectAllStudent());
}

3.2.3 动态SQL

3.2.3.1 动态SQL概述

MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果

动态 SQL 是 mybatis 的强大特性之一,也是它优于其他 ORM 框架的一个重要原因。mybatis 在对 sql 语句进行预编译之前,会对 sql 进行动态解析,解析为一个 BoundSql 对象,也是在此处对动态 SQL 进行处理的。

3.2.3.2 sql预编译

sql 预编译指的是数据库驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。

为什么需要预编译

JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译

  1. 预编译阶段可以优化 sql 的执行
    预编译之后的 sql 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的sql,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。
  2. 预编译语句对象可以重复利用
    把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。

mybatis 默认情况下,将对所有的 sql 进行预编译。

3.2.3.3 动态sql 的动态解析

mybatis 在调用 connection 进行 sql 预编译之前,会对sql语句进行动态解析,动态解析主要包含如下的功能:

  • 占位符的处理
  • 动态sql的处理
  • 参数类型校验
3.2.3.4 动态sql解析阶段的不同表现

在MyBatis 的映射配置文件中,动态传递参数有两种方式:

  • #{} 占位符

  • ${} 拼接符

在动态 SQL 解析阶段, #{ } 和 ${ } 会有不同的表现

#{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符

例如,sqlMap 中如下的 sql 语句

select * from user where name = #{name};

解析为:

select * from user where name = ?;

一个 #{ } 被解析为一个参数占位符 ?

${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换

例如,sqlMap 中如下的 sql

select * from user where name = ${name};

当我们传递的参数为 “hello” 时,上述 sql 的解析为:

select * from user where name = "hello";

预编译之前的 SQL 语句已经不包含变量 name 了。

#{} 和 ${} 的区别

#{} 就是编译好SQL语句再取值.
${} 就是取值以后再去编译SQL语句.

#{} 为参数占位符 ?,即sql 预编译

${} 为字符串替换,即 sql 拼接

#{}:动态解析 -> 预编译 -> 执行

${}:动态解析 -> 编译 -> 执行

#{} 的变量替换是在DBMS 中

${} 的变量替换是在 DBMS 外

变量替换后,#{} 对应的变量自动加上单引号 ‘’

变量替换后,${} 对应的变量不会加上单引号 ‘’

#{} 能防止sql 注入

${} 不能防止sql 注入

#{} 和 ${} 在使用中的技巧和建议
  • 不论是单个参数,还是多个参数,一律都建议使用注解@Param("")

  • 能用 #{} 的地方就用 #{},不用或少用 ${}

  • 表名作参数时,必须用${} ${}。如:select * from ${tableName}

  • order by 时,必须用 ${}。如:select * from t_user order by ${columnName}

  • 使用 ${} 时,要注意何时加或不加单引号,即 和 ′ {} 和 ' {}’

3.2.3.5 sql标签

关于<sql>标签是用来抽取多个相同重复sql片段的标签,抽取后并书写此标签内,随后再为<sql id=“别名”>标签内取一个别名。当需要引入重复sql片段时,使用<include refid=“sql标签别名”>标签来完成

<sql id="selectSql">
    select id, username, password, gender, birth
</sql>
<!--sql标签使用-->
<select id="selectUserById1" resultType="com.mylifes1110.bean.User">
    <include refid="selectSql"></include>
    from tb_user where id = #{id}
</select>
3.2.3.5.1 if标签

关于<if>标签,它适用于是一个场景。我们的以前的增删改查一旦写了条件就写死了,导致很不灵活。比如:在select查询语句中定义了多个查询条件,而这个当查询的时候必须写入这几个查询条件,缺一个都不行。当项目需求改了时怎么办呢?只能需要修改代码来实现,而<if>标签能够动态的来解除多条件的限制,当我们考虑好这几个条件是可以实现动态条件查询的时候,就可以使用<if>标签来分配

// 如果username不为空并且不为空字符串则username的值改为新值

<update id="updateUserById1"> 
    update tb_user set
    <if test="username != null and username != ''">username = #{username}</if>
    <if test="password != null">password = #{password}</if>
    <if test="gender != null">gender = #{gender}</if>
    <if test="birth != null">birth = #{birth}</if>
    where id = #{id}
</update>

该段代码的意思就是我指定<if>标签包裹的哪一个属性都可以修改,而不是写死的只要修改就必须所有的值都传。那我们看一下测试类,使用<if>标签是怎么动态传值的

@Test
public void updateUserById1() {
    User user = new User(1, "Z", null, null, null);
    UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
    System.out.println(userDao.updateUserById1(user));
}

测试类反映如果我们想改的值就可以直接传入新值修改,不修改的值就只需要传放入null即可

3.2.3.5.2 where标签

<where>标签用于补充Sql中使用的where关键,并且可以自动忽略前缀,比如:and和or前缀

<select id="selectBookByCondition" resultType="com.mylifes1110.bean.Book">
    SELECT id , name , author , publish , sort
    FROM t_books
    <where> 
    	1 = 1
        <if test="id != null">
            id = #{id}
        </if>

        <if test="name != null">
            and name = #{name}
        </if>

        <if test="author != null">
            and author = #{author}
        </if>

        <if test="publish != null">
            and publish = #{publish}
        </if>

        <if test="sort != null">
            and sort = #{sort}
        </if>
    </where>
</select>

该代码有一种问题场景存在,我们查询的时候不查询id,把id传入null。下一个name前面会多出一个and来干扰我们的查询,普通的情况下使用就会报错。而使用<where>标签来包裹查询条件,既补充了where关键字,又忽略了这种情况下而多出的and关键字。or关键字亦是如此!

3.2.3.5.3 set标签

<set>标签用于补充Sql语句中使用的set关键字,并且可以自动忽略后缀,比如:,(逗号)

<update id="updateBookByCondition">
    UPDATE t_books
    <set>
        <if test="name != null">
            name = #{name} ,
        </if>

        <if test="author != null">
            author = #{author} ,
        </if>

        <if test="publish != null">
            publish = #{publish} ,
        </if>

        <if test="sort != null">
            sort = #{sort} ,
        </if>
    </set>
    WHERE id = #{id}
</update>

同样的思想,该代码也会有一种问题场景存在。我们在修改的时候,修改的值正常都是所有值都传入的,即使不修改的值,也是需要传值的,这样就导致了一种写死的状态,不能动态的修改自己所需要的值。当我们引入<set>标签时,它可以补充set关键字并实现了动态修改自己想要的值。但是第一个值修改时,第二个值不修改的时候,后面就会多出来一个逗号来干扰修改,其<set>标签就解决了此问题,忽略了后缀 , (逗号)

3.2.3.5.4 trim标签

<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">是一个重量级标签,它代替<where> 和<set>(前缀、后缀、前缀覆盖、后缀覆盖),并具有它们的所有功能

替换<where>标签

<select id="selectBookByCondition" resultType="com.mylifes1110.bean.Book">
		SELECT id,name,author,publish,sort
    FROM t_books
    <!-- 增加WHERE前缀,自动忽略前缀 AND 或 OR -->
    <trim prefix="WHERE" prefixOverrides="AND |OR "> 
        <if test="id != null">
            and id = #{id}
        </if>

        <if test="name != null">
            and name = #{name}
        </if>

        <if test="author != null">
            and author = #{author}
        </if>

        <if test="publish != null">
            and publish = #{publish}
        </if>

        <if test="sort != null">
            and sort = #{sort}
        </if>
	</trim>
</select>

替换<set>标签

<update id="updateBookByCondition">
    UPDATE t_books
	<!-- 增加SET前缀,自动忽略最后的 逗号 -->
    <trim prefix="SET" suffixOverrides=","> 
        <if test="name != null">
            name = #{name} ,
        </if>

        <if test="author != null">
            author = #{author} ,
        </if>

        <if test="publish != null">
            publish = #{publish} ,
        </if>

        <if test="sort != null">
            sort = #{sort}
        </if>
    </trim>
	WHERE id = #{id}
</update>
3.2.3.5.5 foreach标签

<foreach>标签可以用来实现批量插入数据和查询数据

参数描述取值
collection容器类型list、array、map
open起始符(
close结束符)
separator分隔符,
index下标号从0开始,依次递增
item当前项任意名称(循环中通过 #{任意名称} 表达式访问)
3.2.3.5.6 动态sql标签的综合运用(性能篇)
批量新增
<insert id="addPersons">
  insert into person(username,email,gender) VALUES
   <foreach collection="persons" item="person" separator=";">
     (#{person.username},#{person.email},#{person.gender})
    </foreach>
 </insert>
批量更新

case when then end

<update id="updateBatch">
    update t_calendar_extend
    <trim prefix="set" suffixOverrides=",">
        <trim prefix="modify_time = case index" suffix="end,">
            <foreach collection="list" item="item">
                when #{item.index} then #{item.modifyTime}
            </foreach>
        </trim>
        <trim prefix="user_type = case index" suffix="end">
            <foreach collection="list" item="item">
                when #{item.index} then #{item.type}
            </foreach>
        </trim>
    </trim>
    <where>
        index in (
        <foreach collection="list" separator="," item="item">
            #{item.index}
        </foreach>
        )
    </where>
</update>

update t_calendar_extend
set
modify_time =
case index
when ? then ?
when ? then ?
when ? then ? end,
user_type =
case index
when ? then ?
when ? then ?
when ? then ? end
where index in (?,?,?)

update tb_class
set
modify_time =
case id
when ? then ? 
when ? then ?
when ? then ? end,
user_type =
case id
when ? then ?
when ? then ?
when ? then ? end
where id in (?,?,?)

这种方式实现的批量更新操作效率很低,而且,当更新的字段很多时,SQL语句会特别长。

这里,根据index值来更新modify_time 和user_type的值,有几个字段,就要遍历几遍,效率很低。

foreach成多条sql
<update id="updateBatch"  parameterType="java.util.List">  
    <foreach collection="list" item="item" index="index" open="" close="" separator=";">
        update tableName
        <set>
            name=${item.name},
            name2=${item.name2}
        </set>
        where id = ${item.id}
    </foreach>      
</update>

这种方式最简单,就是用foreach组装成多条update语句,但Mybatis映射文件中的sql语句默认是不支持以" ; " 结尾的,也就是不支持多条sql语句的执行。所以需要在连接mysql的url上加 &allowMultiQueries=true 这个才可以执行。

ON DUPLICATE KEY UPDATE
<update id="updateBatch">
    insert into t_output_calendar (index, 
      cal_date, user_type, create_time, 
      modify_time, delete_flag
      )
    values
    <foreach collection="list" item="item" index="index"
        separator=",">
        (
        #{item.index,jdbcType=INTEGER}, 
        #{item.calDate,jdbcType=TIMESTAMP}, 
        #{item.type,jdbcType=TINYINT}, 
        #{item.createTime,jdbcType=TIMESTAMP}, 
        #{item.modifyTime,jdbcType=TIMESTAMP}, 
        #{item.deleteFlag,jdbcType=TINYINT}
        )
    </foreach>
    ON DUPLICATE KEY UPDATE modify_time = VALUES(modify_time), user_type = VALUES(user_type);
</update>

MYSQL中的ON DUPLICATE KEY UPDATE,是基于主键(PRIMARY KEY)或唯一索引(UNIQUE INDEX)使用的。

如果已存在该唯一标示或主键就更新,如果不存在该唯一标示或主键则作为新行插入。

批量删除
<delete id="deleteMoreEmp" parameterType="int[]">
		<!-- delete from emp where empno in(7789,7790) -->
		<!-- forEach : 用来循环 collection : 用来指定循环的数据的类型 可以填的值有:array,list,map item 
			: 循环中为每个循环的数据指定一个别名 index : 循环中循环的下标 open : 开始 close : 结束 separator : 数组中元素之间的分隔符 -->
    delete from emp where empno in
    <foreach collection="array" item="arr" index="no" open="(" separator="," close=")">
        #{arr}
    </foreach>
</delete>

3.2.4 缓存
3.2.4.1 缓存的重要性

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问

无缓存:用户在访问相同数据时,需要发起多次对数据库的直接访问,导致产生大量IO、读写硬盘的操作,效率低下
012
有缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时,直接访问缓存,减少IO、硬盘读写次数、提高效率
013
3.2.4.2 一级缓存

SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中

注意: 无需任何配置,默认开启一级缓存

测试一级缓存

@Test
public void testMapperCache1(){
  	SqlSession sqlSession1 = MyBatisUtils.getSession();
  
  	BookDao bookDao1 = sqlSession1.getMapper(BookDao.class);
	//第一次查询,从数据库获取,并将查询结果存入一级缓存
  	bookDao1.selectBookByCondition(new Book(1));
    //第二次查询,从一级缓存获取
    bookDao1.selectBookByCondition(new Book(1));

  	sqlSession1.close(); 
}

测试后,你查看MyBatis日志,会发现结果第一次查询触发了查询。而第二次的结果是从缓存中拿出来的,并没有触发再次查询

3.2.4.3 二级缓存

SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中

注意: 二级缓存设置后,在sqlSession.commit()或者sqlSession.close()之后生效并且JavaBean必须实现序列化。三者缺一不可!

3.2.4.4 开启全局缓存

<settings>是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,而该标签是放在<properties>标签之后,别名标签之前的

注意: mybaits-config.xml中开启全局缓存(默认开启)

<configuration>
	<properties .../>
  	
  	<!-- 注意书写位置 -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  
  	<typeAliases></typeAliases>
</configuration>
3.2.4.5 指定Mapper缓存

我们可以通过一个简单的<cache />标签来指定某一个Mapper开启缓存

Mapper.xml

<mapper namespace="com.mylifes1110.dao.UserDao">
    <!-- 指定当前Mapper缓存 -->
    <cache /> 

    <select id="selectUserById" resultType="com.mylifes1110.bean.User">
        SELECT * FROM t_user
    </select>
</mapper>

测试类

@Test
public void testMapperCache(){
  	SqlSession sqlSession1 = MyBatisUtils.getSession();
  	BookDao bookDao1 = sqlSession1.getMapper(BookDao.class);
  	bookDao1.selectBookByCondition(new Book());
	//必须关闭SqlSession才可缓存数据
  	sqlSession1.close(); 

  	SqlSession sqlSession2 = MyBatisUtils.getSession();
  	BookDao bookDao2 = sqlSession2.getMapper(BookDao.class);
  	bookDao2.selectBookByCondition(new Book());
  	sqlSession2.close(); //缓存击中
}

如果你使用了MyBatis日志依赖,可以查看到这么一条信息。这条日志反映了从该数据被缓存后,被拉取该缓存的几率为百分之50,也就是一共查询了两次,缓存中查出来了一次

缓存击中
image-20200711203246412
3.2.4.6 缓存清空并重新缓存

缓存清空: 从数据被缓存,随后数据被增删改操作后缓存即为清空

重新缓存: 增删改操作过后,当重新被查询该数据后,此时缓存未击中便重新缓存

@Test
public void testMapperCache2(){
  	SqlSession sqlSession1 = MyBatisUtils.getSession();
  	UserDao UserDao1 = sqlSession1.getMapper(UserDao.class);
  	UserDao1.selectUserByUser(new User());
  	sqlSession1.close(); //必须关闭SqlSession才可缓存数据

    SqlSession sqlSession3 = MyBatisUtils.getSession();
    UserDao bookDao3 = sqlSession3.getMapper(UserDao.class);
    UserDao3.deleteUserById(102);
    sqlSession3.commit(); //DML成功,数据发生变化,缓存清空
    sqlSession3.close();
  
  	SqlSession sqlSession2 = MyBatisUtils.getSession();
  	UserDao userDao2 = sqlSession2.getMapper(UserDao.class);
  	UserDao2.selectUserByUser(new User());
  	sqlSession2.close(); //缓存未击中,重新查询数据库、重新缓存
}
3.3 怎样用好(与其他工具的结合)
3.3.1 Durid连接池
3.3.1.1 Durid连接池概述

Druid 是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和 SQL 解析器组成。该项目主要是为了扩展 JDBC 的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计 SQL 信息、SQL 性能收集、SQL 注入检查、SQL 翻译等,程序员可以通过定制来实现自己需要的功能。

3.3.1.2 不同连接池对比

测试执行申请归还连接 1,000,000(一百万)次总耗时性能对比。

测试环境
环境版本
OSOS X 10.8.2
CPUIntel i7 2GHz 4 Core
JVMJava Version 1.7.0_05
基准测试结果对比
JDBC-Conn Pool1 Thread2 threads5 threads10 threads20 threads50 threads
Druid8981,1911,3241,3621,3251,459
tomcat-jdbc1,2691,3782,0292,1031,8792,025
DBCP2,3245,0555,4465,4715,5245,415
BoneCP3,7383,1503,1945,68111,01823,125
jboss-datasource4,3772,9883,6803,98032,70837,742
C3P010,84113,63710,68211,05514,49720,351
Proxool16,33716,18718,310(Ex)25,94533,706(Ex)39,501 (Ex)
测试结论
  • Druid 是性能最好的数据库连接池,tomcat-jdbc 和 druid 性能接近。
  • Proxool 在激烈并发时会抛异常,不适用。
  • C3P0 和 Proxool 都相当慢,影响 sql 执行效率。
  • BoneCP 性能并不优越,采用 LinkedTransferQueue 并没有能够获得性能提升。
  • 除了 bonecp,其他的在 JDK 7 上跑得比 JDK 6 上快。
  • jboss-datasource 虽然稳定,但性能很糟糕。
3.3.1.3 Durid连接的开发步骤
引入依赖

在Maven项目中的pom.xml文件中引入阿里巴巴的Durid连接池

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
替换MyBatis中的默认数据源

MyBatis中是有默认数据源的,如果我们想用其他连接池,需要替换默认数据源

操作: MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源

package com.mylifes1110.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;

public class MyDruidDataSource extends PooledDataSourceFactory {

    public MyDruidDataSource() {
        this.dataSource = new DruidDataSource();
    }
}
配置mybatis-config.xml

修改mybatis-config.xml文件中连接池相关配置

注意: < property name=“属性名” />属性名必须与com.alibaba.druid.pool.DruidAbstractDataSource中一致

<!--连接池-->
<dataSource type="com.mylifes1110.utils.MyDruidDataSourceFactory"><!--数据源工厂-->
    <property name="driverClass" value="${driver}"/>
    <property name="jdbcUrl" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/> 
</dataSource>
3.3.2 分页插件(PageHelper)
3.3.2.1 PageHelper概述

PageHelper是一个适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作

3.3.2.2 官网

官方网站:https://pagehelper.github.io/

下载地址:https://github.com/pagehelper/Mybatis-PageHelper

3.3.2.3 PageHelper开发步骤
引入依赖

在Maven项目中的pom.xml文件中引入PageHelper依赖

<dependency>
		<groupId>com.github.pagehelper</groupId>
		<artifactId>pagehelper</artifactId>
		<version>5.1.10</version>
</dependency>
配置mybatis-config.xml

在核心配置文件中的别名标签<typeAliases>和<environments>内添加<plugins>标签并引入PageHelper配置

<configuration>
  	<typeAliases></typeAliases>
  
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
  
  	<environments>...</environments>
</configuration>
3.3.2.4 PageInfo对象

PageInfo对象是PageHelper插件中的一个类,里面包含了如下页面中所需要的所有相关数据

PageInfo结构图
image-20200116145234073
3.3.2.5 注意事项
  • 只有在PageHelper.startPage()方法之后的第一个查询会有执行分页
  • 分页插件 不支持带有“for update” 的查询语句
  • 分页插件不支持“嵌套查询”,由于嵌套结果方式会导致结果集被折叠,所以无法保证分页结果数量正确
3.3.2.6 分页查询
创建表
create table tb_user
(
    id       int auto_increment
        primary key,
    username varchar(30) null,
    password varchar(30) null,
    gender   char        null,
    birth    date        null
);
实体类
package com.mylifes1110.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private int id;
    private String username;
    private String password;
    private boolean gender;
    private Date birth;
}
Dao层
package com.mylifes1110.dao;

import com.mylifes1110.bean.User;

import java.util.List;

public interface UserDao {
    List<User> selectUserByPage();
}
datasource
package com.mylifes1110.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;

public class MyDruidDataSource extends PooledDataSourceFactory {

    public MyDruidDataSource(){
        this.dataSource = new DruidDataSource();
    }
}
Service层
// 接口
package com.mylifes1110.service;

import com.github.pagehelper.PageInfo;
import com.mylifes1110.bean.User;

public interface UserService {
    PageInfo<User> selectUserByPage(int pageNum, int pageSize);
}

package com.mylifes1110.service.impl;


// 实现类
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mylifes1110.bean.User;
import com.mylifes1110.dao.UserDao;
import com.mylifes1110.service.UserService;
import com.mylifes1110.utils.MyBatisUtil;

import java.util.List;

public class UserServiceImpl implements UserService {

    @Override
    public PageInfo<User> selectUserByPage(int pageNum, int pageSize) {
        UserDao userDao = MyBatisUtil.getMapper(UserDao.class);
        PageHelper.startPage(pageNum, pageSize);
        List<User> userList = userDao.selectUserByPage();
        MyBatisUtil.closeSession();
        return new PageInfo<User>(userList);
    }
}
工具类
package com.mylifes1110.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MyBatisUtil {
    //获得SqlSession工厂
    private static SqlSessionFactory factory;

    //创建ThreadLocal绑定当前线程中的SqlSession对象
    private static final ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();

    static {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获得连接(从tl中获得当前线程SqlSession)
    private static SqlSession openSession(){
        SqlSession session = tl.get();
        if(session == null){
            session = factory.openSession();
            tl.set(session);
        }
        return session;
    }

    // 每次返回一个新创建的sqlSession
    public static SqlSession getSession(){
        return factory.openSession();
    }

    //释放连接(释放当前线程中的SqlSession)
    public static void closeSession(){
        SqlSession session = tl.get();
        session.close();
        tl.remove();
    }

    //提交事务(提交当前线程中的SqlSession所管理的事务)
    public static void commit(){
        SqlSession session = openSession();
        session.commit();
        closeSession();
    }

    //回滚事务(回滚当前线程中的SqlSession所管理的事务)
    public static void rollback(){
        SqlSession session = openSession();
        session.rollback();
        closeSession();
    }

    //获得接口实现类对象
    public static <T extends Object> T getMapper(Class<T> clazz){
        SqlSession session = openSession();
        return session.getMapper(clazz);
    }
}
controller
package com.mylifes1110.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.github.pagehelper.PageInfo;

import com.mylifes1110.bean.User;
import com.mylifes1110.service.UserService;
import com.mylifes1110.service.impl.UserServiceImpl;

@WebServlet(
    name  = "UserServlet",
    value = "/user/page"
)
public class UserServlet extends HttpServlet {
    private UserService userService = new UserServiceImpl();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        int    pageNum     = 1;
        int    pageSize    = 5;
        String pageNumStr  = request.getParameter("pageNum");
        String pageSizeStr = request.getParameter("pageSize");

        if (pageNumStr != null) {
            pageNum = Integer.parseInt(pageNumStr);
        }

        if (pageSizeStr != null) {
            pageSize = Integer.parseInt(pageSizeStr);
        }

        PageInfo<User> pageInfo = userService.selectUserByPage(pageNum, pageSize);

        request.setAttribute("pageInfo", pageInfo);
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }
}
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">

<!--namespace = 所需实现的接口全限定名-->
<mapper namespace="com.mylifes1110.dao.UserDao">
    <select id="selectUserByPage" resultType="com.mylifes1110.bean.User">
        select id, username, password, gender, birth from tb_user
    </select>
</mapper>
index.jsp
<%--
  Created by IntelliJ IDEA.
  User: Ziph
  Date: 2020/7/8
  Time: 17:59
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<script src="${pageContext.request.contextPath}/js/jquery-2.1.0.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
      integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
        integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
        crossorigin="anonymous"></script>
<html>
<head>
    <title>index</title>
</head>
<body>
<table class="table table-striped table-bordered table-hover">
    <tr>
        <th>ID</th>
        <th>用户名</th>
        <th>密码</th>
        <th>性别</th>
        <th>出生日期</th>
    </tr>
    <c:forEach items="${pageInfo.list}" var="user">
        <tr>
            <td>${user.id}</td>
            <td>${user.username}</td>
            <td>${user.password}</td>
            <td>${user.gender}</td>
            <td>
                <fmt:formatDate value="${user.birth}" pattern="yyyy/MM/dd"/>
            </td>
        </tr>
    </c:forEach>
</table>
<div align="center">
    <nav aria-label="Page navigation">
        <ul class="pagination">
            <c:if test="${pageInfo.hasPreviousPage}">
                <li>
                    <a href="${pageContext.request.contextPath}/user/page?pageNum=${pageInfo.prePage}"
                       aria-label="Previous">
                        <span aria-hidden="true">&laquo;</span>
                    </a>
                </li>
            </c:if>
            <c:forEach begin="1" end="${pageInfo.pages}" var="i">
                <c:if test="${pageInfo.pageNum == i}">
                    <li class="active"><a href="${pageContext.request.contextPath}/user/page?pageNum=${i}">${i}</a>
                    </li>
                </c:if>
                <c:if test="${pageInfo.pageNum != i}">
                    <li><a href="${pageContext.request.contextPath}/user/page?pageNum=${i}">${i}</a></li>
                </c:if>
            </c:forEach>
            <c:if test="${pageInfo.hasNextPage}">
                <li>
                    <a href="${pageContext.request.contextPath}/user/page?pageNum=${pageInfo.nextPage}"
                       aria-label="Next">
                        <span aria-hidden="true">&raquo;</span>
                    </a>
                </li>
            </c:if>
        </ul>
    </nav>
</div>
</body>
</html>
pom.xml依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>paging</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--MySql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <!-- jstl 支持 -->
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <!-- servlet编译环境 -->
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <!-- jsp编译环境 -->
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
各种配置文件

jdbc.properties

#jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2004?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

log4j.properties

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.com.qf.mybatis.part1.basic=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--MyBatis配置-->
<configuration>
    <properties resource="jdbc.properties"/>
    <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!-- 定义实体的别名 -->
    <typeAliases>
        <!--<typeAlias type="com.qianfeng.entity.User" alias="User" />-->
        <package name="com.mylifes1110.bean"/>
    </typeAliases>
    <!--分页插件-->
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    <!--JDBC环境配置、选中默认环境-->
    <environments default="MySqlDB">
        <!--MySql数据库环境配置-->
        <environment id="MySqlDB">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <!--连接池-->
            <dataSource type="com.mylifes1110.datasource.MyDruidDataSource">
                <property name="driverClass" value="${jdbc.driver}"/>
                <!-- &转义&amp; -->
                <property name="jdbcUrl" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--Mapper注册-->
    <mappers>
        <!--注册Mapper文件的所在位置-->
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

3.4 MyBatis补充

3.4.1 MyBatis注解开发

在MyBaits中是可以通过注解来完成CRUD的

注意: 接口注解定义完毕后,需将接口全限定名注册到mybatis-config.xml的< mappers >中

经验: 注解模式属于硬编码到.java文件中,失去了使用配置文件外部修改的优势,可结合需求选用

<mappers>
    <!-- class:接口全限定名 -->
	<mapper class="com.mylifes1110.dao.UserMapper" />
</mappers>
3.4.1.1 查询
public interface UserMapper {
    @Select("SELECT * FROM t_users WHERE id = #{id}")
    public User selectUserById(Integer id);

    @Select("SELECT * FROM t_users WHERE id = #{id} AND password = #{pwd}")
    public User selectUserByIdAndPwd_annotation(@Param("id") Integer id, @Param("pwd") String password);
}
3.4.1.2 删除
@Delete(value = "DELETE FROM t_users WHERE id = #{id}")
public int deleteUser(Integer id);
3.4.1.3 修改
@Update("UPDATE t_users SET name = #{name} , password = #{password} , salary = #{salary} , birthday = #{birthday} WHERE id = #{id}")
public int updateUser(User user);
3.4.1.4 插入
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUser(User user);

@Options(useGeneratedKeys = true , keyProperty = "id") // 自增key,主键为id
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUserGeneratedKeys(User user);
3.4.2 MyBatis的别名与类型转换规则
  1. type-aliases-package 别名

    type-aliases-package: com.itcrazy.alanmall.order.dal.entity
    resultMap中的type 或者 statement中的parameterType 都可以被替换,比如
    <insert id="mqInsert" parameterType="MqMessage"> //com.itcrazy.alanmall.order.dal.entity中寻找MqMessage
    
  2. 自定义类型转换规则

    实现BaseTypeHandler

    插入值时加入typeHandler标签,添加路径

    返回值时在result 标签添加typeHandler标签

3.4.3 MyBatis处理关联关系-嵌套查询(了解)

思路: 查询部门信息时,及联查询所属的员工信息

  • DepartmentDao接口中定义selectDepartmentById,并实现Mapper
  • EmployeeDao接口中定义selectEmployeesByDeptId,并实现Mapper
  • 当selectDepartmentById被执行时,通过< collection >调用selectEmployeesByDeptId方法,并传入条件参数
3.4.3.1 主表查询

定义selectEmployeesByDeptId,并书写Mapper,实现根据部门ID查询员工信息

Dao层接口

public interface EmployeeDao {
    public List<Employee> selectEmployeeByDeptId(@Param("did") String did);
}

Mapper.xml

<mapper namespace="com.mylifes1110.dao.EmployeeDao">
    <!-- 根据部门编号查询所有员工 -->
    <select id="selectEmployeeById" resultType="Employee" >
        SELECT id,name,salary,dept_id 
      	FROM t_employees 
      	WHERE dept_id = #{did}
    </select>
</mapper>
3.4.3.2 级联调用

定义selectDepartmentById,并书写Mapper,实现根据部门ID查询部门信息,并及联查询该部门员工信息

Dao层接口

public interface DepartmentDao {
    public Department selectDepartmentById(@Param("id") String id);
}

Mapper.xml

<mapper namespace="com.mylifes1110.dao.DepartmentDao">
    <resultMap id="departmentResultMap" type="department">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="location" column="location" />
         <!-- column="传入目标方法的条件参数"  select="及联调用的查询目标"-->
        <collection property="emps" ofType="Employee" column="id" 
                    select="com.mylifes1110.dao.EmployeeDao.selectEmployeeByDeptId" />
    </resultMap>
    <select id="selectAllDepartments" resultMap="departmentResultMap">
        SELECT id , name , location
        FROM t_departments
        WHERE id = #{id}
    </select>
</mapper>
3.4.3.3 延迟加载
mybatis-config.xml中开启延迟加载

注意: 开启延迟加载后,如果不使用及联数据,则不会触发及联查询操作,有利于加快查询速度、节省内存资源

<settings>
	<!-- 开启延迟加载(默认false) -->
	<setting name="lazyLoadingEnabled" value="true"/> 
</settings>
3.4.3 常用标签解释
DATE_FORMAT(rrh.CREATE_TIME,"%Y-%m-%d") <![CDATA[ <= ]]> cast(#{timeEnd} as date)

DATE_FORMAT 日期格式转换

<![CDATA[ <= ]]> 转义符 把 [] 内容原样输出, 比进行解析

cast  转换类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值