Mybatis笔记总结

MyBatis总结

1.SSM框架介绍(Spring+SpringMVC+MyBatis)
Junit测试、log4j日志
2.MyBatis框架
1)MyBatis框架介绍(ORM对象关系映射)工作在三层架构持久层
2)MyBatis的第一个程序:student插入信息
MyBatis的主配置文件:连接数据库的四要素
映射文件:Sql语句
实现类:SqlSessionFactoryBuilder->SqlSessionFactory->SqlSession->执行sql命令
3)配置文件解析:定义别名、加载属性文件、注册映射文件、配置多个连接环境、使用日志、定义工具类
4)单表的增删改查(CURD)
5)mapper的动态代理实现增删改查
使用动态代理条件:
接口和对应映射文件在同一包下、映射文件的namespace应该是接口的全限定性类名、sqlSession.getMapper(映射文件对应接口.class)
6)动态SQL标签
、、、、sql片段
7)关联关系查询:1:n、n:1、1:1、n:m、自关联
多表连接查询、多表单独查询
的用法
8)字段名和属性名不一致:起别名、resultMap
9)延迟加载:关联关系中关联对象的多表单独查询能使用延迟加载策略
直接加载默认的
侵入式延迟加载
深度延迟加载
延迟加载的开关:主配置文件中
10)缓存
一级缓存和二级缓存(作用范围都是namespace)
一级缓存的生命周期和SQLSession相同
二级缓存的生命周期和整个应用的生命周期相同
一级缓存默认开启,无开关设置
二级缓存默认关闭,可以设置打开或关闭
二级缓存的全局开关在主配置文件中默认为true
映射文件中可以打开在该命名空间的二级缓存
在映射文件中的也可以单独设置使用不使用二级缓存,用useCache
11)MyBatis注解
注解的基本用法:@Insert、@Update、@Delete、@Select
使用动态Sql:@InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider
生成动态sql的方法:字符串拼接(StringBuffer)、SQL类(匿名内部类)

一、第一个mybatis程序

  1. 在pow文件中导入mybatis相关包
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

2.编写mybatis核心配置文件
Mybatis笔记总结_第1张图片
3.创建bean类

package com.wcc.beans;
public class Student {
     
    private Integer id;
    private String name;
    private int age;
    private double score;

    public Student() {
     
    }

    public Student(String name, int age, double score) {
     
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public Integer getId() {
     
        return id;
    }

    public void setId(Integer id) {
     
        this.id = id;
    }

    public String getName() {
     
        return name;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    public int getAge() {
     
        return age;
    }

    public void setAge(int age) {
     
        this.age = age;
    }

    public double getScore() {
     
        return score;
    }

    public void setScore(double score) {
     
        this.score = score;
    }

    @Override
    public String toString() {
     
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

4.创建dao接口类

package com.wcc.dao;

import com.wcc.beans.Student;

public interface IStudentDao {
     
    void insertStudent(Student student);
}

5.创建dao接口实现类

package com.wcc.dao;

import com.wcc.beans.Student;
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.IOException;
import java.io.InputStream;

/*
* 1.SqlSessionFactoryBuilder
* 2.SqlSessionFactory
* 3.SqlSession
* */
public class StudentDaoImpl implements IStudentDao {
     
    @Override
    public void insertStudent(Student student) {
     
        SqlSession session = null;
        try {
     
            //1.创建SqlSessionFactoryBulider对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //2.加载主配置文件为InputStream类型
            InputStream is = Resources.getResourceAsStream("mybatis.xml");
            //3.由builder创建SqlSessionFactory
            SqlSessionFactory factory = builder.build(is);
            //4.由factory创建SqlSession
            session = factory.openSession();
            //5.执行插入命令
            session.insert("insertStu",student);
            //6.提交
            session.commit();
        } catch (IOException e) {
     
            e.printStackTrace();
        }finally {
     
            //7.关闭SqlSession
            if (session != null){
     
                session.close();
            }
        }
    }
}

6.创建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为dao接口层的权限定性接口名
<mapper namespace="com.wcc.dao.IStudentDao">
    //parameterType为POJO的权限定性类名
    <insert id="insertStu" parameterType="com.wcc.beans.Student">
        insert into student (name,age,score) values (#{
     name},#{
     age},#{
     score})
    </insert>
</mapper>

7.创建测试类
*/

public class MyTest
{
     
    @Test
    public void test()
    {
     
        IStudentDao studentDao = new StudentDaoImpl();
        Student student = new Student("小明",20,98.5);
        studentDao.insertStudent(student);
    }
}

二、优化dao接口实现类

1.将sqlSessionFactory定义为单例模式,提前将Sqlsession创建出来。这样dao的实现类每次增删改查就不用在创建sqlSession了。

package com.yyf.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.IOException;
import java.io.InputStream;

public class MyBatisUtil {
     
    //因为SqlSessionFactory是重量级组件,把它定义为单例的对象
    private static SqlSessionFactory factory = null;
    public static SqlSession getSqlSession() throws IOException {
     
        if (factory == null){
     
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.xml"));
        }
        return factory.openSession();
    }
}

2.添加log2j日志,便于调试程序
1)在pow文件中引入log2j的依赖

  <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.12.1</version>
    </dependency>

2)java目录下编写log2j2.xml文件

```java
<?xml version="1.0" encoding="UTF-8"?>

<configuration status="OFF">
	<appenders>
		<Console name="myConsole" target="SYSTEM_OUT">
			<PatternLayout pattern="[%-5p] %m%n" />
		</Console>
	</appenders>
	
	<loggers>
		<logger name="com.yyf.dao.IStudentDao" level="trace" additivity="false">
			<appender-ref ref="myConsole" />
		</logger>
		<!--<root level="debug">
			<appender-ref ref="myConsole" />
		</root>-->
	</loggers>
</configuration>

3.给主配置文件mybatis.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">
<configuration>
    <!--加载jdbc属性文件-->
    <properties resource="jdbc.properties"/>

    <!--给实体类定义别名-->
    <typeAliases>
        <!--给指定的类定义指定的别名-->
        <!--<typeAlias type="com.yyf.beans.Student" alias="Stu"/>-->
        <!--给指定包下所有的类起别名,默认别名为该类的简单类名-->
        <package name="com.yyf.beans"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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>
        <!--<environment id="oracle">
            <transactionManager type="JDBC"/>
            <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>
        <!--注册映射文件-->
        <!--
            resource一般加载的是内部资源映射文件
        -->
        <!--<mapper resource="com/yyf/dao/mapper.xml"/>-->
        <!--url一般加载项目的外部资源-->
        <!--<mapper url=""/>-->
        <!--
            1.映射文件和对应的接口在同一个包下
            2.映射文件的名称必须和接口的简单类名相同
            3.映射文件中namespace的值必须是对应接口的全限定性类名
        -->
        <!--<mapper class="com.yyf.dao.IStudentDao"/>-->
        <!--
            1.映射文件和对应的接口在同一个包下
            2.映射文件的名称必须和接口的简单类名相同
            3.映射文件中namespace的值必须是对应接口的全限定性类名
            4.(可以不满足)使用动态mapper
        -->
        <package name="com.yyf.dao"/>

    </mappers>
</configuration>

4.将sqlSessionFactory定义为单例模式后的dao接口实现类

package com.yyf.dao;

import com.yyf.beans.Student;
import com.yyf.utils.MyBatisUtil;
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.IOException;
import java.io.InputStream;
import java.util.List;
/*
* 1.SqlSessionFactoryBuilder
* 2.SqlSessionFactory
* 3.SqlSession
* */
public class StudentDaoImpl implements IStudentDao {
     
    //插入学生信息
    @Override
    public void insertStudent(Student student) {
     
        SqlSession session = null;
        try {
     
            //1.通过工具类获取SqlSession对象
            session = MyBatisUtil.getSqlSession();
            //2.执行插入命令
            session.insert("insertStu",student);
            //3.提交
            session.commit();
        } catch (IOException e) {
     
            e.printStackTrace();
        }finally {
     
            //4.关闭SqlSession
            if (session != null){
     
                session.close();
            }
        }
    }

三、使用mapper代理

使用Mapper的动态代理要求:
* 1.sqlSession.getMapper()中的参数为所要代理接口的类型
* 2.映射文件的namespace属性值必须为所代理接口的全限定性类名
* 3.映射文件中的statement的id必须和代理接口中的相应方法名称相同
1.测试类中直接调用sqlSession

package com.yyf;
import static javafx.scene.input.KeyCode.I;
import static org.junit.Assert.assertTrue;
import com.yyf.beans.Student;
import com.yyf.dao.IStudentDao;
import com.yyf.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyTest
{
     
    /**
     * 使用Mapper的动态代理要求:
     * 1.sqlSession.getMapper()中的参数为所要代理接口的类型
     * 2.映射文件的namespace属性值必须为所代理接口的全限定性类名
     * 3.映射文件中的statement的id必须和代理接口中的相应方法名称相同
     */
    SqlSession sqlSession = null;
    IStudentDao studentDao = null;
    @Before
    public void before() throws IOException {
     
        sqlSession = MyBatisUtil.getSqlSession();
        studentDao = sqlSession.getMapper(IStudentDao.class);
        System.out.print("代理:"+studentDao);
    }

    @After
    public void after(){
     
        if (sqlSession != null){
     
            sqlSession.close();
        }
    }
    //学生信息插入
    @Test
    public void test01()
    {
     
        Student student = new Student("小明3",20,98.5);
        studentDao.insertStudent(student);
        sqlSession.commit();
        System.out.print("插入后:"+student);
    }
  }

1.1小知识点
使用动态代理查询时,会根据所需要的结果默认调用方法selectList或selectOne方法。
如果返回结果为List,则默认调用selectList方法,否则都默认调用selectOne方法
如果需要返回map时,需要在对应的方法接口上添加注解@MapKey(value=“对应字段名”)
dao接口中代码

@MapKey(value = "name")
    Map<String,Student> selectAllStudentMap();

对应xml中代码

  <select id="selectAllStudentMap" resultType="Student">
        select id,name,age,score from student
    </select>

对应测试类中的代码

  //使用动态代理查询时,会根据需要的返回结果默认调用方法selectList或selectOne,
    //如果返回结果为List,则默认调用selectList方法,否则都默认调用selectOne方法
    //如果需要返回map时,需要在对应的接口方法上添加注解@MapKey(value="")
    //查询全部学生信息Map
    @Test
    public void test08()
    {
     
        Map<String,Student> map = studentDao.selectAllStudentMap();
        System.out.println(map.get("小明"));
    }

1.2小知识点
如果在接口层需要传入2个或者多个参数时,需要使用@Param()注解
1.3小知识点
总结:#{}中间可以填写什么内容
1.传入参数为对象时,中间写对象的属性名
2.传入参数为一个基本数据类型或String类型,中间写任意值,就是一个占位符
3.传入参数为map,map的key对应的值是基本数据类型,中间写map的key
4.传入参数为map,map的key对应的值是对象,中间写map的key.属性

List<Student> selectStudentByCondition3(@Param("name") String name,@Param("age") int age);

注解可以和参数名相同也可以不同,#{}这里填写Param中矫正的参数名

1.4常用查询语句

<?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.yyf.dao.IStudentDao">

    <!--
        1.parameterType可以省略,如果不省略就必须写正确
        2.传入的参数是一个对象,#{
     对象的属性名},默认调用对象属性的get方法
    -->
    <insert id="insertStudent">
        insert into student (name,age,score) values (#{
     name},#{
     age},#{
     score})
    </insert>

    <delete id="deleteStudentById">
        delete from student where id = #{
     id}
    </delete>

    <update id="updateStudent">
        update student set name=#{
     name},age=#{
     age},score=#{
     score} where id=#{
     id}
    </update>

    <insert id="insertStudentCatchId">
        insert into student (name,age,score) values (#{
     name},#{
     age},#{
     score})
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            <!--select @@identity-->
            select last_insert_id()
        </selectKey>
    </insert>

    <!--
        resultType或者resultMap是必须的,并不是集合的类型,而是集合中单个元素的类型
    -->
    <select id="selectAllStudent" resultType="Student">
        select id,name,age,score from student
    </select>

    <select id="selectAllStudentMap" resultType="Student">
        select id,name,age,score from student
    </select>

    <select id="selectStudentById" resultType="Student">
        select id,name,age,score from student where id = #{
     id}
    </select>

    <select id="selectStudentByName" resultType="Student">
        select id,name,age,score from student where name like '%' #{
     name} '%'
        <!--select id,name,age,score from student where name like concat('%',#{
     name},'%')-->
        <!--select id,name,age,score from student where name like '%${value}%'-->
    </select>

    <select id="selectStudentByCondition" resultType="Student">
        select id,name,age,score from student
        where name like '%' #{
     name} '%' and age &lt; #{
     age}
    </select>

    <select id="selectStudentByCondition2" resultType="Student">
        <!--select id,name,age,score from student
        where age &gt; #{
     min} and age &lt; #{
     max}-->
        select id,name,age,score from student
        where age &gt; #{
     stu1.age} and age &lt; #{
     stu2.age}
    </select>

    <select id="selectStudentByCondition3" resultType="Student">
        select id,name,age,score from student
        where name like '%' #{
     name} '%' and age &lt; #{
     age}
    </select>
    <!--
        总结:#{
     }中间可以填写什么内容
        1.传入参数为对象时,中间写对象的属性名
        2.传入参数为一个基本数据类型或String类型,中间写任意值,就是一个占位符
        3.传入参数为map,map的key对应的值是基本数据类型,中间写map的key
        4.传入参数为map,map的key对应的值是对象,中间写map的key.属性
    -->
</mapper>

四、动态sql语句

1.if的使用

 <select id="selectStudentByIf" resultType="Student">
        select id,name,age,score from student where 1=1
        <if test="name != null and name != ''">
            and name like '%' #{
     name} '%'
        </if>
        <if test="age > 0">
            and age &lt; #{
     age}
        </if>
    </select>

注意:如果没有写1=1 会报错
2.where的使用

 <select id="selectStudentByWhere" resultType="Student">
        select id,name,age,score from student
        <where>
            <if test="name != null and name != ''">
                name like '%' #{
     name} '%'
            </if>
            <if test="age > 0">
                and age &lt; #{
     age}
            </if>
        </where>
    </select>

注意:当where和if搭配时,第一个if标签不要加and,其他的加and
3.choose when otherwise标签

 <!--
        如果输入了name查询条件,不论age有没有输入,都只按照name查询;
        如果没有输入name,只输入了age,按照age条件查询;
        如果两个都没有输入,查询不到任何结果。
    -->
    <select id="selectStudentByChoose" resultType="Student">
        select id,name,age,score from student
        <where>
            <choose>
                <when test="name != null and name != ''">
                    name like '%' #{
     name} '%'
                </when>
                <when test="age > 0">
                    age &lt; #{
     age}
                </when>
                <otherwise>
                    1!=1
                </otherwise>
            </choose>
        </where>
    </select>

4.1foreach标签 传入array数组

<select id="selectStudentByForeachArray" resultType="Student">
        select id,name,age,score from student
        <if test="array != null and array.length > 0">
            where id in
            <foreach collection="array" item="id" open="(" close=")" separator=",">
                #{
     id}
            </foreach>
        </if>
    </select>

4.2foreach标签 传入list集合

<select id="selectStudentByForeachList" resultType="Student">
        select id,name,age,score from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" item="id" open="(" close=")" separator=",">
                #{
     id}
            </foreach>
        </if>
    </select>

4.3foreach标签 传入list《T》集合

<select id="selectStudentByForeachList2" resultType="Student">
        select id,name,age,score from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" item="stu" open="(" close=")" separator=",">
                #{
     stu.id}
            </foreach>
        </if>
    </select>

5.sql标签 include标签 用于替换重复sql语句

<select id="selectStudentByFragment" resultType="Student">
    <include refid="xxx"/>
    <if test="list != null and list.size > 0">
        where id in
        <foreach collection="list" item="stu" open="(" close=")" separator=",">
            #{
     stu.id}
        </foreach>
    </if>
</select>
<sql id="xxx">
    select id,name,age,score from student
</sql>

6.if+set标签
修改用户信息时有时候不会修改全部,就使用set标签

<update id="modify" parameterType="User">
update user 
  <set>
      <if test="name!=null">
        name=#{
     name}
     </if>
     </set>
         where id = #{
     id}
   </update>

7.if+trim

<update id="modify" parameterType="User">
update user 
  <trim prefix="set" suffixOverrides="," suffix="where id = #{id}">
      <if test="name!=null">
        name=#{
     name}
     </if>
     </trim>
   </update>

五,关联关系查询

1.一对多(多表连接查询)
1.1实体类的定义(只展示重要部分)

//一方为主查询,关联属性定义在一方
public class Country {
     
    private Integer cid;
    private String cname;
    //关联属性
    private Set<Minister> ministers;
public class Minister {
     
    private Integer mid;
    private String mname;

1.2.dao接口

public interface ICountryDao {
     
    Country selectCountryById(int id);
}

1.3.映射文件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.yyf.dao.ICountryDao">
    <!--多表链接查询-->
    <resultMap id="countryMap" type="Country">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
        <collection property="ministers" ofType="Minister">
            <id column="mid" property="mid"/>
            <result column="mname" property="mname"/>
        </collection>
    </resultMap>
    <select id="selectCountryById" resultMap="countryMap">
        select cid,cname,mid,mname from country,minister
        where cid=#{
     id} and countryId=cid
    </select>
</mapper>

注意:属性类型为集合类型时 用collection标签 类型用ofType

<collection property="ministers" ofType="Minister">

属性类型为普通类型时用association 标签 类型用javaType

 <association property="ministers" javaType="Minister"> </association>

2.一对多(多表单独查询)

<!--多表单独查询-->
<select id="selectMinisterByCountryId" resultType="Minister">
    select mid,mname from minister where countryId=#{
     cid}
</select>
<resultMap id="countryMap" type="Country">
    <id column="cid" property="cid"/>
    <result column="cname" property="cname"/>
    <collection property="ministers"
                ofType="Minister"
                select="selectMinisterByCountryId"
                column="cid">
    </collection>
</resultMap>
<select id="selectCountryById" resultMap="countryMap">
    select cid,cname from country
    where cid=#{
     id}
</select>

3.多对一(多表连接查询)
3.1实体类

//一方为主查询,关联属性定义在一方
public class Country {
     
    private Integer cid;
    private String cname;
//以多方为主查询,关联属性定义在多方
public class Minister {
     
    private Integer mid;
    private String mname;
    //关联属性,多对一关联属性是一个对象
    private Country country;

3.2dao接口

public interface IMinisterDao {
     
    Minister selectMinisterById(int id);
}

3.3映射文件xml

<resultMap id="ministerMap" type="Minister">
    <id column="mid" property="mid"/>
    <result column="mname" property="mname"/>
    <association property="country" javaType="Country">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
    </association>
</resultMap>
<select id="selectMinisterById" resultMap="ministerMap">
    select mid,mname,cid,cname from minister,country
    where mid=#{
     id} and cid =countryId
</select>

4.多对一(多表单独查询)

<!--多表单独查询-->
<select id="selectCountryById" resultType="Country">
    select cid,cname from country where cid = #{
     countryId}
</select>
<resultMap id="ministerMap" type="Minister">
    <id column="mid" property="mid"/>
    <result column="mname" property="mname"/>
    <association property="country"
                 javaType="Country"
                 select="selectCountryById"
                 column="countryId"/>
</resultMap>
<select id="selectMinisterById" resultMap="ministerMap">
    select mid,mname,countryId from minister where mid = #{
     id}
</select>

5.一对一(多表连接查询)
5.1实体类

public class Husband {
     
    private Integer hid;
    private String hname;
    //关联属性
    private Wife wife;
public class Wife {
     
    private Integer wid;
    private String wname;

5.2dao接口

public interface IHusbandDao {
     
    Husband selectHusbandById(int id);
}

5.3映射文件xml

    <!--1:1多表连接查询-->
<resultMap id="husbandMap" type="Husband">
    <id column="hid" property="hid"/>
    <result column="hname" property="hname"/>
    <association property="wife" javaType="Wife">
        <id column="wid" property="wid"/>
        <result column="wname" property="wname"/>
    </association>
</resultMap>
<select id="selectHusbandById" resultMap="husbandMap">
    select hid,hname,wid,wname from husband,wife
    where hid = #{
     id} and wid = wifeId
</select>

6.一对一(多表单独查询)

    <!--1:1多表单独查询-->
<select id="selectWifeById" resultType="Wife">
    select wid,wname from wife where wid = #{
     wifeId}
</select>
<resultMap id="husbandMap" type="Husband">
    <id column="hid" property="hid"/>
    <result column="hname" property="hname"/>
    <association property="wife"
                 javaType="Wife"
                 select="selectWifeById"
                 column="wifeId">
    </association>
</resultMap>
<select id="selectHusbandById" resultMap="husbandMap">
    select hid,hname,wifeId from husband
    where hid = #{
     id}
</select>

6.1一对一共享主键连接查询

  <!--1:1共享主键多表连接查询-->
    <resultMap id="husbandMap" type="Husband">
        <id column="hid" property="hid"/>
        <result column="hname" property="hname"/>
        <association property="wife" javaType="Wife">
            <id column="wid" property="wid"/>
            <result column="wname" property="wname"/>
        </association>
    </resultMap>
    <select id="selectHusbandById" resultMap="husbandMap">
        select hid,hname,wid,wname from husband,wife
        where hid = #{
     id} and wid = hid
    </select>

6.2一对一共享主键单独查询

  <!--1:1共享主键多表单独查询-->
    <select id="selectWifeById" resultType="Wife">
        select wid,wname from wife where wid = #{
     wifeId}
    </select>
    <resultMap id="husbandMap" type="Husband">
        <id column="hid" property="hid"/>
        <result column="hname" property="hname"/>
        <association property="wife"
                     javaType="Wife"
                     select="selectWifeById"
                     column="hid">
        </association>
    </resultMap>
    <select id="selectHusbandById" resultMap="husbandMap">
        select hid,hname from husband
        where hid = #{
     id}
    </select>

7.自关联一对多(多表连接)
7.1.1实体类

//作为一方
public class Newslabel {
     
    private Integer id;
    private String name;
    //关联属性
    private Set<Newslabel> children;

7.1.2dao接口

public interface INewslabelDao {
     
    Newslabel selectNewslabelById(int id);
}

7.1.3映射文件xml

<!--自关联1:n的多表连接查询,查询栏目及其子栏目的信息(不能查到其孙栏目信息)-->
<resultMap id="newslabelMap" type="Newslabel">
    <id column="ptid" property="id"/>
    <result column="ptname" property="name"/>
    <collection property="children" ofType="Newslabel">
        <id column="cdid" property="id"/>
        <result column="cdname" property="name"/>
    </collection>
</resultMap>
<select id="selectNewslabelById" resultMap="newslabelMap">
    select pt.id ptid,pt.name ptname,cd.id cdid,cd.name cdname
    from newslabel pt,newslabel cd
    where pt.id=#{
     id} and cd.pid=pt.id
</select>
7.1自关联一对多(多表单独)
    <!--自关联1:n的多表单独查询,查询栏目及其子孙栏目的信息-->
<select id="selectNewslabelByParent" resultMap="newslabelMap">
    select id,name from newslabel where pid = #{
     id}
</select>
<resultMap id="newslabelMap" type="Newslabel">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="children"
                ofType="Newslabel"
                select="selectNewslabelByParent"
                column="id"/>
</resultMap>
<select id="selectNewslabelById" resultMap="newslabelMap">
    select id,name from newslabel
    where id = #{
     id}
</select>

7.3自关联一对多(多表单独查询栏目的子孙栏目)

<!--自关联1:n的多表单独查询,查询栏目的子孙栏目的信息-->
<select id="selectNewslabelByParent" resultMap="newslabelMap">
    select id,name from newslabel where pid = #{
     id}
</select>
<resultMap id="newslabelMap" type="Newslabel">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="children"
                ofType="Newslabel"
                select="selectNewslabelByParent"
                column="id"/>
</resultMap>

7.4自关联一对多(多表单独查询栏目及其父辈栏目)

    <!--自关联1:n的多表单独查询,查询栏目及其父辈栏目的信息-->
    <resultMap id="newslabelMap" type="Newslabel">
        <id column="id" property="id"/>
    <result column="name" property="name"/>
    <association property="parent"
                 javaType="Newslabel"
                 select="selectNewslabelById"
                 column="pid"/>
</resultMap>
    <select id="selectNewslabelById" resultMap="newslabelMap">
        select id,name,pid from newslabel
        where id = #{
     id}
    </select>

8.多对多多表连接查询
8.1实体类

//以student为主查询
public class Student {
     
    private Integer sid;
    private String sname;
    //关联属性
    private Set<Course> courses;
public class Course {
     
    private Integer cid;
    private String cname;

8.2dao接口

public interface IStudentDao {
     
    Student selectStudentById(int id);
}

8.3映射文件xml


```java
```java
  <!--n:m多表连接查询-->
    <resultMap id="studentMap" type="Student">
        <id column="sid" property="sid"/>
        <result column="sname" property="sname"/>
        <collection property="courses" ofType="Course">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
        </collection>
    </resultMap>
    <select id="selectStudentById" resultMap="studentMap">
        select sid,sname,cid,cname from student,course,middle
        where sid = #{
     id} and studentId = sid and cid = courseId
    </select>

9.多对多的多表单独查询

<!--n:m多单独接查询-->
<select id="selectCourseById" resultType="Course">
    select cid,cname from course where cid = #{
     courseId}
</select>
<resultMap id="middleMap" type="Middle">
    <id column="mid" property="mid"/>
    <association property="course"
                 javaType="Course"
                 select="selectCourseById"
                 column="courseId"/>
</resultMap>
<select id="selectMiddleByStudent" resultMap="middleMap">
    select mid,studentId,courseId from middle where studentId = #{
     sid}
</select>
<resultMap id="studentMap" type="Student">
    <id column="sid" property="sid"/>
    <result column="sname" property="sname"/>
    <collection property="courses"
                ofType="Course"
                select="selectMiddleByStudent"
                column="sid"/>
</resultMap>
<select id="selectStudentById" resultMap="studentMap">
    select sid,sname from student
    where sid = #{
     id}
</select>

10.n:m多表连接+单独查询

  <!--n:m多表连接+单独查询-->
    <select id="selectCourseById" resultType="Course">
        select cid,cname from course where cid = #{
     courseId}
    </select>
    <resultMap id="studentMap" type="Student">
        <id column="sid" property="sid"/>
        <result column="sname" property="sname"/>
        <collection property="courses"
                    ofType="Course"
                    select="selectCourseById"
                    column="courseId"/>
    </resultMap>
    <select id="selectStudentById" resultMap="studentMap">
        select sid,sname,courseId from student,middle
        where sid = #{
     id} and studentId = sid
    </select>

小知识点
1.如果数剧库中的字段名和实体类中的属性名不一致
解决方案1:使用resultMap进行手动映射

<!--字段名和属性名不一致:使用resultMap手动映射-->
<resultMap id="studentMap" type="Student">
    <id column="sid" property="id"/>
    <result column="sname" property="name"/>
</resultMap>
<select id="selectStudentById" resultMap="studentMap">
    select sid,sname from student where sid = #{
     id}
</select>

解决方案2:给字段起别名,让别名和属性名相同
例:select sid id,sname name from student ( 数剧库中字段名为sid,实体类中为id)

   <!--字段名和属性名不一致:给字段起别名,让别名和属性名相同-->
    <select id="selectStudentById" resultType="Student">
        select sid id,sname name from student where sid = #{
     id}
    </select>

六,延迟加载

延迟加载只在多表单独查询时起作用
Mybatis笔记总结_第2张图片
Mybatis笔记总结_第3张图片
6.1延迟加载策略默认是关闭的,默认为直接加载。

6.1.1当想使用延迟加载时,可以在mybaits主配置文件中设置settings标签的lazyLoadingEnabled属性设置为ture进行打开,settings标签放置有顺序,需要要在properties标签后放置。

6.1.2打开延迟加载后,默认是深度延迟加载,当用户查询主表详情时,关联表不会被查找,只有当查找到关联表的详情后关联表才被查询。
6.2主配置文件

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

    <!--加载jdbc属性文件-->
    <properties resource="jdbc.properties"/>

    <settings>
        <!--打开延迟加载的总开关,默认使用的是深度延迟加载策略-->
        <setting name="lazyLoadingEnabled" value="true"/>
//        
  //      
    </settings>

    <!--给实体类定义别名-->
    <typeAliases>
        <package name="com.yyf.beans"/>
    </typeAliases>

6.3测试类

 @Test
    public void test()
    {
     
        Country country = countryDao.selectCountryById(2);
        System.out.println("国家的名称:" + country.getCname());//关联表没有被查找
//        System.out.println("国家对应部长的个数:" + country.getMinisters().size());此时关联表才被查询。
    }

6.2.1 如果想使用侵入式延迟加载,在settings中设置aggressiveLazyLoading属性为true,同时总开关要开着,不然不起作用

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

    <!--加载jdbc属性文件-->
    <properties resource="jdbc.properties"/>

    <settings>
        <!--打开延迟加载的总开关,默认使用的是深度延迟加载策略-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--总开关打开,该配置才起作用,侵入式延迟加载的开关-->
       <setting name="aggressiveLazyLoading" value="true"/>
    </settings>

    <!--给实体类定义别名-->
    <typeAliases>
        <package name="com.yyf.beans"/>
    </typeAliases>

6.3.1测试类中当查询到主表的详情时就会查找关联表

 @Test
    public void test()
    {
     
        Country country = countryDao.selectCountryById(2);
        System.out.println("国家的名称:" + country.getCname());
    }

七,缓存管理

Mybatis笔记总结_第4张图片
Mybatis笔记总结_第5张图片

7.1一级缓存默认开启,无法关闭
一级缓存中存放的结果不是查询结果本身,而是一个map
一级缓存底层实现是一个Map
key:hashCode + statementId + SQL语句
value:查询结果本身

7.1.1两次查询相同的student的只进行了一次对数剧库的查询
7.1.2如果使用不同的方法,即使查找的是相同的id也会对数据库进行两次查找
7.1.3不管增删改是否提交,都会清空一级缓存

 //证明:一级缓存是存在的
    @Test
    public void test01()
    {
     
        //第一次查询
        Student student = studentDao.selectStudentById(2);
        System.out.println("第一次查询:" + student);

        //第二次查询
        Student student2 = studentDao.selectStudentById(2);
        System.out.println("第二次查询:" + student2);
        //两次查询相同的student的只进行了一次对数剧库的查询
    }
    // 证明:一级缓存中存放的结果不是查询结果本身,而是一个map
    // 一级缓存底层实现是一个Map
    // key:hashCode + statementId + SQL语句
    // value:查询结果本身
    @Test
    public void test02()
    {
     
        //第一次查询
        Student student = studentDao.selectStudentById(2);
        System.out.println("第一次查询:" + student);

        //第二次查询
        Student student2 = studentDao.selectStudentById2(2);
        System.out.println("第二次查询:" + student2);
        /如果使用不同的方法,即使查找的是相同的id也会对数据库进行两次查找
    }

    // 证明:增删改操作刷新一级缓存(清空一级缓存)
    @Test
    public void test03()
    {
     
        //第一次查询
        Student student = studentDao.selectStudentById(2);
        System.out.println("第一次查询:" + student);

        // 插入一个对象
        studentDao.insertStudent(new Student());

        //第二次查询
        Student student2 = studentDao.selectStudentById(2);
        System.out.println("第二次查询:" + student2);
        //不管增删改是否提交,都会清空一级缓存
    }

7.2二级缓存
全局开关 是默认打开的,单个mapper.xml是默认关闭的,如果要使用则需要
1)对应查询的实体类实现 Serializable接口

public class Student implements Serializable {
     
    private Integer id;
    private String name;
    private int age;
    private double score;
    public Student() {
     
    }

2)mapper.xml文件中创建标签

<mapper namespace="com.yyf.dao.IStudentDao">

    <!--
        在该命名空间下打开二级缓存,
        size可以设置二级缓存的大小,
        eviction可以设置二级缓存的逐出策略,
        flushInterval可以设置二级缓存的刷新间隔
    -->
    <cache size="512" eviction="LRU" flushInterval="10000"/>

    <!--
        flushCache设置该操作是否刷新二级缓存
    -->
        <insert id="insertStudent" flushCache="false">
        insert into student (name,age,score) values (#{
     name},#{
     age},#{
     score})
    </insert>

   <!--useCache设置该查询方法是否使用缓存-->
    <select id="selectStudentById" resultType="Student" useCache="false">
        select id,name,age,score from student where id = #{
     id}
    </select>
 //证明:二级缓存是存在的
    @Test
    public void test01() throws IOException {
     
        // 第一次查询

        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        studentDao = sqlSession.getMapper(IStudentDao.class);
        Student student = studentDao.selectStudentById2(6);
        System.out.println("第一次查询:" + student);
        //关闭SqlSession,清空一级缓存
        sqlSession.close();

        SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
        studentDao = sqlSession2.getMapper(IStudentDao.class);
        // 第二次查询
        Student student2 = studentDao.selectStudentById2(6);
        System.out.println("第二次查询:" + student2);
        sqlSession2.close();

        SqlSession sqlSession3 = MyBatisUtil.getSqlSession();
        studentDao = sqlSession3.getMapper(IStudentDao.class);
        // 第三次查询
        Student student3 = studentDao.selectStudentById2(6);
        System.out.println("第三次查询:" + student3);
        sqlSession3.close();
    }

    // 证明:增删改操作对二级缓存的影响
    // 增删改操作会刷新(清空)二级缓存
    // 1.刷新二级缓存到底执行了那些???
    // 二级缓存的底层也是Map
    // key:hashCode + statemengId + SQL语句
    // value:查询结果
    // 刷新二级缓存实际上是将二级缓存中的所有Entry对象的value置为null
    // 并没有彻底删除整个Entry对象,即key仍保留
    // 2.到底何时可以到DB执行select查询?
    // 有两种情况可以到DB执行查询
    // 1>Map中根本就不存在要查找的Entry,即key未找到
    // 2>Map中存在要查找的key,但value值为null
    // 3.增删改默认可以影响二级缓存,但也可以让其不影响二级缓存
    // 只需要在增删改的statement中添加属性flushCache="false"即可

7.3使用第三方二级缓存
7.3.1在pow文件中引入第三方缓存所需要的依赖

 <!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
    <dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.8.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.2.1</version>
    </dependency>

7.3.2mybatis主配置文件

<configuration>
    <!--加载jdbc属性文件-->
    <properties resource="jdbc.properties"/>
    
    <settings>
        <!--使用log4j2的日志-->
        <setting name="logImpl" value="LOG4J2"/>
        <!--二级缓存的总开关,默认是打开的-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

7.3.3mapper映射文件

<mapper namespace="com.yyf.dao.IStudentDao">

    <!--使用DHCache第三方缓存,把type类型设置为第三方缓存类型-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <property name="timeToLiveSeconds" value="1000"/>
    </cache>

    <!--
        flushCache设置该操作是否刷新二级缓存
    -->
    <insert id="insertStudent" flushCache="false">
        insert into student (name,age,score) values (#{
     name},#{
     age},#{
     score})
    </insert>
    <!--useCache设置该查询方法是否使用缓存-->
    <select id="selectStudentById" resultType="Student" useCache="false">
        select id,name,age,score from student where id = #{
     id}
    </select>

小结:

 1.二级缓存可以全局性关闭
 当前应用中所有查询均不能使用二级缓存,在mybatis的主配置文件
 2.二级缓存可以局部性关闭
 可以指定在某一个映射文件中是否使用二级缓存,在映射文件中使用
 3.二级缓存可以进行对某个查询方法是否使用二级缓存进行设置
 在对应查询标签