Mybatis入门学习笔记(超级详细)

Mybatis的概述和入门配置

Mybatis的配置优化和CRUD操作

Mybatis动态SQL语句

Mybatis的关联映射

Mybatis注解式开发

Mybatis缓存

1 Mybatis的概述和入门配置

1.1 Mybatis的概述

1.1.1 引入:

相信我们在学习Mybatis之前已经学习了JDBC,也发现了JDBC存在一些问题:

  • 连接信息不可配置,连接数据库的信息都与代码进行了强耦合。
  • 每次都需要自己取连接,获取发送SQL语句的对象:PrepraredStatement对象。
  • 手动开启管理事务处理。
  • 最大的问题是:解析结果集,封装成Java对象,一个一个字段的获取与注入。

Mybatis框架是把JDBC进行了封装和优化,是现今最为流行的持久层框架之一。大家必须熟练!

1.1.2 框架介绍

mybatis是Apache软件基金会下的一个开源项目,前身是iBatis框架。2010年这个项目由apache 软件基金会迁移到google code下,改名为mybatis。2013年11月又迁移到了github(开源项目平台)

Mybatis是一个基本ORM思想的持久层框架。

1.1.3 Mybatis的优点

1.使用灵活(轻量级框架):

轻量级企业开发框架:一个技术对底层没有依赖或者依赖很弱就是轻量级框架。

Mybatis不会对应用程序或者数据库的现有设计强加任何影响。SQL语句写在XML里,便于统一管理和优化。

2.解除SQL与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易进行单元测试。SQL语句和代码的分离,提高了可维护性。天然支持分层设计思想!

3.Mybatis的核心优势:ORM框架(Object Relational Mapper)

List<Student>  -> Select * from tb_student

可以直接查询数据出来直接返回成对应的Java对象数据(必须字段名称一样才可以!)

4.Mybatis支持缓存。可以提高性能

1.1.4 mybatis的不足

1.Mybatis在做多表关联操作时,非常的麻烦,要做很多额外处理。(其实也还好!)

2.框架还是比较简陋,功能尚有缺失,二级缓存机制不佳。

1.1.5 ORM的概念
  1. ORM概念:Object Relational Mapping 对象关系映射

    在Java中编程:使用的是面向对象的开发方式

    在MySQL中写的SQL语句:使用的是关系型的数据库

    将表中的数据映射成一个对象,对象关系映射

    List<Student>  -> Select * from tb_student
    
    Student -> Select * from tb_student where id = 1
    
  2. mybatis的映射方式

在这里插入图片描述

1.2 Mybatis的入门框架搭建

1.2.1 前期准备

1.创建模块,建包分层

  • action 做测试调用的
  • bean 放置实体类。User.java Student.java
  • dao Mybatis的核心逻辑层,面向接口编程的。
  • util 工具包,放置各种工具,例如连接工厂。

2.导入jar包

  • 导入Mybatis的核心jar包:mybatis-3.5.0.jar

  • 导入Mybatis框架本身依赖的日志框架包:log4j-1.2.17.jar

    ​ – 作用:方便打印底层日志信息给程序员排错或者浏览的。

    ​ – 注意:必须导入一个日志配置文件到src下去:log4j.properties/log4j.xml

  • 导入数据库驱动:mysql-connector-java-5.1.37-bin.jar

1.2.2 创建连接工厂

1.得到Mybatis的核心配置文件(名字原则上可以随意): mybatis-config.xml, 复制到src下。

2.编写Mybatis框架的连接工厂类

a. SqlSessionFactoryBuilder:这是一个临时对象,用完就不需要了。通过这个会话工厂建造类来创建一个会话工厂。

b. SqlSessionFactory:会话工厂得到会话对象,一个项目中只需要创建一个会话工厂对象即可。通过会话工厂对象来创建会话对象。

c. SqlSession(会话): 每次访问数据库都需要创建一个会话对象,这个会话对象不能共享。访问完成以后会话需要关闭。底层封装了JDBC的Connection操作数据库。

  • 定义一个静态变量用于保存Mybatis的会话工厂
public static SqlSessionFactory sqlSessionFactory;
static {
    try {
        // Resources下的getResourceAsStream方法直接去src下寻找文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 返回会话对象SqlSession
public static SqlSession getSqlSession(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    return sqlSession;
}
  • 关闭会话
public  static void close(SqlSession sqlSession){
    if(sqlSession != null) sqlSession.close();
}
1.2.3 通话会话对象SqlSession(数据库连接)操作数据库

1.建立数据库表格数据

DROP DATABASE day18;
CREATE DATABASE day18;
USE day18;
CREATE TABLE `tb_user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `loginName` VARCHAR(23) DEFAULT NULL,
  `userName` VARCHAR(23) DEFAULT NULL,
  `passWord` VARCHAR(23) DEFAULT NULL,
  `create_date` DATETIME DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

/*Data for the table `tb_user` */

INSERT  INTO `tb_user`(`id`,`loginName`,`userName`,`passWord`,`create_date`) VALUES

(1,'swk','齐天大圣','tangtang','2019-11-24 12:00:37'),

(3,'tangtang','大唐唐玄奘','kongkong','2019-11-24 12:08:22'),

(4,'zbj','天棚元帅','change','2019-11-24 12:08:22'),

(5,'change','嫦娥仙子','wugang','2019-11-24 12:08:22'),

(6,'ptlz','菩提老祖','sunwukong','2019-11-11 11:11:11'),

(7,'rlfz','如来佛祖','ptlz','2019-11-11 11:11:11');

2.在bean包下定义一个测试类,定义实体类User.java。

public class User {
    private int id;
    private String loginName;
    private String passWord;
    private String userName;
    private String createDate;
    
	//Javabean省略
}

3.在持久层dao定义一个访问数据表的接口:UserMapper.java。

/**
 访问用户表的接口
 */
public interface UserMapper {
    List<User> findAll();
}

4.得到一个与接口对应的映射文件UserMapper.xml放在该接口的包下。

注意:

  • 映射文件的名称必须与接口名称一致

  • 映射文件的命名空间必须是所对应接口的路径
    namespace=“com.itheima.dao.UserMapper”

  • 必须在核心配置文件mybatis-config.xml中关联本映射文件的路径。

<mapper resource="com/itheima/dao/UserMapper.xml"></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">
<!-- namespace: 必须申明成被服务接口的路径 -->
<mapper namespace="com.itheima.dao.UserMapper">

    <!-- List<User> findAll();
        id必须与被服务接口所服务方法的名称一模一样
        resultType :必须声明返回数据的结果对象的类型!
    -->
    <select id="findAll" resultType="com.itheima.bean.User">
        select * from tb_user
    </select>
</mapper>
1.2.4 在主程序中调用映射文件的SQL语句操作

1.得到一个会话对象sqlSession

2.通过会话对象,调用getMapper方法得到一个数据访问层接口的代理对象

重点:sqlSession会话对象去找到UserMapper.xml文件把它作为当前接口UserMapper的代理对象

3.用该持久层(数据访问层)接口userMapper代理对象查询数据。

最终根据调用的方法名称去映射文件中UserMapper.xml中找到对应SQL语句执行。

注意SQL语句实体字段值必须与数据库字段值一致才能成功!

public class MybatisDemo {
    @Test
    public void selectAll(){
        // 1、得到一个会话对象。
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、通过会话对象得到一个数据访问层接口的代理对象(实现类对象)
        // 重点:sqlSession会话会去找到UserMapper.xml文件把它
        // 作为当前接口UserMapper的实现类代理对象!!!
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 3、用该持久层接口userMapper代理对象查询数据!!
        List<User> users = userMapper.findAll();
        System.out.println(users);

        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);

    }

1.3 Mybatis的工作流程

  1. 通过InputStream对象(这里也可以用Reader对象)调用getResourceAsStream方法直接去src下寻找核心配置文件。
  2. 通过SqlSessionFactoryBuilder对象创建SqlSessionFactory 对象。
  3. 获得当前的会话对象SqlSession。
  4. 默认开启事务
  5. 核心配置文件已经关联了映射文件,根据地址去找到映射文件把它作为当前接口UserMapper的实现类代理对象(userMapper)。
  6. 使用代理对象调用映射文件中的SQL语句执行
  7. 提交事务
  8. 关闭资源

2 Mybatis的配置优化和CRUD操作

2.1 Mybatis参数传递机制研究

2.1.1 入参只有一个的情况

参数只有一个的情况----变量名称可以随便写#{xxxx}接收

//MybatisDemo
User user = userMapper.getUserById(4);
//UserMapper.java
User getUserById(int id);

取参数值格式:#{变量名称}

  • #{} 解析传递进来的参数数据
  • ${}对传递进来的参数原样拼接在SQL中
  • #{} 是预编译处理,${}是字符串替换。
  • 使⽤#{}可以有效的防⽌SQL注⼊,提⾼系统安全性。

UserMapper.xml


<!-- User getUserById(int id);
    *parameterType="int"**参数类型(可以省略不写)
    取参数值格式:#{变量名称}
    一个参数的情况下,变量名称可以随意指定!
 -->
<select id="getUserById"  resultType="com.itheima.bean.User">
    select * from tb_user where id = #{xzxx}
</select>
2.1.2 入参有多个的情况

多个参数我们必须为参数设置别名:@Param(名称)

在取入参值的时候,名称必须与别名一致!!

//MybatisDemo
User user = userMapper.login("zbj" , "change");
//UserMapper.java
User login(@Param("name") String loginName, @Param("passWord") String passWord);

UserMapper.xml

可以看到下面代码的参数都被替换成了别名。

<!--
 User login(String loginName, String passWord);
 二个参数的情况下,不可以随便书写参数名称,参数必须设置别名
-->
<select id="login"  resultType="com.itheima.bean.User">
    select * from tb_user where loginName = #{name} and passWord = #{passWord}
</select>
2.1.3 入参是对象的情况

对象参数的话,参数值本身存在字段名称,我们可以直接用字段名称取参数值即可(前提是取名的时候对象字段名称必须与数据库的字段名称一样,否则也会出错):

底层原理,实际上是调用对象的get方法取参数:

#{userName } = user.getUserName();
#{loginName} = user.getLoginName();

注意:开发中例如注册信息多半是用对象封装以后,传入到底层保存。

//MybatisDemo
User user = new User();
user.setCreateDate(new Date());
user.setLoginName("nvgw");
user.setUserName("朱琳");
user.setPassWord("tangtang");
int count = userMapper.save(user);
//UserMapper.java
int save(User user);

UserMapper.xml

我们可以看到,下面代码我们传入的参数直接就是按照对象的字段名称提取值传入数据库。

<!--   int save(User user);
    入参是一个对象 :可以直接按照对象的字段名称提取值
    原理是调用对象的get方法获取字段值:
       #{loginName} = user.getLoginName():
       #{userName}= user.getUserName():
 -->
<insert id="save" >
    INSERT INTO `tb_user` (
          `loginName`,
          `userName`,
          `passWord`,
          `create_date`
        )
        VALUES
          (
            #{loginName},
            #{userName},
            #{passWord},
            #{createDate}
          )
</insert>
2.1.4 入参是Map集合的情况

Map集合作为Mybatis的入参,参数本身的名称就是键名称,所以可以直接根据键名称提取参数值!

//MybatisDemo
Map<String , String> maps = new HashMap<>();
maps.put("loginName" , "zbj");
maps.put("pwd" , "change");
User user = userMapper.loginByMap(maps);
//UserMapper.java
User loginByMap(Map<String, String> maps);

UserMapper.xml

可以看到下面代码的参数就是参照Map集合的键名称的,比如maps.put(“pwd” , “change”)==>passWord = #{pwd}

<!--
    User loginByMap(Map<String, String> maps);
     参数是Map集合的情况:因为Map集合的参数是有键的,我们可以直接根据键名称获取参数值
-->
<select id="loginByMap"  resultType="com.itheima.bean.User">
    select * from tb_user where loginName = #{loginName} and passWord = #{pwd}
</select>
2.1.5 入参是对象,但是对象中存在复合对象的情况

先添加地址类Address。

public class Address {
    private String name; // 名称
    private String code;  // 编号

   	//getter setter toString省略
}

然后再在User类里面添加复合对象。

private Address addr;
...getter/setter,toString

参数是复合类型:直接提取复合对象.内部字段

//MybatisDemo
User u = new User();
Address a = new Address();
a.setName("广州");
u.setAddr(a);
//UserMapper.java
List<User> getUsersByAddr(User u);

UserMapper.xml

可以看到下面的参数类型就是复合对象.内部字段#{addr.name}= u.getAddr().getName();

<!-- List<User> getUsersByAddr(User u);
参数是复合类型:直接提取复合对象.内部字段
#{addr.name}  = u.getAddr().getName();
-->
<select id="getUsersByAddr"  resultType="com.itheima.bean.User">
    select * from tb_user where address = #{addr.name}
</select>

2.2 核心配置文件优化

2.2.1 mybatis-config.xml的优化

properties优化:

在外部定义了一个db.properties文件存放数据库的连接信息,然后在核心配置文件中通过:

<properties resource="db.properties"></properties>

来关联这个信息。

这样以后我们要修改参数只需找到db.properties修改键值即可。

//db.properties 
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day18
username=root
password=root
<!-- mybatis-config.xml -->
<!-- 第一种方案-->

<!-- 会自动去src下加载db.properties属性配置文件信息-->
<properties resource="db.properties"></properties>

<!-- 第二种方案-->
<!-- 加载数据库的核心配置信息  resource:自动去类路径src下寻找。
   <properties resource="db.properties">
       优先用外部配置,如果外部没有这个配置再用这里的配置!
       <property name="username" value="root"/>
       <property name="password" value="dlei"/>
</properties>
-->
...
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>

mybatis的properties配置:

在构造函数中传入

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in, environment, props);

直接使用子标签

<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/day18"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>

配置 resource 地址

<properties resource="db.properties"></properties>

配置 url 地址:会使用 url 去读取流作为配置

<properties resource="xxx.cn"></properties>
  • 在 properties 元素体内指定的属性首先被读取。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
2.2.2 配置类型别名
<!-- 类型别名 -->
<typeAliases>
    <!-- 1、配置User类型的别名为: User-->
   <!-- <typeAlias type="com.itheima.bean.User"  alias="User"></typeAlias>-->

    <!-- 2、可以省略别名设置,那么默认的别名就是类名:User -->
    <!--   <typeAlias type="com.itheima.bean.User" ></typeAlias>-->

    <!-- 3、使用包扫描(企业级开发思想) 扫描这个包下的全部类型,默认别名用类名-->
    <package name="com.itheima.bean"/>
</typeAliases>

这样的好处就是UserMapper.xml里面的 resultType不需要再写路径名,只需要填写别名。

<!--UserMapper.xml-->
<select id="selectAll" resultType="User">
    select * from tb_user
</select>
2.2.3 mappers配置优化

子元素 mapper: 每行语句指定一个映射配置文件

<!-- 直接去src下寻找关联映射文件的路径 -->
<!-- <mapper resource="com\itheima\dao\UserMapper.xml"/>
 <mapper resource="com\itheima\dao\StudentMapper.xml"/>-->

子元素 package: 加载包下所有的映射配置文件

<!-- 包扫描:直接扫描该包下的全部映射文件加载!-->
<package name="com.itheima.dao"/>

2.3 模糊查询

2.3.1 方案一:%${value}%

直接使用这个值’%${value}%'提取模糊查询的值。

//MybatisDemo
List<User> users = userMapper.getUserByNameLike("天");
//UserMapper
List<User> getUserByNameLike(String userName);

resultType: 如果返回的是集合,使用的就是集合中单个元素的类型!

** v a l u e : ∗ ∗ 字 符 串 拼 接 , 使 用 {value}:** 字符串拼接,使用 value:使. 如果是简单类型:(8种基本类型+String类型),这里变量名只能是value!

  • ${} 对传递进来的参数原样拼接在SQL中

UserMapper.xml

<!--
    List<User> getUserByNameLike(String name);
    通过名字模糊查询
-->
<select id="getUserByNameLike" resultType="User">
       select * from tb_user where userName like '%${value}%'
</select>
2.3.2 CONCAT(’%’, #{xxx} , ‘%’)

直接在数据库层面进行前后拼接%.

//MybatisDemo
List<User> users = userMapper.getUserByNameLike2("天");
//UserMapper
List<User> getUserByNameLike2(String userName);

UserMapper.xml

 <!-- List<User> getUserByNameLike2(String userName); -->
 <!--    方案二:CONCAT('%', #{xxx} , '%')  :在前后拼接% -->
<select id="getUserByNameLike2" resultType="User">
     select * from tb_user where userName like CONCAT('%' , #{xxx} , '%')
 </select>
2.3.3 直接在业务查询时做好

直接在业务查询的时候,通过Java代码做好模糊查询的入参就可以了!

name= ‘%天%’

//MybatisDemo
List<User> users = userMapper.getUserByNameLike3("%天%");
//UserMapper
List<User> getUserByNameLike3(String userName);

UserMapper.xml

<!--     List<User> getUserByNameLike3(String userName); -->
 <!--   方案三: 直接在业务查询的时候,通过Java代码做好模糊查询的入参就可以了! -->
<select id="getUserByNameLike3" resultType="User">
     select * from tb_user where userName like #{xxzds}
 </select>

2.4 获取主键

插入数据获取被插入数据的主键,有两种方式。

通过LAST_INSERT_ID()获取刚插⼊记录的⾃增主键值,在insert语句执⾏后,执⾏select
**LAST_INSERT_ID()**就可以获取⾃增主键。

原理:先查询序列得到主键,将主键设置到user对象中,将user对象插⼊数据库

// MybatisDemo
User user = new User();
user.setCreateDate(new Date());
user.setLoginName("nvgw");
user.setUserName("朱琳");
user.setPassWord("tangtang");
// int count = userMapper.save(user);
int count = userMapper.save2(user);

//UserMapper
int save(User user);
int save2(User user);
2.4.1 方式一:子元素<selectKey>
<!--   int save(User user);
  方式一:子元素\<selectKey>
 -->
<insert id="save" >
    INSERT INTO `tb_user` (
          `loginName`,
          `userName`,
          `passWord`,
          `create_date`
        )
        VALUES
          (
            #{loginName},
            #{userName},
            #{passWord},
            #{createDate}
          )
    <!--
    keyColumn:主键在表中对应的列名
    keyProperty:主键在实体类中对应的属性名
    resultType:主键的数据类型
    order:
        BEFORE: 在添加语句前执行查询主键的语句
        AFTER: 在添加语句后执行查询主键的语句
        原理: user.setId(主键值)
    -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
        select last_insert_id()
    </selectKey>
</insert>
2.4.2 直接在insert通过属性获取主键
<!--   int save2(User user); -->
 <!-- 方式二:插入数据获取主键,使用属性解决
 useGeneratedKeys true 使用自动生成的主键  keyColumn表中主键的列名 keyProperty实体类中主键的属性名
 -->
<insert id="save2" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
    INSERT INTO `tb_user` (
          `loginName`,
          `userName`,
          `passWord`,
          `create_date`
        )
        VALUES
          (
            #{loginName},
            #{userName},
            #{passWord},
            #{createDate}
          )
</insert>

2.5 resultType与resultMap

<resultMap id="userResultMap" type="User">
    <!--colum数据库字段列名 property实体类字段名称-->
    <id column="id" property="id"></id>
     <result property="username" column="username"/>
</resultMap>

如果实体类中的字段与数据库的列名称不一致,映射数据会失败。

因为,如果我们的数据表的字段和JavaBean的属性名称是相同时,我们就会使用上面的代码

Mybatis会⾃动帮我们把返回的结果进⾏封装成JavaBean

那当我们数据表的字段和JavaBean的属性名称不是相同时,我们就需要使⽤resultMap

解决方式有两种:

2.5.1 自定义结果集配置:ResultMap
//MybatisDemo
List<User> users = userMapper.getUserByNameLike("天");
//UserMapper
List<User> getUserByNameLike(String userName);

如果这里的不使用resultMap,而是使用resultType的话,等下打印出来createDate=null,原因就是实体类中的字段与数据库的列名称不一致。

<!--自定义结果集-->
<resultMap id="userResultMap" type="User">
    <!--colum数据库字段列名 property实体类字段名称-->
    <id column="id" property="id"></id>
	<result column="create_date" property="createDate"></result>
</resultMap>

<!--List<User> getUserByNameLike(String userName);-->
<select id="getUserByNameLike" resultMap="userResultMap">
	select * from tb_user where userName like '%${value}%'
</select>

2.5.1 直接使用查询别名
//MybatisDemo
List<User> users = userMapper.getUserByNameLike2("天");
//UserMapper
List<User> getUserByNameLike2(String userName);

UserMapper.xml

通过配置别名的方式,使得数据库的列名和实体类的名字相同。

<select id="getUserByNameLike2" resultType="User">
    select * , create_date  createDate from tb_user where userName like '%${value}%'
</select>
2.5.3 resultType与resultMap区别

resultType :指定输出结果的类型(pojo、简单类型、hashmap…),将sql查询结果映射为java对象。

使⽤resultType注意:sql查询的列名要和resultType指定pojo的属性名相同,指定相同属性⽅可映射成功,如果sql查询的列名要和resultType指定pojo的属性名全部不相同,list中⽆法创建pojo对象的。就会出现createDate=null的情况。

resultMap:将sql查询结果映射为java对象。

如果sql查询列名和最终要映射的pojo的属性名不⼀致,使⽤resultMap将列名和pojo的属性名做⼀个对应关系 (列名和属性名映射配置)

3 Mybatis动态SQL语句

3.1 动态sql语句查询拼接if/where

应用场景:在日常生活中,我们在搜索物品的时候,会添加不同的条件去查询物品,那么就需要根据这些条件自动去拼接动态的生成SQL语句。

条件在变化的过程中,如何实现动态sql语句查询。

需求:用户可能按照 登录名/用户名/地址 动态查询用户数据,甚至不带条件查询全部用户数据。

因为拼接的代码只是UserMapper.xml里面修改,所以我们先把前面其他文件中相同的代码在前面先写好。

public class MybatisDemo {
    @Test
    public void selectById() {
        // 1.得到数据库会话对象
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2.得到数据访问层接口的代理对象。默认选择使用接口的映射文件作为代理对象!
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u = new User();
        u.setLoginName("t");
        u.setUserName("唐");
        u.setAddress("大");

        // List<User> users = userMapper.selectByDynamic(u);
        List<User> users = userMapper.selectByDynamic02(u);
        System.out.println(users);

        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
   
}
//UserMapper.java
public interface UserMapper {
    List<User> selectByDynamic(User u);
    List<User> selectByDynamic02(User u);
}
<!--UserMapper.xml-->
<mapper namespace="com.itheima.dao.UserMapper">
    
    <!-- 自定义结果集  -->
    <resultMap id="userResultMap" type="User">
        <!-- column数据库字段列名 property实体类字段名称 -->
        <id column="id" property="id"></id>
        <!-- 配置关联名称不一样字段的映射 -->
        <result column="create_date" property="createDate"></result>
    </resultMap>
</mapper>
3.1.1 方式一恒等式解决动态sql拼接问题

使用if标签判断查询条件不能为空以及不能为空字符(空字符表示用户可能在搜索框没有输入)。

中间&+amp;&+amp;表示&&(xml规定)

如果第一个if语句判断为false,那么就不会执行下面的语句,那么就会导致拼接失败,变成了

 select * from tb_user where ?

所以我们可以使用让一个恒等式使得不管怎么用,where后面拼接的值永远为真。

<!--
     List<User> selectByDynamic(User u);
     1、动态sql语句:条件可能是 用户名 登录名 地址 甚至么有条件!
     方式一:恒等式解决动态sql拼接问题(拓展方式)
    -->
<select id="selectByDynamic" resultMap="userResultMap">
    select * from tb_user where 1=1
    <if test="loginName != null &amp;&amp; loginName != ''">
        and loginName like CONCAT('%' , #{loginName} , '%')
    </if>
    <if test="userName != null &amp;&amp; userName != ''">
        and userName like CONCAT('%' , #{userName} , '%')
    </if>
    <if test="address != null &amp;&amp; address != ''">
        and address like CONCAT('%' , #{address} , '%')
    </if>
</select>
3.1.2 方式二:使用if结合where标签解决动态sql拼接问题。

那么在mybatis中还有另外一种方法可以解决这个问题,就是使用if结合where标签解决动态sql拼接问题。

注意:第一个条件的and可以加可以不加!where标签会自动识别去掉不合理的sql语句拼接。

<select id="selectByDynamic02" resultMap="userResultMap">
    select * from tb_user
    <where>
        <if test="loginName != null &amp;&amp; loginName != ''">
             loginName like CONCAT('%' , #{loginName} , '%')
        </if>
        <if test="userName != null &amp;&amp; userName != ''">
            and userName like CONCAT('%' , #{userName} , '%')
        </if>
        <if test="address != null &amp;&amp; address != ''">
            and address like CONCAT('%' , #{address} , '%')
        </if>
    </where>
</select>

3.2 动态修改SQL语句的执行

需求:可能修改 用户名 密码 地址

动态修改SQL语句其实很简单,只需要把where标签换成set标签,去掉and,每个SQL语句后面加逗号,最好一个不加即可。

public class MybatisDemo {
 @Test
    public void updateByDynamic() {
        // 1.得到数据库会话对象
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2.得到数据访问层接口的代理对象。默认选择使用接口的映射文件作为代理对象!
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u = new User();
        u.setId(4);
        u.setUserName("猪刚鬣");
        u.setAddress("大唐净坛");
        u.setPassWord("chan");
        
        int count = userMapper.updateByDynamic(u);
        System.out.println("修改了:" + count);
        sqlSession.commit();
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}
public interface UserMapper {
    int updateByDynamic(User u);
}

配置自定义结果集,然后使用update标签和set标签、if标签加上SQL语句即可。

<mapper namespace="com.itheima.dao.UserMapper">
    
    <!-- 自定义结果集  -->
    <resultMap id="userResultMap" type="User">
        <!-- column数据库字段列名 property实体类字段名称 -->
        <id column="id" property="id"></id>
        <!-- 配置关联名称不一样字段的映射 -->
        <result column="create_date" property="createDate"></result>
    </resultMap>
    
    <update id="updateByDynamic">
        update tb_user
        <set>
            <if test="userName !=null &amp;&amp; userName !=''">
                userName = #{userName} ,
            </if>
            <if test="passWord !=null &amp;&amp; passWord !=''">
                passWord = #{passWord} ,
            </if>
            <if test="address !=null &amp;&amp; address !=''">
                address = #{address}
            </if>
        </set>
        where id = #{id}
    </update>
</mapper>

3.3 动态插入和删除SQL语句foreach

3.3.1 foreach遍历集合参数(插入)

因为有的时候我们要添加数据是多个对象的,所以需要用到list集合去存入,存入时我们可以用foreach去对每一个对象进行遍历。

public class MybatisDemo {
    @Test
    public void insertBatch() {
        // 1.得到数据库会话对象
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2.得到数据访问层接口的代理对象。默认选择使用接口的映射文件作为代理对象!
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 定义一个集合存储3个用户对象一起保存起来
        //     public User(String loginName, String passWord, String userName, Date createDate, String address) {
        List<User> users = new ArrayList<>();
        users.add(new User("cangcang", "123456" , "苍老师" , new Date() , "东京"));
        users.add(new User("songdao", "34567" , "松岛老师" , new Date() , "大阪"));
        users.add(new User("jiateng", "1234" , "加藤老师" , new Date() , "北海道"));

        int count = userMapper.addUsers(users);
        System.out.println("添加了数据:" + count);

        sqlSession.commit();// 事务提交
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

public interface UserMapper {
    int addUsers(List<User> users);
}

UserMapper.xml

<mapper namespace="com.itheima.dao.UserMapper">

    <!--   int addUsers(List<User> users);
        users = [u1 , u2 , u3] 每一次都去遍历一个用户对象
                 u
    -->
    <insert id="addUsers">
            insert into tb_user(loginName , userName , passWord , create_date , address) values
        <!--
        foreach遍历传输进来的集合:
            collection:两个取值:list 表示集合 array 表示数组
            item:遍历集合中使用的变量名字
            separator:每次遍历后添加分隔符
        -->
        <foreach collection="list" item="u" separator=",">
            (#{u.loginName}, #{u.userName} , #{u.passWord} ,#{u.createDate} , #{u.address} )
        </foreach>
    </insert>
</mapper>
3.3.2 foreach遍历数组参数(删除)

用做删除操作的时候比较简单,我们只需要将要删除的内容的id存放到一个数组中,然后用foreach遍历这个数组中的每一个元素,进行删除操作即可。

public class MybatisDemo {
 @Test
    public void deleteBatch() {
        // 1.得到数据库会话对象
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2.得到数据访问层接口的代理对象。默认选择使用接口的映射文件作为代理对象!
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 定义一个数组存储3个id值,批量删除
        int count = userMapper.deleteByIds(new int[]{1,3,5,7,9});
        System.out.println("删除了数据:" + count);

        sqlSession.commit();// 事务提交
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

public interface UserMapper {
    int deleteByIds(int[] ids);
}

UserMapper.xml

<mapper namespace="com.itheima.dao.UserMapper">
<delete id="deleteByIds">
        delete from tb_user  where id in
        <!--
            collection :代表当前遍历的是数组 array
            open:以什么开始
            close:以什么结尾
            item:遍历使用的游标变量
            separator:每次去到一个数据后用什么隔开
        -->
        <foreach collection="array" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </delete>
</mapper>

3.4 sql和include标签

sql和include标签:简化映射文件中的脚本代码的书写

sql标签:可以放置需要重复利用的脚本代码。

include标签: 引用上面定义的SQL语句

< !-- include可以把sql语句的脚本关联过来使用 通过refid去寻找–>
< include refid=“whereSQL”>< /include>

我们前面用了动态查询拼接的例子,我们可以发现文件里面的重复代码过多,冗余度过高,所以我们可以来做一个简化。

<mapper namespace="com.itheima.dao.UserMapper">
    
    <!-- 自定义结果集  -->
    <resultMap id="userResultMap" type="User">
        <!-- column数据库字段列名 property实体类字段名称 -->
        <id column="id" property="id"></id>
        <!-- 配置关联名称不一样字段的映射 -->
        <result column="create_date" property="createDate"></result>
    </resultMap>

    <!--
     List<User> selectByDynamic(User u);
     1、动态sql语句:条件可能是 用户名 登录名 地址 甚至么有条件!
     方式一:恒等式解决动态sql拼接问题(拓展方式)
    -->
    <select id="selectByDynamic" resultMap="userResultMap">
        select * from tb_user where 1=1
        <if test="loginName != null &amp;&amp; loginName != ''">
            and loginName like CONCAT('%' , #{loginName} , '%')
        </if>
        <if test="userName != null &amp;&amp; userName != ''">
            and userName like CONCAT('%' , #{userName} , '%')
        </if>
        <if test="address != null &amp;&amp; address != ''">
            and address like CONCAT('%' , #{address} , '%')
        </if>
    </select>

 <!--
     List<User> selectByDynamic02(User u);
     2、动态sql语句:条件可能是 用户名 登录名 地址 甚至么有条件!
     方式二:使用if结合where标签解决动态sql拼接问题。
     注意:第一个条件的and可以加可以不加!where标签会自动识别去掉不合理的sql语句拼接。
    -->
    <select id="selectByDynamic02" resultMap="userResultMap">
        select * from tb_user
        <where>
            <if test="loginName != null &amp;&amp; loginName != ''">
                 loginName like CONCAT('%' , #{loginName} , '%')
            </if>
            <if test="userName != null &amp;&amp; userName != ''">
                and userName like CONCAT('%' , #{userName} , '%')
            </if>
            <if test="address != null &amp;&amp; address != ''">
                and address like CONCAT('%' , #{address} , '%')
            </if>
        </where>
    </select>
</mapper>

那么我们就可以用sql标签放置相同的代码

<!-- sql标签可以放置相同的脚本代码-->
<sql id="whereSQL">
    <if test="loginName != null &amp;&amp; loginName != ''">
        and loginName like CONCAT('%' , #{loginName} , '%')
    </if>
    <if test="userName != null &amp;&amp; userName != ''">
        and userName like CONCAT('%' , #{userName} , '%')
    </if>
    <if test="address != null &amp;&amp; address != ''">
        and address like CONCAT('%' , #{address} , '%')
    </if>
</sql>

然后只需要使用include可以把sql语句的脚本关联过来使用,通过refid去寻找

<select id="selectByDynamic" resultMap="userResultMap">
    select * from tb_user where 1=1
    <include refid="whereSQL"></include>
</select>

<select id="selectByDynamic02" resultMap="userResultMap">
    select * from tb_user
    <where>
        <include refid="whereSQL"></include>
    </where>
</select>

这样的话代码就被极大的简化了。

4 Mybatis的关联映射

4.1 1-1关联映射查询实现

需求:查询出某个用户的身份证信息。用户信息和身份证信息是 一 对 一的关系。

实现步骤:

1、必须在实体类中先建立1-1的关系。

​ 定义一个身份证类

public class UserCard {
    private int id;
    private double height;
    private double weight;
    private int married;
    
  	//getter setter toString省略
}

再定义用户类关联身份证类

public class User {
    private int id;
    private String userName;
    private Date birthday;
    private char sex;
    private String address;
    // 一个用户对象 一个身份证对象: 1-1的关系。
    private UserCard userCard;
    
 	//getter setter toString省略
}

2、实现1-1的关联映射查询实现。

获取会话对象,获取用户的数据访问层接口代理对象,创建方法查询用户信息。

public class MybatisDemo {
    @Test
    public void query1_1(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、查询用户对象信息(目的是可以查询出1-1关联的身份证信息 )
        List<User> users = userMapper.findAll();
        System.out.println(users);
        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

因为创建了UserCard的对象类,所以,我们不管需不需要用到,都应该再dao包下创建它的映射文件。

public interface UserCardMapper {
}
public interface UserMapper {
    List<User> findAll();
}
<!--UserCardMapper.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.itheima.dao.UserCardMapper">
<!--不需要用到,但是必须写上来-->
</mapper>

下面是配置UserMapper.xml,首先先配置好两个实体的结果集,然后在再将两段结果集进行关联,官方规定了1-1关联的字段映射必须使用一个标签:association。最后再配置SQL语句,结果集返回类型是两个实体类关联的结果集。

<mapper namespace="com.itheima.dao.UserMapper">
    <resultMap id="userCardResultMap" type="UserCard">
        <id property="id" column="id"/>
        <result property="height" column="height"/>
        <result property="weight" column="weight"/>
        <result property="married" column="married"/>
    </resultMap>

    <resultMap id="userResultMap" type="User">
        <id property="id" column="id"/>
        <result property="userName" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </resultMap>

    <!-- extends:先继承用户基础的结果集映射用户对象本身的信息,里面再关联映射关联字段的信息 -->
    <resultMap id="user1_1ResultMap" type="User" extends="userResultMap">
        <!-- 开始配置1-1关联映射的身份证字段对象信息:userCard
             官方规定了1-1关联的字段映射必须使用一个标签:association
                property:申明关联的1-1字段
                resultMap:关联字段userCard自己的结果集映射
        -->
        <association property="userCard" resultMap="userCardResultMap"/>
    </resultMap>

    <!--  List<User> findAll(); -->
    <select id="findAll" resultMap="user1_1ResultMap">
        SELECT * FROM USER u LEFT JOIN user_card uc ON u.id = uc.id
    </select>

</mapper>

4.2 1-N

需求: 查询出用户对象的时候,可以查询出它的全部订单信息。用户 与 订单是 1-N的关系!

实现步骤:

1、必须在实体类中先建立1-N的关系。

​ 定义一个订单类:

public class OrderForm {
    private int oid;
    private int userId;
    private String number;
    private Date createTime;
    private String note;
    
	//getter setter toString省略

}

定义一个用户类:

public class User {
    private int id;
    private String userName;
    private Date birthday;
    private char sex;
    private String address;
    // 一个用户对象 一个身份证对象: 1-1的关系。
    private UserCard userCard;
    // 一个用户对象 一批订单对象: 1-N的关系
    private List<OrderForm> orderForms;
 
 	//getter setter toString省略
}
public interface UserMapper {
    List<User> findAll();
}

下面是同样是配置UserMapper.xml,首先先配置好两个实体的结果集,然后在再将两段结果集进行关联,官方规定了1-N关联的字段映射必须使用一个标签:collection。最后再配置SQL语句,结果集返回类型是两个实体类关联的结果集。

<?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.itheima.dao.UserMapper">

    <resultMap id="userResultMap" type="User">
        <id property="id" column="id"/>
        <result property="userName" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </resultMap>

    <resultMap id="orderFormResultMap" type="OrderForm">
        <id property="oid" column="oid"/>
        <result property="userId" column="user_id"/>
        <result property="number" column="number"/>
        <result property="createTime" column="create_time"/>
        <result property="note" column="note"/>
    </resultMap>

    <!-- extends:先继承用户基础的结果集映射用户对象本身的信息,里面再关联映射关联字段的信息 -->
    <resultMap id="user1_NResultMap" type="User" extends="userResultMap">
        <!-- 一个用户包含一批订单,官方规定了1-N的字段必须使用如下标签映射: collection -->
        <!--
         property: 多方的属性名
         javaType:多方的属性类型
         ofType: 集合中每个元素的类型
         resultMap:多方映射
         -->
        <collection property="orderForms" javaType="list" ofType="OrderForm"
                    resultMap="orderFormResultMap"/>
    </resultMap>

    <!-- List<User> findAll(); -->
    <select id="findAll" resultMap="user1_NResultMap">
        <!-- 连表查询 -->
        SELECT * FROM USER u LEFT JOIN order_form of ON u.id = of.user_id
    </select>

</mapper>

4.3 N-N

需求: 查询出全部用户信息的同时可以查询出用户的全部角色信息。用户与角色是:N-N关系!

实现步骤:

1、必须在实体类中先建立N-N的关系。

​ 同样我们需要建立一个角色类:

public class Role {
    private int roleId;
    private String roleName;
    private String roleDetail;
  
  	//getter setter toString省略
}

然后去用户类去关联:(其他代码参照1-N用户类)

// 一个用户对象 一批角色对象: N-N的关系
private List<Role> roles;

2、实现N-N的关联映射查询实现:实现上与1-N一模一样。单独来看都是1-N的关系。

这里的配置和1-N一样,直接上代码。

public class MybatisDemo {
    @Test
    public void queryN_N(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、查询用户对象信息(目的是可以查询出1-N关联的订单信息 )
        List<User> users = userMapper.findAll();
        System.out.println(users);
        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}
public interface UserMapper {
    List<User> findAll();
}
<?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.itheima.dao.UserMapper">

    <resultMap id="userResultMap" type="User">
        <id property="id" column="id"/>
        <result property="userName" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </resultMap>

    <resultMap id="roleFormResultMap" type="Role">
        <id property="roleId" column="role_id"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDetail" column="role_Detail"/>
    </resultMap>

    <!-- extends:先继承用户基础的结果集映射用户对象本身的信息,里面再关联映射关联字段的信息 -->
    <resultMap id="userN_NResultMap" type="User" extends="userResultMap">
        <!-- 一个用户包含一批订单,官方规定了N_N与1-N的字段必须使用如下标签映射: collection -->
        <!--
         property: 多方的属性名
         javaType:多方的属性类型
         ofType: 集合中每个元素的类型
         resultMap:多方映射
         -->
        <collection property="roles" javaType="list" ofType="Role"
                    resultMap="roleFormResultMap"/>
    </resultMap>

    <!-- List<User> findAll(); -->
    <select id="findAll" resultMap="userN_NResultMap">
        <!-- 连表查询 -->
        SELECT * FROM USER u LEFT JOIN user_role ur ON u.id = ur.`user_id`
        LEFT JOIN role r ON ur.`role_id` = r.`role_id`
    </select>

</mapper>

4.4 延迟加载的配置优化

我们会发现,在之前代码中,在配置结果集的时候我们要根据每一个类去配置它的结果集,这种做法带来的坏处就是代码量过多,如果一个数据很多列,那么我们需要一个个的写,这是不可能的。那么我们就给了一种新的配置优化的做法。我们在前面会根据每个类去创建它的映射文件,比如User类就在dao包下创建UserMapper.java和UserMapper.xml,UserCard类就在dao包下创建UserCardMapper.java和UserCardMapper.xml。

那么我们就可以把之前的这段结果集代码给删除。

<resultMap id="userCardResultMap" type="UserCard">
    <id property="id" column="id"/>
    <result property="height" column="height"/>
    <result property="weight" column="weight"/>
    <result property="married" column="married"/>
</resultMap>

<resultMap id="userResultMap" type="User">
    <id property="id" column="id"/>
    <result property="userName" column="username"/>
    <result property="birthday" column="birthday"/>
    <result property="sex" column="sex"/>
    <result property="address" column="address"/>
</resultMap>

然后在UserCardMapper.java文件下创建一个方法

public interface UserCardMapper {
    UserCard getUserCardById(int userId);
}

再去UserCardMapper.xml文件配置好映射文件。记住namespace: 必须申明成被服务接口的路径。

<?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.itheima.dao.UserCardMapper">
    <!-- UserCard getUserCardById(int userId); -->
    <select id="getUserCardById" resultType="UserCard">
        select * from user_card where id = #{xxzxz}
    </select>
</mapper>

然后再回到UserMapper.xml文件中进行配置,声明关联的字段、根据当前用户的哪个字段值去查询关联对象、 关联字段的类型、然后用select去提取UserCardMapper.java的相对路径,表示去这个关联对象中去查询信息。

<mapper namespace="com.itheima.dao.UserMapper">

    <resultMap id="user1_1ResultMap" type="User" >
        <id property="id" column="id"/>
        <!-- 开始配置1-1关联映射的身份证字段对象信息:userCard
             官方规定了1-1关联的字段映射必须使用一个标签:association
                property:申明关联的1-1字段
                javaType : 关联字段的类型
                column:根据当前用户的哪个字段值去查询关联对象!
                select:去那当前行的column列值去查询关联的这个字段的对象信息!

                拓展:
                 fetchType="eager" 立即加载
                 fetchType="lazy" 延迟加载
        -->
        <association property="userCard" column="id" javaType="UserCard"
                     select="com.itheima.dao.UserCardMapper.getUserCardById"/>
    </resultMap>

    <!--  List<User> findAll(); -->
    <select id="findAll" resultMap="user1_1ResultMap">
        select * from user
    </select>

</mapper>

通过这样配置,我们会发现代码量少了很多。

4.5 1-1延迟加载

延迟加载:需要的时候才去查询,不需要的时候不要查询!!提高数据操作的性能!

Mybatis默认是没有开启延迟加载的,本案例是可以支持延迟加载的,需要开启延迟加载才可以。

延迟加载的具体含义:不需要的时候不会加载关联的字段信息查询,需要的时候才会触发查询关联字段信息。

在mybatis-config.xml核心配置文件中配置。

<!--配置mybatis中全局设置-->
<settings>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

同样的做法:

需求:查询出某个用户的身份证信息。用户信息和身份证信息是 一 对 一的关系。

实现步骤:
1、必须在实体类中先建立1-1的关系。

​ 忽略。可以翻看上面1-1的代码。

2、实现1-1的关联映射查询实现。

UserMapper.java

public interface UserMapper {
    List<User> findAll();
}

UserMapper.xml

UserMapper.xml文件中进行配置,声明关联的字段、根据当前用户的哪个字段值去查询关联对象、 关联字段的类型、然后用select去提取UserCardMapper.java的相对路径,表示去这个关联对象中去查询信息。

SQL语句直接查询整个user,然后再去UserCardMapper.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.itheima.dao.UserMapper">

    <resultMap id="user1_1ResultMap" type="User" >
        <id property="id" column="id"/>
        <!-- 开始配置1-1关联映射的身份证字段对象信息:userCard
             官方规定了1-1关联的字段映射必须使用一个标签:association
                property:申明关联的1-1字段
                javaType : 关联字段的类型
                column:根据当前用户的哪个字段值去查询关联对象!
                select:去那当前行的column列值去查询关联的这个字段的对象信息!

                拓展:
                 fetchType="eager" 立即加载
                 fetchType="lazy" 延迟加载
        -->
        <association property="userCard" column="id" javaType="UserCard"
                     select="com.itheima.dao.UserCardMapper.getUserCardById"/>
    </resultMap>

    <!--  List<User> findAll(); -->
    <select id="findAll" resultMap="user1_1ResultMap">
        select * from user
    </select>

</mapper>

UserCardMapper.java

定义方法传入userId(其实就是主键)

public interface UserCardMapper {
    UserCard getUserCardById(int userId);
}

UserCardMapper.xml

namespace: 必须申明成被服务接口的路径

把userId传进来做主键

<?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.itheima.dao.UserCardMapper">
    <!-- UserCard getUserCardById(int userId); -->
    <select id="getUserCardById" resultType="UserCard">
        select * from user_card where id = #{xxzxz}
    </select>
</mapper>

最后我们来看运行结果:

public class MybatisDemo {
    @Test
    public void query1_1(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、查询用户对象信息(目的是可以查询出1-1关联的身份证信息 )
        List<User> users = userMapper.findAll();
        for(User u : users){
            System.out.println(u.getUserName());
        }
        System.out.println("------------------------------------");
        for (User u : users){
            System.out.println("身份证:" + u.getUserCard());
        }
        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

在这里插入图片描述

这是没有开启延迟加载的,会直接把数据加载出来。

在这里插入图片描述

这是开启延迟加载的,会等到需要取数据的时候再去加载。

4-6 1-N的延迟加载

其实1-N和N-N是一样的,然后代码的配置也是跟1-1差不多的,所以直接上代码。

需求: 查询出用户对象的时候,可以查询出它的全部订单信息。用户 与 订单是 1-N的关系!

实现步骤:

​ 1、必须在实体类中先建立1-N的关系。

略。

​ 2、实现1-N的关联映射查询实现。

public interface UserMapper {
    List<User> findAll();
}

注意这个UserMapper.xml中的 fetchType=“lazy” : 延迟记载, fetchType=“eager” : 立即加载是就近原则,这里设置了,核心配置设置文件也设置了,优先选择这里,因为比较近。

collection是1-N和N-N的。

<?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.itheima.dao.UserMapper">



    <resultMap id="user1_NResultMap" type="User">
        <id column="id" property="id"></id>
        <!-- 一个用户包含一批订单,官方规定了1-N的字段必须使用如下标签映射: collection -->
        <!--
         property: 多方的属性名
         javaType:多方的属性类型
         ofType: 集合中每个元素的类型
         column: 根据当前用户的哪个字段的值去查询关联的订单信息对象
         select : 根据用户id查询它的订单信息的方法,
         fetchType="lazy" : 延迟记载
         fetchType="eager" : 立即加载
         -->
        <collection property="orderForms" javaType="list" ofType="OrderForm" fetchType="lazy"
               column="id" select="com.itheima.dao.OrderFormMapper.getOrdersByUserId"     />
    </resultMap>

    <!-- List<User> findAll(); -->
    <select id="findAll" resultMap="user1_NResultMap">
        select * from user
    </select>

</mapper>
public interface OrderFormMapper {
    List<OrderForm> getOrdersByUserId(int userId);
}
<?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.itheima.dao.OrderFormMapper">
    <!--     List<OrderForm> getOrdersByUserId(int userId);
        -->
    <select id="getOrdersByUserId" resultType="OrderForm">
        select * , user_id userId , create_time createTime from order_form where user_id = #{xDsd}
    </select>
</mapper>
public class MybatisDemo {
    @Test
    public void query1_N(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、查询用户对象信息(目的是可以查询出1-N关联的订单信息 )
        List<User> users = userMapper.findAll();
        for (User user : users) {
            System.out.println(user.getUserName());
        }
        System.out.println("------------------------------------------------");
        for(User u : users){
            System.out.println(u.getOrderForms());
        }
        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

没有延迟加载

在这里插入图片描述

延迟加载
在这里插入图片描述

5 Mybatis注解式开发

5.1 注解式开发入门

注解式开发的核心:可以不再提供映射文件,SQL语句直接写在接口方法上。

这种好处就是我们再也不需要取配置繁杂的映射文件。

下面我们用注解式实现SQL的增删改查。

5.1.1 注解式查询

MybatisDemo

@Test
public void query(){
    // 1、获取与数据库的会话
    SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
    // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 3、查询用户对象信息
    List<User> users = userMapper.findAll();
    System.out.println(users);
    // 4、关闭会话
    MybatisSqlSessionFactoryUtils.close(sqlSession);
}

UserMapper

@Select("select * from user")
List<User> findAll();
5.1.2 注解式增加

MybatisDemo

@Test
public void add(){
    // 1、获取与数据库的会话
    SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
    // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 3、添加用户对象信息
    User user = new User();
    user.setAddress("吉山区");
    user.setUserName("吉山一哥");
    user.setBirthday(new Date());
    user.setSex('男');
    int count = userMapper.save(user);
    System.out.println("添加了:"+ count);
    sqlSession.commit();
    // 4、关闭会话
    MybatisSqlSessionFactoryUtils.close(sqlSession);
}

UserMapper

@Insert("insert into user(userName,birthday,sex,address) values (#{userName} , #{birthday} , #{sex} , #{address})")
int save(User user);
5.1.3 注解式删除

MybatisDemo

@Test
public void delete(){
    // 1、获取与数据库的会话
    SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
    // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 3、删除用户对象信息
    int count = userMapper.deleteById(5);
    System.out.println("删除了:"+ count);
    sqlSession.commit();
    // 4、关闭会话
    MybatisSqlSessionFactoryUtils.close(sqlSession);
}

UserMapper

@Delete("delete from user where id = #{xdsaf}")
int deleteById(int id);
5.1.4 注解式修改

MybatisDemo

@Test
    public void update(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、修改用户对象信息
        User user = new User();
        user.setId(2);
        user.setAddress("吉山区");
        user.setUserName("吉山西施");
        int count = userMapper.update(user);
        System.out.println("修改了:"+ count);
        sqlSession.commit();
        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

UserMapper

@Update("update user set userName = #{userName} , address = #{address} where id = #{id}")
int update(User user);

5.2 注解配置结果集映射

那在注解式开发中如果要配置那些实体类属性名和列名不一样的字段要怎么办呢?

我们同样也是用配置结果集映射来解决,但是现在需要查询谁就在谁的接口中的方法进行注解。

MybatisDemo

@Test
public void query(){
    // 1、获取会话
    SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
    // 2、数据访问层接口的代理对象
    RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
    // 3、查询全部角色信息
    List<Role> roles = roleMapper.findAll();
    System.out.println(roles);
    // 4、关闭会话
    MybatisSqlSessionFactoryUtils.close(sqlSession);
}

RoleMapper

public interface RoleMapper {
    @Select("select * from role")
    @Results({
            @Result(column = "role_id" , property = "roleId") ,
            @Result(column = "role_name" , property = "roleName") ,
            @Result(column = "role_detail" , property = "roleDetail")
    })
    List<Role> findAll();
}

5.3 注解获取主键

插入数据获取主键使用注解:@SelectKey

MybatisDemo

@Test
public void addGetKey(){
    // 1、获取与数据库的会话
    SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
    // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 3、添加用户对象信息
    User user = new User();
    user.setAddress("吉山区");
    user.setUserName("吉山一哥");
    user.setBirthday(new Date());
    user.setSex('男');
    int count = userMapper.addGetKey(user);
    System.out.println("添加了:"+ count);
    System.out.println("主键是:"+ user.getId());
    sqlSession.commit();
    // 4、关闭会话
    MybatisSqlSessionFactoryUtils.close(sqlSession);
}

UserMapper

@Insert("insert into user(userName,birthday,sex,address) values (#{userName} , #{birthday} , #{sex} , #{address})")
/*
    statement:获取主键的SQL语句
    keyProperty: 实体类中主键的属性名
    keyColumn:表中主键列的名字
    resultType:主键数据类型
    before: false 之后 true之前
*/
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id",
        resultType = Integer.class, before = false)

5.4 注解关联映射

其实注解式开发中1-1,1-N,N-N的格式都是一样的,区别就是使用的关键字不同。

注意:1-1关联映射使用核心注解是:@One。

注意:1-N(N-N)关联映射使用核心注解是:@Many

MybatisDemo

public class MybatisDemo03 {
    @Test
    public void query(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、查询用户对象信息(身份证,订单)
        List<User> users = userMapper.findAll1_1();
        System.out.println(users);
        // 4、关闭会话
        MybatisSqlSessionFactoryUtils.close(sqlSession);
    }
}

在UsrMapper中进行配置,

**column:主表的主键, **

**property:另一方的属性名, **

one:配置一对一的关联关系,

many:配置一对多的关联关系,

select:关联另一方接口中的方法地址,

fetchType:注解中定义的延迟加载。

// 1-1
// 1-N
// N-N
@Select("select * from user")
@Results({
        @Result(column = "id" ,  property = "id"),
        //column:主表的主键, property:另一方的属性名, one:配置一对一的关联关系
        // 1-1  用户 身份证
        @Result(column = "id", property = "userCard",
                  one = @One(select = "com.itheima.dao.UserCardMapper.getUserCardById", fetchType = FetchType.LAZY)),
        // 1-N 用户 订单信息
        @Result(column = "id", property = "orderForms",
                many = @Many(select = "com.itheima.dao.OrderFormMapper.getOrdersByUserId", fetchType = FetchType.LAZY))
})
List<User> findAll1_1();

UserCardMapper

public interface UserCardMapper {
    @Select("select * from user_card where id = #{xsdsaf}")
    UserCard getUserCardById(int userId);
}

OrderFormMapper

public interface OrderFormMapper {
    @Select("select * , user_id userId,create_time createTime from order_form where user_id = #{xxxx}")
    List<OrderForm> getOrdersByUserId(String userId);
}

6 Mybatis缓存

​ 用mybatis从数据库中查询数据,如果有多个用户使用同一个SQL语句来查询记录,得到相同的查询结果。如果表中记录很多,查询速度比较慢。使用缓存的目的就是为了提升查询的速度。缓存是内存中一个区域,保存已经查询过的记录。

​ 缓存的思想:把常用的查询结果数据存入到内存中,下次可以直接在内存中寻找使用。避免每次都查数据库。缓存是为了提高系统的性能,节约数据库的宝贵资源

在这里插入图片描述

6.1 一级缓存

一级缓存:默认是开启的,一次会话中的多次相同操作,只有第一次会去数据库查询,后续直接去缓冲中拿数据!

1.第1次查询记录,将查询到的数据写入到缓存中

2.第2次查询的时候,首先从缓存中去读取数据,如果缓存中有数据,直接返回,而不去访问数据库了

3.如果这个会话执行了添加,修改,删除,提交,会关闭清空当前会话的1级缓存

在这里插入图片描述

@Select("select * from user")
List<User> findAll();
@Test
public void query1(){
    // 1、获取与数据库的会话
    SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
    // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 3、查询用户对象信息(目的是可以查询出1-1关联的身份证信息 )
    List<User> users = userMapper.findAll();
    for(User u : users){
        System.out.println(u.getUserName());
    }

    //sqlSession.commit(); // 提交也会清空一级缓存!
    System.out.println("------------------------------------");
    // 以下操作进一级缓存拿数据!
    List<User> users1 = userMapper.findAll();
    for(User u : users1){
        System.out.println(u.getUserName());
    }

    // 4、关闭会话
    MybatisSqlSessionFactoryUtils.close(sqlSession);
}

一级缓存效果如图,就是直接拿数据,不用再访问数据库。
在这里插入图片描述

一级缓存提交之后的效果:
在这里插入图片描述

6.2 二级缓存

默认是关闭的。
1.开启二级缓存配置:核心配置文件mybatis-config.xml

       <!-- 开启二级缓存 -->
             <setting name="cacheEnabled" value="true"/>

​ 2.如果是映射文件中的查询操作需要使用二级缓存,同学们直接在映射文件中上方加上 标签

 <cache/> <!-- 本映射文件中的全部查询将开启二级缓存!-->

​ 3.注解的方式实现二级缓存
@CacheNamespace //对注解下的接口中的全部查询实现二级缓存

4.增删操作也会清空二级缓冲

​ 注意:二级缓存的对象必须实现序列化接口,如果缓存的对象太多,Mybatis可以把对象缓存到文件中。

public class User implements Serializable

在这里插入图片描述

 @Test
    public void query2(){
        // 1、获取与数据库的会话
        SqlSession sqlSession = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 3、查询用户对象信息(目的是可以查询出1-1关联的身份证信息 )
        List<User> users = userMapper.findAll();
        for(User u : users){
            System.out.println(u.getUserName());
        }
        sqlSession.close(); //会话关闭!!!

        System.out.println("-----------------------------------------------------");
        // 增删操作也会清空二级缓冲!!
//        SqlSession sqlSession2 = MybatisSqlSessionFactoryUtils.getSqlSession();
//        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
//        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
//        userMapper2.deleteById(6);
//        sqlSession2.commit();
//        sqlSession2.close();


        System.out.println("------------------------------------");
        // 以下操作进一级缓存拿数据!
        // 1、获取与数据库的会话
        SqlSession sqlSession1 = MybatisSqlSessionFactoryUtils.getSqlSession();
        // 2、获取用户的数据访问层接口的代理对象,会自动找映射文件:UserMapper.xml文件
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        // 3、查询用户对象信息(目的是可以查询出1-1关联的身份证信息 )
        List<User> users1 = userMapper1.findAll();
        for(User u : users1){
            System.out.println(u.getUserName());
        }
        sqlSession1.close(); //会话关闭!!!
    }

@CacheNamespace  //对注解下的接口中的全部查询实现二级缓存
public interface UserMapper {
    @Select("select * from user")
    List<User> findAll();

    @Delete("delete from user where id = #{xadfds}")
    void deleteById(int id);
}

开启二级缓存:
在这里插入图片描述

二级缓存提交:

在这里插入图片描述
整个重新加载一遍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值