Mybatis

Posted by Wh0ami-hy on June 30, 2022

1. Mybatis简介

MyBatis是对JDBC的封装

MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架

MyBatis支持的数据库类型包括:Oracle、MySQL、SQL Server、DB2

2. 搭建MyBatis

2.1. 开发环境

JDK:1.8

构建工具:maven 3.5.4

MySQL版本:MySQL 5.7

MyBatis版本:MyBatis 3.5.7

2.2. 创建maven工程

打包方式:jar

引入依赖

<dependencies>
	<!-- Mybatis核心 -->
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.5.7</version>
	</dependency>
	<!-- MySQL驱动 -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.3</version>
	</dependency>
    
    <!-- 为了实现使用注解代替实体Bean的getter and setter等..... -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2.3. 创建MyBatis的核心配置文件

习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,操作时可以直接复制、粘贴(了解即可) 核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息 核心配置文件存放的位置是src/main/resources目录下

<?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">  
<configuration>  
	<!--设置连接数据库的环境-->  
	<environments default="development">  
        <!--开发环境的配置-->
		<environment id="development">  
			<transactionManager type="JDBC"/>  
			<dataSource type="POOLED">  
				<property name="driver" value="com.mysql.cj.jdbc.Driver"/>  
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>  
				<property name="username" value="root"/>  
				<property name="password" value="123456"/>  
			</dataSource>  
		</environment>  
        <!--生产环境的配置-->
		<environment id="produce">  
			<transactionManager type="JDBC"/>  
			<dataSource type="POOLED">  
				<property name="driver" value="com.mysql.cj.jdbc.Driver"/>  
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>  
				<property name="username" value="root"/>  
				<property name="password" value="123456"/>  
			</dataSource>  
		</environment>  
	</environments>  
	<!--引入映射文件-->  
	<mappers>  
		<mapper resource="mappers/UserMapper.xml"/>  
	</mappers>  
   <!-- 全局参数 -->  
   <settings>  
       <!-- 使全局的映射器启用或禁用缓存 -->  
       <setting name="cacheEnabled"             value="true"   />  
       <!-- 允许JDBC 支持自动生成主键 -->  
       <setting name="useGeneratedKeys"         value="true"   />  
       <!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->  
       <setting name="defaultExecutorType"      value="SIMPLE" />  
   <!-- 指定 MyBatis 所用日志的具体实现 -->  
       <setting name="logImpl"                  value="SLF4J"  />  
       <!-- 使用驼峰命名法转换字段 -->  
   <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
   </settings>
</configuration>

2.4. 创建mapper接口

MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类

package com.offcn.mybatis.mapper;  
  
public interface UserMapper {  
	/**  
	* 添加用户信息  
	*/  
	int insertUser();  
}

2.5. 创建MyBatis的映射文件

相关概念:ORM(Object Relationship Mapping)对象关系映射。

  • 对象:Java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系
Java概念 数据库概念
属性 字段/列
对象 记录/行

映射文件的命名规则

  • 表所对应的实体类的类名+Mapper.xml
  • 例如:表user,映射的实体类为User,所对应的映射文件为UserMapper.xml
  • 因此一个映射文件对应一个实体类,对应一张表的操作
  • MyBatis映射文件用于编写SQL,访问以及操作表中的数据
  • MyBatis映射文件存放的位置目录必须和接口所在包一毛一样。  接口: com.hy.mapper.UserMapper.java ,目录: com/hy/mapper/UserMapper.xml

MyBatis中可以面向接口操作数据,要保证两个一致

  • mapper接口的全类名和映射文件的命名空间(namespace)保持一致
  • mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致

配置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="com.hy.mapper.UserMapper">
    <!--id表示接口当中定义的方法. resultType,表示数据库查询的结果.-->
    <select id="findAll" resultType="com.hy.entity.Student">
        select * from student;
    </select>
</mapper>

3. 核心配置文件详解(重点)

核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱): properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers

重点配置有:properties、settings、typeAliases、environments、mappers

Mybatis默认的事务管理器就是JDBC。连接池:POOLED

<?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">
<configuration>
    <!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
    <properties resource="jdbc.properties"></properties>
    <settings>
        <!--将表中字段的下划线自动转换为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.offcn.mybatis.bean"/>
    </typeAliases>
    <environments default="mysql_test">
        <environment id="mysql_test">
            <!--
            transactionManager:设置事务管理方式
            属性:
	            type:设置事务管理方式,type="JDBC|MANAGED"
	            type="JDBC":设置当前环境的事务管理都必须手动处理
	            type="MANAGED":设置事务被管理,例如spring中的AOP
            -->
            <transactionManager type="JDBC"/>
            <!--
            dataSource:设置数据源
            属性:
	            type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
	            type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
            -->
            <dataSource type="POOLED">
                <!--设置驱动类的全类名-->
                <property name="driver" value="${jdbc.driver}"/>
                <!--设置连接数据库的连接地址-->
                <property name="url" value="${jdbc.url}"/>
                <!--设置连接数据库的用户名-->
                <property name="username" value="${jdbc.username}"/>
                <!--设置连接数据库的密码-->
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <!-- <mapper resource="UserMapper.xml"/> -->
        <!--
        以包为单位,将包下所有的映射文件引入核心配置文件
        注意:
			1. 此方式必须保证mapper接口和mapper映射文件必须在相同的包下
			2. mapper接口要和mapper映射文件的名字一致
        -->
        <package name="com.offcn.mybatis.mapper"/>
    </mappers>
</configuration>

3.1. 动态配置属性值

<!-- 
	引入db.properties文件,此时可用 ${属性名} 的方式访问属性值
-->
    <properties resource="jdbc.properties"></properties><property name="driver" value="${jdbc.driver}"/>

3.2. 设置多个数据库环境

<!-- 
		   environments:设置多个连接数据库的环境
            default:设置默认使用的环境的id
            environment:设置具体的连接数据库的环境信息
            id:设置环境的唯一标识
--><environments default="mysql_test">
        <environment id="mysql_test">

3.3. 设置数据源

 <!--
            dataSource:设置数据源
	        type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
	        type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
 --><dataSource type="POOLED">
    <!--设置驱动类的全类名-->
    <property name="driver" value="${jdbc.driver}"/>
    <!--设置连接数据库的连接地址-->
    <property name="url" value="${jdbc.url}"/>
    <!--设置连接数据库的用户名-->
    <property name="username" value="${jdbc.username}"/>
    <!--设置连接数据库的密码-->
    <property name="password" value="${jdbc.password}"/>
</dataSource>

3.4. 类型别名

 <!--   
        typeAlias:给类起别名
        type:需要设置别名的类的全类名
        alias:设置的别名,且别名不区分大小写。若不设置此属性,该类型拥有默认的别名,即类名
--><typeAliases>
    <typeAlias alias="Author" type= "com.hy.Author"/>
</typeAliases>

<!--以包为单位,设置该包下所有的类都拥有默认的别名,即类名,且不区分大小写--><package name="com.hy.entity"/>

3.5. 引入映射文件

<!-- 引入单个映射文件
	<mapper resource="UserMapper.xml"/> 
		或
	<mapper class="com.hy.UserMapper"/>  注解和xml可同时生效
--><mapper resource="UserMapper.xml"/> 

<!--
     以包为单位,将包下所有的映射文件引入核心配置文件
   注意:
   MyBatis映射文件存放的位置目录必须和接口所在包一毛一样
   mapper接口要和mapper映射文件的名字一致
   接口: com.hy.mapper.UserMapper.java 
   目录: com/hy/mapper/UserMapper.xml
-->
如:
<mappers>
    <package name="com.hy.mapper"/>
</mappers>

3.6. 设置

<settings>
    <!--
	可以在查询表中数据时,自动将_类型的字段名转换为驼峰,例如:字段名user_name,此时字段名就会转换为userName
	-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

3.7. 生命周期

这三个组件共同构成了 MyBatis 的核心机制,用于实现数据库操作的配置、创建和执行。SqlSessionFactoryBuilder 用于创建 SqlSessionFactory,而 SqlSessionFactory 用于创建 SqlSession,最终通过 SqlSession 执行具体的数据库操作

SqlSessionFactoryBuilder

读取配置文件或配置对象,并根据这些配置创建一个SqlSessionFactory 实例

SqlSessionFactory

SqlSessionFactory 的创建是一个耗费资源的过程,因此在应用程序启动时通常只会创建一个 SqlSessionFactory 实例,并在整个应用程序生命周期中重复使用。SqlSessionFactory 可以根据配置信息创建和管理数据库连接,同时也负责加载映射文件或注解,并将其转化为可执行的 SQL 语句

SqISession

SqlSession 是与数据库交互的主要接口。它提供了执行 SQL 语句、管理事务和与映射器(Mapper)交互的方法。通过 SqlSessionFactory 的 openSession() 方法可以创建 SqlSession 实例。每个数据库操作通常需要一个新的 SqlSession 实例。完成数据库操作后,应该及时关闭 SqlSession,以释放相关资源

4. MyBatis的CRUD

4.1. 模糊查询的三种姿势

  • concat函数拼接%
  • 手动拼接%%
  • 慎用 ${}
select * from t_user where username like concat('%',#{mohu},'%') 
select * from t_user where username like '%${mohu}%'
select * from t_user where username like "%"#{mohu}"%"

4.2. 批量操作

<update id="deleteByFlag" parameterType="String">  
    update dorm_electric set del_flag = 1  
    where electric_id in  
    <foreach item="electricId" collection="array" separator=",">  
        #{electricId}  
    </foreach>  
</update>

5. Mapper XML文件中的参数传递

获取参数值必须使用 #{}

${}:静态sql,本质就是字符串拼接,有sql注入风险,不会自动添加单引号

#{}:预编译sql,本质就是占位符赋值,用?占位,会自动添加单引号

<select id="getUserById" parameterType="long" resultType="com.example.User">
    SELECT * FROM user WHERE id = #{id}
</select>

对应的mapper方法

User getUserById(long id);

SQL标签中的parameterType属性值是SQL语句中需要接收的参数的类型。可以是类型的完全限定名或别名,如int 或 java.lang.Integerhashmap 或 java.util.HashMap,可以是实体类名

5.1. 单个参数

若mapper接口中的方法参数为单个时,可以使用${}、#{}获取参数的值,注意${}需要手动加单引号(因为本质是字符串拼接)

  • 基本数据类型(如intlongString等)。
  • 包装类(如IntegerLongString等)。
  • 自定义Java对象。

5.2. 多个参数

若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中

默认以arg0,arg1...为键,以参数为值
或
以param1,param2...为键,以参数为值

可以使用${}、#{}访问map集合的键来获取相对应的值,注意${}需要手动加单引号。使用arg或者param都行,要注意的是,arg是从arg0开始的,param是从param1开始的

也可以在对应的mapper方法中使用@Param注解给每个参数指定名称,在XML中使用#{指定名称}来引用参数。

<update id="updateUser" parameterType="map">
    UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
</update>

对应的mapper方法

void updateUser(@Param("id") long id, @Param("name") String name, @Param("age") int age);

5.3. 参数对象

将参数封装为一个Java对象,并指定对象的类型

<insert id="insertUser" parameterType="com.example.User">
    INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>

对应的mapper方法

void insertUser(User user);

5.4. Map类型

使用java.util.Map作为参数类型,在XML中使用#{key}来引用Map中的值。

<select id="getUserById" parameterType="java.util.Map" resultType="com.example.User">
    SELECT * FROM user WHERE id = #{id}
</select>

对应的mapper方法

User getUserById(Map<String, Object> params);

5.5. List类型

使用java.util.List作为参数类型,在XML中通过遍历来使用List中的值

<select id="getUsersByIdList" parameterType="java.util.List" resultType="com.example.User">
    SELECT * FROM user WHERE id IN
    <foreach collection="list" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

对应的mapper方法

List<User> getUsersByIdList(List<Long> idList);

6. Mapper XML文件中执行结果的映射方式

MyBatis中xml标签的返回值

  • select 返回查询结果,返回的结果是多种多样的
  • update 返回更新的记录的条数
  • insert 返回插入的记录的条数
  • delete 返回删除的记录的条数

6.1. resultType映射

  • 返回结果是基本类型:resultType=基本类型
  • 返回结果是List类型:resultType=List中元素的类型
  • 返回结果是Map类型:resultType =map(适用于单条记录和多条记录)

如果返回值为map单条记录,比如

<select id="getUser" resultType="java.util.Map">
    SELECT * FROM users WHERE id = #{userId}
</select>

如果返回值为map多条记录,比如

<select id="getCars" resultType="java.util.Map">
    SELECT * FROM cars
</select>

通过使用@MapKey注解( 在mapper接口的方法上添加),可以在返回多条记录的Map时,指定自定义的属性作为键,提供更灵活的结果映射方式

@Select("SELECT * FROM hotels")
@MapKey("hotelName")
Map<String, Hotel> getHotels();

在上述代码中,查询结果中的hotelName列将被用作Map的键,而Hotel对象将作为对应的值。最终返回的Map对象的键将是hotelName的值,值将是对应的Hotel对象。

6.2. resultMap映射

resultMap 用于复杂的结果映射场景,可以定义多个映射规则,支持嵌套查询和关联关系的映射。resultMap 需要在映射文件中定义,并且可以重复使用

<!-- resultMap 结果集映射
	 id:表示自定义映射的唯一标识,不能重复
	 type:查询的数据要映射的实体类的类型  
-->
<resultMap id="empResultMap" type="Emp">
    <!--column数据库中的字段,property实体类中的属性-->
	<result property="id" column="eid"></result>
	<result property="pwd" column="password"></result>
</resultMap>

<!--select标签中resultMap属性的值为前面 resultMap标签中id的值-->
<select id="getAllEmp" resultMap="empResultMap">
	select * from t_emp;
</select>

6.2.1. 多对一映射处理

查询学生信息以及学生所对应的老师信息,多个学生可能对应同一个老师

思路:查询所有学生信息,再根据查出来的学生的tid,寻找对应老师

学生实体

public class Student {
    private String name;
    private int id;
    // 学生关联一个老师
    private Teacher teacher;
}

按查询嵌套处理

相当于MySQL中子查询

- association:处理多对一的映射关系
- property:需要处理多对一的映射关系的属性名
- javaType:该属性的类型
<select id="getStudent" resultMap="StudentTeacher">
	select * from student;
</select>
<resultMap id="StudentTeacher" type="Student">
	<result property="id" column="id"></result>
	<result property="name" column="name"></result>
    <!--单独处理:嵌套子查询-->
	<association property="teacher" column="tid" javaType="Teacher" select="getTeacherById"/>
</resultMap>
<select id="getTeacherById" resultType="Teacher">
	select * from teacher where id = #{tid}
</select>

按结果嵌套处理

相当于MySQL中联表查询

- association:处理多对一的映射关系
- property:需要处理多对一的映射关系的属性名
- javaType:该属性的类型
<select id="getStudent" resultMap="StudentTeacher">
    select s.id as sid,s.name as sname,t.id as tid,t.name as tname 
    from student as s,teacher as t 
    where s.tid=t.id;
</select>
<resultMap id="StudentTeacher" type="Student">
	<result property="id" column="sid"></result>
	<result property="name" column="sname"></result>
    <!--复杂属性,单独处理-->
	<association property="teacher" javaType="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
    </association>
</resultMap>

6.2.2. 一对多映射处理

查询老师信息以及老师所对应的学生信息,一个老师同时对应多个学生

思路:查询老师信息,再根据查出来的老师的id,寻找对应的学生

老师实体

public class Teacher {
    private int id;
    private String name;
	// 一个老师关联多个学生
    private List<Student> students;
}

按查询嵌套处理

- collection:用来处理一对多的映射关系
- ofType:表示该属性对应的集合中存储的数据的类型
<select id="getTeacher" resultMap="TeacherStudent">
    select * from teacher where id=#{tid};
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="id"></result>
    <result property="name" column="name"></result>
    <!--单独处理:嵌套子查询-->
    <collection property="students" javaType="ArrayList" ofType="Student" select="getTeacherById" column="id">
    </collection>
</resultMap>
<select id="getTeacherById" resultType="Student">
    select * from student where tid=#{tid};
</select>

按结果嵌套处理

- collection:用来处理一对多的映射关系
- ofType:表示该属性对应的集合中存储的数据的类型
<select id="getTeacher" resultMap="TeacherStudent">
    select s.id as sid,s.name as sname,t.name as tname,t.id as tid
    from student as s,teacher as t
    where s.tid = t.id and t.id = #{tid};
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="id"></result>
    <result property="name" column="tname"></result>
    <!--复杂属性,单独处理-->
    <collection property="students" ofType="Student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
        <result property="tid" column="tid"></result>
    </collection>
</resultMap>

对比

嵌套查询 嵌套结果
嵌套查询是在查询 SQL 后再进行一个(子)查询 嵌套结果是一个多表查询的 SQL 语句
会执行多条 SQL 语句 只有一条复杂的 SQL 语句(多表连接)
SQL语句编写较为简单 SQL语句编写较为复杂
单表查询用 多表联表查询用

6.3. 驼峰映射

Mybatis提供的驼峰命名映射的方式,如数据库中的s_name字段,能够自动映射到Java实体类中的sName属性

<select id="selectByname" parameterType="java.lang.String" resultType="com.hy.entity.Student">  
    select s_name from student;  
</select>

SpringBoot整合Mybatis后开启有两种方式,一个是配置文件中开启,一个是配置类开启

application.properties配置文件中

mybatis:  
  configuration:  
    map-underscore-to-camel-case: true

7. 延迟加载

MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用全局延迟加载 lazyLoadingEnabled=true|false,当开启时,所有关联对象都会延迟加载

<settings>
	<!--开启延迟加载-->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

MyBatis延迟加载是通过在查询结果中返回代理对象来实现的。当查询结果中的代理对象被访问时,MyBatis会自动加载关联对象

<resultMap id="userResultMap" type="User">
  <id property="id" column="id"/>
  <result property="username" column="username"/>
  <result property="email" column="email"/>
  <collection property="posts" ofType="Post" select="getPostByUserId" fetchType="lazy"/>
</resultMap>

<select id="getUserById" resultMap="userResultMap">
  SELECT * FROM users WHERE id = #{id}
</select>

<select id="getPostByUserId" resultType="Post">
  SELECT * FROM posts WHERE user_id = #{id}
</select>

在上面的代码中,我们首先定义了一个resultMap,它将User对象映射到数据库表中的行,并使用collection元素来定义User对象的posts属性。我们将fetchType属性设置为”lazy”,以启用延迟加载。注:fetchType="lazy(延迟加载)|eager(立即加载)"

然后,我们定义了一个getUserById查询,它使用userResultMap来映射查询结果。当查询结果中的User对象被访问时,MyBatis会自动加载与该User对象关联的Post对象。

最后,我们定义了一个getPostByUserId查询,它返回与给定用户ID关联的所有Post对象。当getUserById查询返回User对象时,MyBatis会自动加载与该User对象关联的Post对象

使用场景:在对应的四种表关系中,一对多、多对多通常情况下采用延迟加载,多对一、一对一通常情况下采用立即加载

MyBatis 延迟加载的主要应用场景是在处理大量数据时,可以通过只在需要时加载数据来提高性能和减少内存占用。例如,如果你有一个包含大量订单的数据库表,而每个订单都有一个关联的用户,你可以使用延迟加载来避免在检索订单时同时加载所有相关的用户数据

8. 分页(重点)

后端的分页应该使用物理分页

8.1. RowBounds类

属于逻辑分页。它是在 SQL 查询出所有结果的基础上截取数据的,数据量大时不适用

8.2. 数据库原生limit

属于物理分页

select * from user limit #{startIndex},#{pageSize};

8.3. pagehelper插件(推荐)

属于物理分页。分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数

9. 用注解实现CRUD(不推荐)

注解在接口上实现

@Select("select * from user")
List<User> getUsers();

在mybatis-config.xml中绑定接口

<mappers>
    <mapper resource="com/hy/mapper/UserMapper.xml"/>
</mappers>

本质:反射机制

底层:动态代理

10. 插入数据后主键回填(重点)

数据库的id(主键)字段设置为自增时,代码层面可以在每次新增完一条记录后,获取新增的记录的id(主键值)

JDBC的实现

// 构造PreparedStatement时添加一个参数:PreparedStatement.RETURN_GENERATED_KEYS
ps = con.prepareStatement("INSERT INTO person(username,password,money) VALUES(?,?,?)", PreparedStatement.RETURN_GENERATED_KEYS);
ps.setObject(1, person.getUsername());
int i = ps.executeUpdate();
// 在更新操作执行完成之后,调用 getGeneratedKeys ,然后又会获取到一个 ResultSet 对象,从这个游标集中就可以获取到刚刚插入数据的id。
rs = ps.getGeneratedKeys();
int id = -1;
if (rs.next()) {
    id = rs.getInt(1);
}
return id;
}

MyBatis的实现(推荐)

在Mapper XML文件中

<insert id="insertBook" useGeneratedKeys="true" keyProperty="id"> 
insert into book (name,author) values (#{name},#{author})
</insert>

useGeneratedKeys = true告诉MyBatis使用数据库自动生成的主键,并将其回填到keyProperty指定的id属性中

必须确保数据库表中的主键字段配置为自动生成,并且与实体类的属性名称匹配

MySQL的实现

利用MySQL自带的 last_insert_id()

<insert id="insertBook">
    <selectKey keyProperty="id" resultType="java.lang.Integer">
        SELECT LAST_INSERT_ID()
    </selectKey>
    insert into t_book (b_name,author) values (#{name},#{author});
</insert>

11. 连接池替换

用第三方数据库连接池(Druid)替换MyBatis默认的连接池POOLED

12. 动态SQL(重点)

根据不同的条件生成不同的SQL语句

MyBatis的动态SQL本质上是通过拼接SQL语句来实现的,默认情况下 MySQL 可执行的最大 SQL 语句大小为 4194304 即 4MB,如果动态 SQL 拼接后的大小远大于默认值,则会报错。

如 实现查询功能,不给查询条件,则查询所有,给指定查询条件,则根据条件来查询,总之给不给条件都可以实现查询功能

12.1. if

<!-- List<Blog> queryBlog(Map map) -->
<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <if test="title !=null">
            and title=#{title}
        </if>
        <if test="author !=null">
            and author=#{author}
        </if>
    </where>
</select>

12.2. choose、when、otherwise

<!-- List<Blog> queryBlogChoose(Map map) -->
<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <choose>
            <when test="title !=null">
                and title=#{title}
            </when>
            <when test="author !=null">
                and author=#{author}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

12.3. trim、where、set

where标签

插入where子句,若子句的开头为 “AND” 或 “OR”,where 标签也会将它们去除

set

set标签会动态前置SET关键字,同时也会删掉无关的逗号

<!-- int updateBlog(Map map) -->
<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title !=null">
            title = #{title},
        </if>
        <if test="author !=null">
            author = #{author},
        </if>
    </set>
    where id=#{id}
</update>

12.4. SQL片段

使用 sql标签将SQL语句中用到的公共部分的SQL语句抽取出来

id:唯一标识一个sql标签
<sql id="if-title-author">
    <if test="title !=null">
        title = #{title},
    </if>
    <if test="author !=null">
        author = #{author},
    </if>
</sql>

使用 include标签引用

refid:对应sql片段的唯一标识
<include refid="if-title-author"></include>

12.5. foreach

collection:集合名
item:集合中每个元素的命名
open:以什么开始
close:以什么结束
separator:以什么分割
<foreach collection="ids" item="id" open="and (" close=")" separator="or">

13. 缓存(了解)

提高查询效率,将用户经常查询的数据放在缓存(内存)中

频繁读,少量写的数据适合放在缓存

频繁写,少量读的数据不适合放在缓存

MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

默认情况下,只有一级缓存开启(SqlSession级别的缓存, 也称为本地缓存)

二级缓存需要手动开启和配置(namespace级别的缓存,也称为全局缓存)

为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

13.1. 一级缓存

缓存失效的情况

查询不同的内容

增删改操作,可能会改变原来的数据,所以必定会刷新缓存

查询不同Mapper.xml

手动清理缓存 sqlSession.clearCache()

小结

一级缓存默认是开启的,只在一次SqISession中有效,也就是从拿到连接到关闭连接这个区间段

13.2. 二级缓存

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中

如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中,当新的会话查询信息时,就可以从二级缓存中获取内容

开启全局缓存

在 mybatis-config.xml 核心配置文件中,显式开启

<setting name="cacheEnabled" value="true"/>    

在相应的Mapper.xml中使用二级缓存

<cache
eviction="FIFO"
flushInterval=" 60000"
size="512"
readOnly= "true"/>

小结

所有的数据都会先放在一级缓存中

只有当会话提交,或者关闭的时候,才会提交到二级缓冲中

13.3. 缓存原理

缓存顺序 1.先看级缓存中有没有 2.再看一级缓存中有没有 3.查询数据库

13.4. 自定义缓存Ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存

引入依赖

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>

使用自定义cache

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

本站总访问量