Mybaits学习笔记

Mybatis–框架概述

1、什么是框架?

​ 它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。
​ 使用框架的好处:
​ 框架封装了很多的细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。

MyBatis框架概述

	mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。

​ mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。

​ 采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

2、三层架构

​ 表现层:
​ 是用于展示数据的
​ 业务层:
​ 是处理业务需求
​ 持久层:
​ 是和数据库交互的
在这里插入图片描述
在这里插入图片描述

3、持久层技术解决方案

​ JDBC技术:
​ Connection
​ PreparedStatement
​ ResultSet
​ Spring的JdbcTemplate:
​ Spring中对jdbc的简单封装
​ Apache的DBUtils:
​ 它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装

以上这些都不是框架
JDBC是规范
Spring的JdbcTemplate和Apache的DBUtils都只是工具类

jdbc问题分析

1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

2、Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

3、使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。

4、对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

4、mybatis的概述

mybatis是一个持久层框架,用java编写的。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程
它使用了ORM思想实现了结果集的封装。

ORM:
	Object Relational Mappging 对象关系映射
	简单的说:
		就是把数据库表和实体类及实体类的属性对应起来
		让我们可以操作实体类就实现操作数据库表。

		user			User
		id			userId
		user_name		userName
今天我们需要做到
	实体类中的属性和数据库表的字段名称保持一致。
		user			User
		id			id
		user_name		user_name

5、mybatis的入门

mybatis的环境搭建
	第一步:创建maven工程并 在pom.xml文件中添加坐标 
	第二步:创建实体类和dao的接口
	第三步:创建Mybatis的主配置文

SqlMapConifg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis主配置文件-->
<configuration>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssmtest"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--制定配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>
        <mapper resource="com/atheima/dao/IUserDao.xml"/>
    </mappers>
</configuration>

​ 第四步:创建映射配置文

IUserDao.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.atheima.dao.IUserDao">
    <select id="findAll" resultType="com.atheima.domain.User">
        select * from user;
    </select>
</mapper>

在这里插入图片描述

​ 环境搭建的注意事项:

​ 第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致。
​ 在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
​ 所以:IUserDao 和 IUserMapper是一样的
​ 第二个:在idea中创建目录的时候,它和包是不一样的
​ 包在创建时:com.itheima.dao它是三级结构
​ 目录在创建时:com.itheima.dao是一级目录
​ 第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同
​ 第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
​ 第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名

	当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类。
mybatis的入门案例
	第一步:读取配置文件
	第二步:创建SqlSessionFactory工厂
	第三步:创建SqlSession
	第四步:创建Dao接口的代理对象
	第五步:执行dao中的方法
	第六步:释放资源
    public static void main(String[] args)throws Exception {
        //1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建Dao接口的代理对象
        IUserDao userDao = session.getMapper(IUserDao.class);
        //5.使用代理对象执行方法
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        in.close();
    }
	注意事项:
		不要忘记在映射配置中告知mybatis要封装到哪个实体类中
		配置的方式:指定实体类的全限定类
	mybatis基于注解的入门案例:
		把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句
		同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。
明确:
	我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。
	不管使用XML还是注解配置。
	但是Mybatis它是支持写dao实现类的。

在这里插入图片描述

6、自定义Mybatis的分析

mybatis在使用代理dao的方式实现增删改查时做什么事呢?
	只有两件事:
		第一:创建代理对象
		第二:在代理对象中调用selectLis			

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第一天要求

1、回顾mybatis自定义和环境搭建+完善自定义Mybatis的注解开发
2、Mybatis基于代理Dao的CRUD操作 重点内容
3、CRUD中可能遇到的问题:参数的传递以及返回值的封装
4、介绍Mybatis基于传统dao方式的使用(自己编写dao的实现类) 了解的内容
5、mybatis主配置文件中的常用配置
properties标签
typeAliases标签 —解释Integer的写法
mappers标签的子标签:package


OGNL表达式:
Object Graphic Navigation Language
对象 图 导航 语言

它是通过对象的取值方法来获取数据。在写法上把get给省略了。
比如:我们获取用户的名称
	类中的写法:user.getUsername();
	OGNL表达式写法:user.username
mybatis中为什么能直接写username,而不用user.呢:
	因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名

Mybati–基于代理Dao的实现

自定义Mybatis开发流程图
在这里插入图片描述
几种标签属性的解释

resultType属性:用于指定结果集的类型。

parameterType属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。

sql语句中使用**#{}**字符:它代表占位符,相当于原来jdbc部分所学的?,都是用于执行语句时替换实际的数据。具体的数据是由#{}里面的内容决定的。

#{}中内容的写法:由于我们保存方法的参数是一个User对象,此处要写User对象中的属性名称。它用的是ognl表达式。

ognl表达式:它是apache提供的一种表达式语言,全称是:Object Graphic Navigation Language 对象图导航语言它是按照一定的语法格式来获取数据的。语法格式就是使用#{对象.对象}的方式
#{user.username}它会先去找user对象,然后在user对象中找到username属性,并调用getUsername()方法把值取出来。但是我们在parameterType属性上指定了实体类名称,所以可以省略user.而直接写username。

问题拓展:新增用户id的返回值
在这里插入图片描述
模糊查询易忘点
在这里插入图片描述
模糊查询的另一种配置方式
在这里插入图片描述
#{}与${}的区别

#{}表示一个占位符号

通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。#{}可以接收简单类型值或pojo属性值。如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。

${}表示拼接sql串

通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换,${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。

模糊查询的${value}源码分析
在这里插入图片描述
Mybatis与JDBC编程的比较
1.数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。解决:

在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。

2.Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。解决:

将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

3.向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数对应。解决:

Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。

4.对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。解决:

Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

Mybatis–参数深入

parameterType配置参数
该属性的取值可以是基本类型,引用类型(例如:String类型),还可以是实体类类型(POJO类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。
基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:java.lang.String。实体类类型,目前我们只能使用全限定类名。究其原因,是mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。

Mybatis–输出结果封装

4.1 resultType配置结果类型
resultType属性可以指定结果集的类型,它支持基本类型和实体类类
需要注意的是,它和parameterType一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类
同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。

4.2 实体类属性和数据库表的列名已经不一致时
在这里插入图片描述
4.2.1 修改映射配置
在这里插入图片描述
4.2.2 定义结果类型resultMap
在这里插入图片描述
此时映射配置就要用resultMap而不是resulttype
在这里插入图片描述

Mybatis–传统DAO层开发

总体流程
在这里插入图片描述

分析编写dao的实现类Mybatis的执行过程
在这里插入图片描述

分析代理dao的执行过程
在这里插入图片描述

5.1.1 持久层Dao实现类

public class UserDaoImpl implements IUserDao {
    private SqlSessionFactory factory;
    public UserDaoImpl(SqlSessionFactory factory){
        this.factory = factory;
    }

    @Override
    public List<User> findAll() {
        SqlSession session= factory.openSession();
        //参数就是能获取配置信息的Key就是配置文件中的namespace加上方法的名称
        List<User>  users = session.selectList("com.itheima.dao.IUserDao.findAll");
        session.close();
        return users;
    }

    @Override
    public void saveUser(User user) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用方法实现保存
        session.insert("com.itheima.dao.IUserDao.saveUser",user);
        //3.提交事务
        session.commit();
        //4.释放资源
        session.close();
    }

    @Override
    public void updateUser(User user) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用方法实现更新
        session.update("com.itheima.dao.IUserDao.updateUser",user);
        //3.提交事务
        session.commit();
        //4.释放资源
        session.close();
    }

    @Override
    public void deleteUser(Integer userId) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用方法实现更新
        session.update("com.itheima.dao.IUserDao.deleteUser",userId);
        //3.提交事务
        session.commit();
        //4.释放资源
        session.close();
    }

    @Override
    public User findById(Integer userId) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SqlSession中的方法,实现查询一个
        User user = session.selectOne("com.itheima.dao.IUserDao.findById",userId);
        //3.释放资源
        session.close();
        return user;
    }

    @Override
    public List<User> findByName(String username) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SqlSession中的方法,实现查询列表
        List<User> users = session.selectList("com.itheima.dao.IUserDao.findByName",username);
        //3.释放资源
        session.close();
        return users;
    }

    @Override
    public int findTotal() {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SqlSession中的方法,实现查询一个
        Integer count = session.selectOne("com.itheima.dao.IUserDao.findTotal");
        //3.释放资源
        session.close();
        return count;
    }
}

5.1.2 持久层映射配置


```xml
```java
<?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.itheima.dao.IUserDao">

        <!--第二种方式-->
<!--    <resultMap type="com.itheima.domain.User" id="userMap">-->
<!--        <id column="id" property="userId"/>-->
<!--        <result column="username" property="userName"/>-->
<!--        <result column="sex" property="userSex"/>-->
<!--        <result column="address" property="userAddress"/>-->
<!--        <result column="birthday" property="userBirthday"/>-->
<!--    </resultMap>-->

    <!--查询所有的操作-->
    <select id="findAll" resultType="com.itheima.domain.User">
    <!--当实体类类型和数据库字段类型不一致时  1.用别名方法解决-->
    <!--select id as userId,username as userName,address as userAddress,sex as userSex,birthday as userBirthday from user;-->
    <!--第二种方法自定义字段对应 resultMap参数就对应上面的resultMap的id属性"userMap"-->
        select * from user;
    </select>
    <!--保存用户-->
    <insert id="saveUser" parameterType="com.itheima.domain.User">
        <!--配置插入操作后,获取插入数据的id-->
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username,address,sex,birthday)values (#{username},#{address},#{sex},#{birthday});
    </insert>
    <!--更新用户-->
    <update id="updateUser" parameterType="com.itheima.domain.User">
        update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id};
    </update>
    <!--删除用户-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id};
    </delete>
    <!--查找单个用户-->
    <select id="findById" parameterType="java.lang.Integer" resultType="com.itheima.domain.User">
        select * from user where id=#{id};
    </select>
    <!--根据名字模糊查询用户-->
    <select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
        <!--第二种写法select * from user where username like '%${value}%' 此时传入的参数不需要写%%-->
        select * from user where username like #{name};
    </select>
    <select id="findTotal" resultType="int">
        select count(id) from user;
    </select>

</mapper>

Statement和PrepareStatement区别
在这里插入图片描述

5.1.3 测试

public class MybaitsTest {

    private InputStream in;
    private IUserDao userDao;

    @Before //用于在测试方法执行之前执行
    public void init() throws Exception{
        //读取配置资源,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //使用工厂对象创建dao对象
        userDao = new UserDaoImpl(factory);

    }

    @After //用于在测试方法执行之后执行
    public void destory() throws Exception{
        //释放资源
        in.close();

    }

    /**
     * 测试查询所有
     */
    @Test
    public void testFindAll(){
        //5.执行查询所有方法
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }

    }

Mybatis–SqlMapConfig.xml配置文件

6.1.1SqlMapConfig.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>
    <!--配置properties-->
    <!--可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
    resource属性:
        用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
    url属性:
        是要求按照Url的写法来写地址
        URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
        它的写法:
        http://localhost:8080/mybatisserver/demo1Servlet
        协议      主机     端口       URI

        URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的
        -->
    <properties url="file:///F:/SSM_Learn/Mybatis_CRUD/src/main/resources/jdbcConfig.properties">
        <!--
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/ssmtest?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        -->
    </properties>
    <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <!--
        typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就不再区分大小写
                <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
        -->
        <!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
        <package name="com.itheima.domain"/>
    </typeAliases>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置ysql的环境-->
        <environment id="mysql">
            <!--配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <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="com/itheima/dao/IuserDao.xml"></mapper>-->
        <!--package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了 -->
        <package name="com.itheima.dao"/>
    </mappers>
</configuration>

6.2properties(属性)

在使用properties标签配置时,我们可以采用两种方式指定属性配置。

<?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-->
    <!--可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
    resource属性:
        用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
        -->
    <properties resource="jdbcConfig.properties">
        <!--
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/ssmtest?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        -->
    </properties>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置ysql的环境-->
        <environment id="mysql">
            <!--配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <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="com/itheima/dao/IuserDao.xml"></mapper>
    </mappers>
</configuration>

6.3.1自定义别名:
在这里插入图片描述
6.4mappers(映射器)
在这里插入图片描述

第二天要求

1、mybatis中的连接池以及事务控制 原理部分了解,应用部分会用
mybatis中连接池使用及分析
mybatis事务控制的分析
2、mybatis基于XML配置的动态SQL语句使用 会用即可
mappers配置文件中的几个标签:




3、mybatis中的多表操作 掌握应用
一对多
一对一(?)
多对多
1、连接池:
我们在实际开发中都会使用连接池。
因为它可以减少我们获取连接所消耗的时间。
2、mybatis中的连接池
mybatis连接池提供了3种方式的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
注意:如果不是web或者maven的war工程,是不能使用的。
我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。
3、mybatis中的事务
什么是事务
事务的四大特性ACID
不考虑隔离性会产生的3个问题
解决办法:四种隔离级别

它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚

4、mybatis中的多表查询
特例:
如果拿出每一个订单,他都只能属于一个用户。
所以Mybatis就把多对一看成了一对一。

mybatis中的多表查询:
	示例:用户和账户
		一个用户可以有多个账户
		一个账户只能属于一个用户(多个账户也可以属于同一个用户)
	步骤:
		1、建立两张表:用户表,账户表
			让用户表和账户表之间具备一对多的关系:需要使用外键在账户表中添加
		2、建立两个实体类:用户实体类和账户实体类
			让用户和账户的实体类能体现出来一对多的关系
		3、建立两个配置文件
			用户的配置文件
			账户的配置文件
		4、实现配置:
			当我们查询用户时,可以同时得到用户下所包含的账户信息
			当我们查询账户时,可以同时得到账户的所属用户信息
	步骤:
		1、建立两张表:用户表,角色表
			让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
		2、建立两个实体类:用户实体类和角色实体类
			让用户和角色的实体类能体现出来多对多的关系
			各自包含对方一个集合引用
		3、建立两个配置文件
			用户的配置文件
			角色的配置文件
		4、实现配置:
			当我们查询用户时,可以同时得到用户所包含的角色信息
			当我们查询角色时,可以同时得到角色的所赋予的用户信息

Mybatis-连接池与事务深入

连接池说明
在这里插入图片描述

1.1Mybatis的连接池技术

	我们在前面的WEB课程中也学习过类似的连接池技术,而在Mybatis中也有连接池技术,但是它采用的是自己的连接池技术。在Mybatis的SqlMapConfig.xml配置文件中,通过<dataSource type=”pooled”>来实现Mybatis中连接池的配置。

1.1.1Mybatis连接池的分类在这里插入图片描述
Mybatis_POOLED的过程

在这里插入图片描述
在这里插入图片描述

POOLED和UNPOOLED的对比
在这里插入图片描述

1.1.2Mybatis中数据源的配置

<!--配置环境-->
    <environments default="mysql">
        <!--配置ysql的环境-->
        <environment id="mysql">
            <!--配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <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>
        <package name="com.itheima.dao"/>
    </mappers>

1.1.3 Mybatis中连接的获取过程分析

在这里插入图片描述
在这里插入图片描述
1.2 Mybatis的事务控制

1.2.2Mybatis中事务提交方式

Mybatis中事务的提交方式,本质上就是调用JDBC的setAutoCommit()来实现事务控制。

    @Before //用于在测试方法执行之前执行
    public void init() throws Exception{
        //读取配置资源,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //获取SqlSession对象
        sqlSession = factory.openSession();
        //获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    @After //用于在测试方法执行之后执行
    public void destory() throws Exception{

        //保存事务  如果不保存事务 方法不会报错 但是不会真正执行成功 会导致事务回滚1--Setting autocommit to false--2-- Rolling back JDBC Connection
        sqlSession.commit();

        sqlSession.close();
        in.close()
    }
    /**
     * 查询所有用户
     * @throws Exception
     */
    @Test
    public void testFindAll() throws Exception{

        //执行查询所有方法
        List<User> users = userDao.findAll();
        for(User user: users){
            System.out.println(user);
        }

    }
    
    /**
     * 测试保存用户
     */
    @Test
    public void testSaveUser() throws Exception{
        User user = new User();
        user.setUsername("Coner007");
        user.setAddress("五邑大学00");
        user.setSex("男");
        user.setBirthday(new Date());
        System.out.println("保存操作之前: " + user);
        //执行保存方法
       userDao.saveUser(user);

        System.out.println("保存操作之后: " + user);
        //配置文件中配置了返回保存用户的id 所以保存后能获取到保存用户的id值
    }

观察控制台输出
在这里插入图片描述
这是我们的Connection的整个变化过程,通过分析我们能够发现之前的CUD操作过程中,我们都要手动进行事务的提交,原因是setAutoCommit()方法,在执行时它的值被设置为false了,所以我们在CUD操作中,必须通过sqlSession.commit()方法来执行提交操作。

1.2.3Mybatis自动提交事务的设置

	通过上面的研究和分析,现在我们一起思考,为什么CUD过程中必须使用sqlSession.commit()提交事务?主要原因就是在连接池中取出的连接,都会将调用connection.setAutoCommit(false)方法,这样我们就必须使用sqlSession.commit()方法,相当于使用了JDBC中的connection.commit()方法实现事务提交。
    @Before //用于在测试方法执行之前执行
    public void init() throws Exception{
        //读取配置资源,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //获取SqlSession对象
        sqlSession = factory.openSession(true);
        //获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //用于在测试方法执行之后执行
    public void destory() throws Exception{

        //保存事务  如果不保存事务 方法不会报错 但是不会真正执行成功 会导致事务回滚1--Setting autocommit to false--2-- Rolling back JDBC Connection
      //  sqlSession.commit();

        sqlSession.close();
        in.close();

    }

控制台输出
在这里插入图片描述
我们发现,此时事务就设置为自动提交了,同样可以实现CUD操作时记录的保存。虽然这也是一种方式,但就编程而言,设置为自动提交方式为false再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定提交是否进行提交。

Mybatis–Mybatis的动态SQL语句

动态SQL标签

	我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到
       <!-- 根据queryVo的条件查询用户 -->
    <select id="findUserByVo" parameterType="com.itheima.domain.QueryVo" resultMap="userMap">
        select * from user where username like #{user.userName}
    </select>

    <!-- 根据条件查询
    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user where 1=1
        <if test="userName != null">
          and username = #{userName}
        </if>
        <if test="userSex != null">
            and sex = #{userSex}
        </if>
    </select>-->

    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user
        <where>
            <if test="userName != null">
                and username = #{userName}
            </if>
            <if test="userSex != null">
                and sex = #{userSex}
            </if>
        </where>
    </select>

    <!-- 根据queryvo中的Id集合实现查询用户列表 -->
    <select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
        <include refid="defaultUser"></include>
        <where>
            <if test="ids != null and ids.size()>0">
                <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
                    #{uid}
                </foreach>
            </if>
        </where>
    </select>

Mybatis中简化编写的SQL片段

	Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
    <!-- 了解的内容:抽取重复的sql语句-->
    <sql id="defaultUser">
        select * from user
    </sql>
    <!-- 查询所有 -->
    <select id="findAll" resultMap="userMap">
        <include refid="defaultUser"></include>
    </select>

Mybatis–多表查询之一对多

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.itheima.dao.IAccountDao">
<!--使用resultMap,定义专门的resultMap用于映射一对一查询结果。通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的-->

<!--第二种方式-->
    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>
<!--使用resultMap,定义专门的resultMap用于映射一对一查询结果。通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的-->
    <!-- 查询所有 -->
    <select id="findAll" resultMap="accountUserMap">
        select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
    </select>
    

	<!--第一种方式 定义AccountUser类-->
    <!--查询所有账户同时包含用户名和地址信息-->
    <select id="findAllAccount" resultType="accountuser">
        select a.*,u.username,u.address from account a , user u where u.id = a.uid;
    </select>
	<!--注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型returnType的值设置为AccountUser类型,这样就可以接收账户信息和用户信息了。-->
</mapper>

一对多查询

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">

    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!-- 配置user对象中accounts集合的映射 -->
        <collection property="accounts" ofType="account">
            <id column="id" property="id"></id>
            <result column="uid" property="uid"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userAccountMap">
        select * from user u left outer join account a on u.id = a.uid
    </select>
    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="INT" resultType="user">
        select * from user where id = #{uid}
    </select>
</mapper>

collection 部分定义了用户关联的账户信息。表示关联查询结果集
property=“accList”:
关联查询的结果集存储在User对象的上哪个属性。
ofType=“account”:
指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

Mybatis–多表查询之多对多

结构

在这里插入图片描述

public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //多对多的关系映射,一个用户对应多个角色
    private List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

public class Role implements Serializable {
    private Integer roleId;
    private String roleName;
    private String roleDesc;

    //多对多的关系映射:一个角色可以赋予多个用户
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

IRoleDao.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.itheima.dao.IRoleDao">
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="user">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </collection>
    </resultMap>
    <!--查询所有-->
    <select id="findAll" resultMap="roleMap">
        SELECT u.*,r.id as rid,r.role_name,r.role_desc from role r
        left outer join user_role ur on r.id= ur.rid
        left outer join user u on u.id = ur.uid
    </select>
</mapper>

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

    <!-- 定义User的resultMap-->
    <resultMap id="userMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <collection property="roles" ofType="role">
            <id column="roleId" property="id"></id>
            <result property="roleName" column="role_name"></result>
            <result property="roleDesc" column="role_desc"></result>
        </collection>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userMap">
        SELECT u.*,r.id as rid,r.role_name,r.role_desc from role r
        right outer join user_role ur on r.id= ur.rid
        right outer join user u on u.id = ur.uid
    </select>

    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="INT" resultType="user">
        select * from user where id = #{uid}
    </select>

</mapper>
	从User出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为User与Role的多对多关系,可以被拆解成两个一对多关系来实现。

第四天要求

1、Mybatis中的延迟加载
问题:在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?

      在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。
      在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。

什么是延迟加载
	在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
	不管用不用,只要一调用方法,马上发起查询。

在对应的四种表关系中:一对多,多对一,一对一,多对多
	一对多,多对多:通常情况下我们都是采用延迟加载。
	多对一,一对一:通常情况下我们都是采用立即加载。

2、Mybatis中的缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的一级缓存和二级缓存
一级缓存:
它指的是Mybatis中SqlSession对象的缓存。缓存的是对象
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。

	二级缓存:
		它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
		二级缓存的使用步骤:
			第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
			第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
			第三步:让当前的操作支持二级缓存(在select标签中配置)
		二级缓存存的是数据不是对象 当第二次调用时 会创建一个对象 把数据封装到该对象中 并返回

3、Mybatis中的注解开发
环境搭建
单表CRUD操作(代理Dao方式)
多表查询操作
缓存的配置

Mybatis–延迟加载策略

1.1何为延迟加载

延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

坏处:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

使用assocation实现延迟加载

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

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById">

        </association>
    </resultMap>

    <!-- 根据用户id查询账户信息 -->
    <select id="findAll" resultMap="accountUserMap">
        select * from account
    </select>

    <!-- 根据用户id查询账户信息 -->
    <select id="findAccountByUid" resultType="account">
        select * from account where uid = #{id};
    </select>
</mapper>
select:填写我们要调用的select 映射的id 
column :填写我们要传递给select 映射的参数

开启Mybatis的延迟加载策略

<?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="jdbcConfig.properties"></properties>

    <!--配置参数-->
    <settings>
        <!--开启Mybaits支持延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--默认就是false的  可以不用配置-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.itheima.domain"></package>
    </typeAliases>

    <!--配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>

            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    <!-- 配置映射文件的位置 -->
    <mappers>
        <package name="com.itheima.dao"></package>
    </mappers>
</configuration>

使用Collection实现延迟加载

	同样我们也可以在一对多关系配置的<collection>结点中配置延迟加载策略。<collection>结点中也有select属性,column属性。需求:完成加载用户对象时,查询该用户所拥有的账户信息。

在User实体类中加入List属性

package com.itheima.domain;

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

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //一对多关系映射:主表实体应该包含从表实体的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

编写用户持久层映射配置

在这里插入图片描述
编写账户持久层映射配置

    <!-- 根据用户id查询账户信息 -->
    <select id="findAccountByUid" resultType="account">
        select * from account where uid = #{id};
    </select>
    @Test
    public void testFindAll(){
        List<User> users = userDao.findAll();
//        for(User user : users){
////            System.out.println("-----每个用户的信息------");
////            System.out.println(user);
////            System.out.println(user.getAccounts());
////        }
    }

在这里插入图片描述
此时并没有加载Account账户

Mybatis–缓存

在这里插入图片描述
证明一级缓存的存在

一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就存在。
public class UserTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;
    private SqlSessionFactory factory;

    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession对象
        sqlSession = factory.openSession(true);
        //4.获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{
        //提交事务
        // sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }

     /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);
        
        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);  //返回结果为true
    }

在这里插入图片描述

	我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

一级缓存的分析

一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
在这里插入图片描述

    /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);

        sqlSession.close();
        //在此获取Sqlsesison对象
        sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);   //返回值为false  因为缓存被清空  第二是是重新查询的到的
    }

Mybatis二级缓存

在这里插入图片描述

	二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存结构图
在这里插入图片描述

二级缓存的开启与关闭

第一步:在SqlMapConfig.xml文件开启二级缓存

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

第二步:配置相关的Mapper映射文件

在这里插入图片描述
配置statement上面的useCache属性
在这里插入图片描述
二级缓存的测试

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 */
public class SecondLevelCacheTest {

    private InputStream in;
    private SqlSessionFactory factory;

    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);

    }

    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{

        in.close();
    }

    /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = userDao1.findById(41);
        System.out.println(user1);
        //一级缓存消失
        sqlSession1.close();

        SqlSession sqlSession2 = factory.openSession();
        IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = userDao2.findById(41);
        System.out.println(user2);
        sqlSession2.close();

        System.out.println(user1 == user2);
    }

}

经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出sql语句,所以此时的数据就只能是来自于我们所说的二级缓存。

Mybatis–注解开发

Mybatis的常用注解说明
在这里插入图片描述 使用Mybatis注解实现基本CRUD

使用注解方式开发持久层接口

package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @author Coner007
 * @creat 2020-09-03-14:15
 * 在mybatis中针对CRUD一共有四个注解
 * @Select @Insert @Update @Delete
 */
public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",
            value= {
            @Result(id=true,column="id",property="userId"),
                    @Result(column="username",property="userName"),
                    @Result(column="sex",property="userSex"),
                    @Result(column="address",property="userAddress"),
                    @Result(column="birthday",property="userBirthday")
    })
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user(username,address,sex,birthday) values (#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);
    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}")
    void updateUser(User user);
    /**
     * 删除用户
     * @param id
     */
    @Delete("delete from user where id=#{id}")
    void deleteUser(Integer id);
    /**
     * 根据id查找用户
     * @param id
     */
    @Select("select * from user where id=#{id}")
    User findById(Integer id);
    /**
     * 根据名字模糊查找用户
     * @param username
     */
    @Select("select * from user where username like #{username}")
    List<User> findByName(String username);

    /**
     * 查询总数
     * @return
     */
    @Select("select count(*) from user")
    int findTotal();
}

编写SqlMapConfig 配置文件

<?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 resource="jdbcConfig.properties"></properties>
    <!-- 配置别名 -->
    <typeAliases>
        <package name="com.itheima.domain"/>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <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>
    <!-- 指定带有注解的dao接口所在位置 -->
    <mappers>
        <mapper class = "com.itheima.dao.IUserDao"></mapper>
    </mappers>
</configuration>

使用注解实现复杂关系映射开发

	实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,在使用注解开发时我们需要借助@Results注解,@Result注解,@One注解,@Many注解。

复杂关系映射的注解说明

在这里插入图片描述 mybatis基于注解的二级缓存
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值