(汇总)Mybatis超全三万字详解

(汇总)Mybatis超全详解

文章目录

1、Mybatis

2、mybatis 简介

2.1、什么是mybatis

概念MyBatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作,专注于 SQL 本身,让开发者更加灵活地操作数据库。

理解:mybatis是DAO层的一个解决方案 他的主要功能和 JDBC以及dbutils是一样的 主要完成的是数据库的增删改查

SSM:Struts,SpringMVC/Spring/Mybatis

SSH:Struts,SpringMVC/Spring/Hibernate

SSS:Struts,SpringMVC/Spring/SpringData JPA

拓展

有了JDBC,dbutils了,为什么还要学习这个 mybatis呢?

原因:这个时候我们就会提到 一个框架 这个框架叫做 Hibernate

其实在 JDBC和 Hibernate,以及我们的 mybatis中我们的代码最简洁是Hibernate,代码运行速度最高的是JDBC,代码复杂度最高的也是JDBC,代码复杂度最低的是Hibernate

好像没有 mybatis啥事一样?

其实我们的mybatis的运行效率 是介于 JDBC和Hibernate之间的,同时 代码的复杂度 也是介于 Hibernate和 JDBC之间的 它相当于是 JDBC和Hibernate之间的一个中间产物 你可以这样去理解

但是到今天为止 纯原生态的这个mybatis也基本上不会用

2.2、mybatis能干什么
text 复制代码
数据库的增删改查,而且还提供了缓存 插件等功能供我们使用
2.3、特点
  1. 灵活性高: MyBatis 允许开发者直接编写 SQL 语句,而不需要像 Hibernate 这样的 ORM 框架那样依赖自动生成的 SQL。这使得 MyBatis 在处理复杂查询、联合查询或存储过程时非常灵活。
  2. 性能优化: MyBatis 支持一级缓存和二级缓存,这可以减少数据库的访问频率,提高应用的性能。
  3. 简单易用: 相较于其他 ORM 框架,MyBatis 更加轻量级,不需要额外的复杂配置。它直接操作 SQL,容易理解和使用,特别是对熟悉 SQL 的开发者来说。
  4. 动态 SQL 支持 : MyBatis 提供了强大的动态 SQL 支持,通过、``等标签,开发者可以根据条件动态生成 SQL 语句,减少冗余 SQL 代码。

3、mybatis的第一个helloworld程序

3.1、导包
xml 复制代码
  <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>


        <!--第一步:导包-->
        <!--下面就是iBatis的相关的包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>


        <!--mysql的驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.5.10</version>
        </dependency>

        <!--日志门面-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
3.2、编写mybatis.xml配置文件
xml 复制代码
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--    这个约束的文件中主要配置的是数据库连接的四要素
            default:表示的是默认使用哪一个环境
            这个一般就和下面的id名字一样就好了
    -->

    <environments default="mysql">
        <!--        id是可以随便写的 一般写环境的名字-->
        <environment id="mysql">
            <!--            事务管理器 这个位置可以配置两个
                             Managed
                             还可以是JDBC
           -->
            <transactionManager type="JDBC"></transactionManager>
            <!--            数据源-->
            <dataSource type="POOLED">
                <!--                这里面就要配置数据库连接的四要素了-->
                <!--数据库的驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <!--连接的地址-->
                <property name="url" value="jdbc:mysql:///cd_2401?useUnicode=true&amp;characterEncoding=utf-8"></property>
                <!--数据库的用户名-->
                <property name="username" value="root"></property>
                <!--连接密码-->
                <property name="password" value="root"></property>
            </dataSource>
        </environment>
        <environment id="Oracle">
            <transactionManager type=""></transactionManager>
            <dataSource type=""></dataSource>
        </environment>
        <environment id="db2">
            <transactionManager type=""></transactionManager>
            <dataSource type=""></dataSource>
        </environment>
    </environments>

<!--    将Mapper.xml申明到这个文件中来-->

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

</configuration>
3.3、编写实体
3.4、编写UserMapper.xml
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
-->
<mapper namespace="UserMapper">
<!--    这个文件是实体对应的映射文件
        这个文件中主要写的是 数据库的增删改查的描述文件
-->
<!--    接下来 我们就可以写增删改查了...-->
    <select id="list" resultType="com.qfedu.edu.pojo.User">
        select * from t_user
    </select>

</mapper>
3.5、编写测试
java 复制代码
public static void main(String[] args) throws Exception {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库
        List<Object> objects = sqlSession.selectList("UserMapper.list");
        System.out.println("查询出来的数据:" + objects);
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }

4、mybatis下基本的增删改查的编写

4.1、UserMapper.xml的编写
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
-->
<mapper namespace="UserMapper">
<!--    这个文件是实体对应的映射文件
        这个文件中主要写的是 数据库的增删改查的描述文件
-->
<!--    接下来 我们就可以写增删改查了...-->
    <select id="list" resultType="com.qfedu.edu.pojo.User">
        select * from t_user
    </select>

<!--    更新数据-->
    <update id="update">
        update t_user set username="xxxx"
    </update>

<!--    添加-->

    <insert id="insert">
        insert into t_user(username,password) values('小波波111','11110')
    </insert>

<!--    删除-->
    <delete id="delete">
        delete from t_user where userId=2
    </delete>
</mapper>
4.2、测试文件的编写
java 复制代码
package com.qfedu.edu.crud;

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.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/2  9:43
 */
public class Test001 {

    @Test
    public void testQuery1() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库
        List<Object> objects = sqlSession.selectList("UserMapper.list");
        System.out.println("查询出来的数据:" + objects);
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();

    }
    @Test
    public void testUpdate() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库
        sqlSession.update("UserMapper.update");
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }

    @Test
    public void testAdd() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库
        sqlSession.update("UserMapper.insert");
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }
    @Test
    public void testDelete() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库
        sqlSession.delete("UserMapper.delete");
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }
}

5、mybatis下标签和方法混合调用问题

增删改的方法是可以互换的

增删改的标签也是可以互换的

因为不管是什么标签 最终执行的是SQL语句

为什么查询不能互换呢?

因为查询是有结果的 增删改的结果只有 影响的行数 所以他是不能互换的

6、基本参数的问题

6.1、传递单一参数

parameterType:这个参数写啥呢?

1、写这个参数类型在Java中的全路径

2、写当前类型在mybatis中取的别名

    如果传递的是单一的参数的话 #{随便写都是对的} 但是做开发的时候一般写 value
    注意事项:parameterType:这个只能在一个描述中用一次
6.1.1、编写mapper.xml文件
xml 复制代码
<!--    这个是研究参数的传递问题的-->
<!--    传递单一的参数-->
<!--    通过用户名找用户
-->
    <select id="listByUsername" parameterType="string"  resultType="com.qfedu.edu.pojo.User">
        select * from t_user where username=#{value}
    </select>
6.1.2、编写测试
java 复制代码
 @Test
    public void testQuery1() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();

        Object xxxx = sqlSession.selectOne("UserMapper.listByUsername", "xxxx");

        System.out.println("查询到的数据是:"+xxxx);

        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();

    }
6.2、传递对象类型的参数
6.2.1、编写UserMapper.xml
xml 复制代码
    <!--    添加数据到数据库
            传递对象是可以写 这个类型的全路径
            还可以写 这个类型的别名
            下面引用这个值的时候 直接写 对象中 成员变量的变量名
    -->
    <insert id="insert" parameterType="user">
        insert into t_user(username, password)
        values (#{username},#{password})
    </insert>
6.2.2、编写测试
java 复制代码
 @Test
    public void testInsert() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();

        User user = new User();
        user.setUsername("uuuuu");
        user.setPassword("ooooo");
        sqlSession.insert("UserMapper.insert", user);

        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();

    }
6.2.3、取别名
    取别名的第一种方式:类名的全路径
    <typeAlias type="com.qfedu.edu.pojo.User" alias="user"></typeAlias>
   取别名的第二种方式
        这种方式取别名别名是:类名的所有单词小写
xml 复制代码
    <typeAliases>
        <package name="com.qfedu.edu.pojo"/>
    </typeAliases>
6.3、传递集合类型的参数(map)

在使用mybatis做开发的时候 传入的参数 只能有一个 那么就产生问题了,如果我想传递 多个参数 如何玩呢?

那么这个时候要使用功能的话 要么封装成 map 要么封装成 对象去传递

6.3.1、编写mapper.xml
    第一种传递map直接写Java中类型的全路径
       还可以写Map在mybatis中的别名
    那么 #{}中写啥呢?map中对应的key
xml 复制代码
<!--    测试传递map-->
<!--    需求:通过用户名和密码删除用户数据
-->

    <delete id="deleteByUsernameAndPassword" parameterType="map">
        delete from t_user where username=#{username} and password=#{password}
    </delete>
6.3.2、编写测试文件
java 复制代码
 @Test
    public void testDeleteByUsernameAndPassword() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //这个就是咋们的map
        Map<String,Object> maps=new HashMap<String, Object>();
        maps.put("username","小波波111");
        maps.put("password","11110");
        sqlSession.delete("UserMapper.deleteByUsernameAndPassword",maps);
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();

    }

7、参数的返回问题

7.1、返回单一的参数
7.1.1、编写xml描述

resultType:这个就表示的是返回数据的问题

这里可以写 返回数据的全路径 同时也可以写 别名

xml 复制代码
<!--    参数的返回问题-->
<!--    返回简单参数-->
<!--    通过id查询用户名-->
    <select id="listUsernameByUserId" parameterType="int" resultType="string">
        select username from t_user where userId=#{value}
    </select>
7.1.2、测试
java 复制代码
@Test
    public void testListUsernameByUserId() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Object o = sqlSession.selectOne("UserMapper.listUsernameByUserId", 1);
        System.out.println("查询到的用户名是:"+o);

        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }
7.2、返回对象类型的参数
7.2.1、编写xml描述

这个位置返回的是对象类型的参数

resultType:这个结果集上面可以写 这个类的全路径

还可以写这个对象的别名

xml 复制代码
<!--    通过id查询用户信息
-->
    <select id="listUserByUserId" parameterType="int" resultType="user">
        select * from t_user where userId=#{value}
    </select>
7.2.2、编写测试
java 复制代码
 @Test
    public void testListUserByUserId() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();


        Object o = sqlSession.selectOne("UserMapper.listUserByUserId", 1);

        System.out.println("查询到的用户名是:"+o);

        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();

    }
7.2.3、给类取别名
xml 复制代码
    <typeAliases>
<!--        取别名的第一种方式-->
<!--        <typeAlias type="com.qfedu.edu.pojo.User" alias="user"></typeAlias>-->
<!--        取别名的第二种方式
            这种方式取别名别名是:类名的所有单词小写
-->
        <package name="com.qfedu.edu.pojo"/>
    </typeAliases>
7.3、返回集合类型的参数
7.3.1、编写xml描述

返回集合类型的参数的时候 resultType中写什么呢?写集合中 泛型的数据类型

比如:我们查询所有的用户信息 那么就应该返回 List

所以下面就应该写User的类型 因为User这个类的类型又取了别名

所以直接写 User的别名 user就可以了

xml 复制代码
<!--    返回集合类型的参数-->

    <select id="list" resultType="user">
        select * from t_user
    </select>
7.3.2、编写测试
java 复制代码
    /**
     * 查询所有的用户信息
     * @throws IOException
     */
    @Test
    public void testList() throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<Object> objects = sqlSession.selectList("UserMapper.list");
        System.out.println("查询到的用户名是:"+objects);
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }

拓展

拓展1、数据库的字段和Java对象的字段不一样怎么办?
解决方案1、取别名(*)
xml 复制代码
<select id="list" resultType="user">
        SELECT userId AS 'id',username AS 'name',PASSWORD AS 'pwd' FROM t_user
</select>
解决方案2、定义映射关系(*)
xml 复制代码
<!--
       下面的resultMap表示的是定义了结果集的映射
         type:表示数据库查询出来映射的Java类是哪一个
    -->
    <resultMap id="listResultMap" type="user">
        <!--映射主键-->
        <id property="id" column="userId"></id>
        <!--映射其他字段-->
        <result property="name" column="username"></result>
        <result property="pwd" column="password"></result>
    </resultMap>


    <!--    下面通过映射结果集来解决 字段不一样的问题-->
    <select id="list" resultMap="listResultMap">
        SELECT *
        FROM t_user
    </select>
拓展2,占位符的问题(面试题)

mybatis中的占位符 有两个 #{} 一个是 ${}

#{}在执行的时候 实际上是翻译成了 JDBC中的占位符 ?

${}在执行的时候 直接是 SQL语句的拼接

${}因为是SQL的直接的拼接所有有SQL注入的风险

#{}是预编译的SQL能有效的防止SQL注入

8、mybatis下 基本工具类的编写

8.1、王者(使用到threadLocal)的写法
java 复制代码
package com.qfedu.edu.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.Reader;

/**
 * @author xiaobobo
 * @title: MyBatisutils
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个就是咋们的mybatis的工具类
 *    王者的写法....(王者)
 * @date 2024/9/2  14:35
 */
public class MyBatisUtilsMax {
    //全局申明一个流的对象
    private static  Reader resourceAsReader = null;
    //因为返回的这个类没有成员变量所以不存在 现成安全问题  所以自身能被维护成成员变量
    private static SqlSessionFactory sqlSessionFactory =null;
    //现成局部变量保证 一个方法中 无论拿多少次的SqlSesion那么拿到的 都是同一个SqlSession对象
    private static  ThreadLocal<SqlSession> threadLocal=null;

    static {
        try {
            resourceAsReader=Resources.getResourceAsReader("mybatis.xml");
            sqlSessionFactory= new SqlSessionFactoryBuilder().build(resourceAsReader);
            resourceAsReader.close();
            threadLocal=new ThreadLocal<SqlSession>();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 第一部分
     * @return
     * @throws IOException
     */
    public static SqlSession getSqlSession() throws IOException {
        //先从线程局部变量中去拿这个SqlSession对象
        SqlSession sqlSession = threadLocal.get();
        if(null!=sqlSession){
            //说明这个线程在执行过程中使用过这个对象
            return sqlSession;
        }
        //执行到这里 说明这个对象没有被使用过 那么就创建一个SqlSession对象
        sqlSession = sqlSessionFactory.openSession();
        //然后再将这个对象放到threadLocal中去 防止还要使用
        threadLocal.set(sqlSession);
        return sqlSession;
    }

    /**
     * 关闭资源
     */
    public static void close() throws IOException {
        SqlSession sqlSession = threadLocal.get();
        if(null!=sqlSession){
            //最后我们就来关闭资源
            sqlSession.commit();
            sqlSession.close();
        }
    }
}

9、mybatis中接口引入的代码编写(*)

9.1、为什么要引入接口

1、就是为了让异常前移 如果是没有这个方法的话 那么在运行之前就能直接报错

2、面向接口编程 这样程序的拓展性就率比较好了...

9.2、引入接口的代码编写
9.2.1、编写UserMapper这个接口
java 复制代码
package com.qfedu.edu.interface1;

import com.qfedu.edu.pojo.User;

/**
 * @author xiaobobo
 * @title: UserMapper
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个就是访问数据库的接口
 * 这个接口是不需要编写实现类的
 * @date 2024/9/2  15:19
 */
public interface UserMapper {
    /**
     * 通过用户名查询用户对象
     * @param username
     * @return
     */
    User listUserByUsername(String username);
}
9.2.2、编写描述文件
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">

<!--引入接口之后的写法1:命名空间不能随便写了...要写这个Mapper对象的接口的全路径-->
<mapper namespace="com.qfedu.edu.interface1.UserMapper">
    <!--    方法的描述要和接口中保持一致
            id也不能乱写了 只能是这个接口中对应方法的名字
            parameterType:必须和接口中的方法保持一致
            resultType:也必须和方法保持一致
     -->
    <select id="listUserByUsername" parameterType="string" resultType="user">
        select *
        from t_user
        where username = #{value}
    </select>
</mapper>
9.2.3、编写测试
 接口有对象吗?
    接口没有对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   这句代码中 为什么还能给这个接口赋值对象呢?
   说明这里是多态 接口类型的变量指向了 接口的实现类对象
   接口的实现类又在哪里呢? 没有
   是框架通过 代理的设计模式动态生成的
java 复制代码
    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //再调用这个接口中的方法
        User user = userMapper.listUserByUsername("小波波111");
        System.out.println("返回来的这个用户对象是:" + user);
        close();
    }

10、mybaits中动态SQL的讲解(上)

组合条件查询的问题

比如:我可以通过id查询 也可以通过 用户名 还可以通过密码查询 也可以随机组合查询 如果都不传递参数那么就查询所有数据

10.1、编写SQL描述
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.qfedu.edu.dynamicsql.UserMapper">

    <!--    这里是研究组合条件查询的问题-->
    <select id="findUserByCondition" parameterType="user" resultType="user">
        select * from t_user where 1=1
       <include refid="condition"></include>
    </select>


    <!--    组合条件查询的第二种方式
            where标签会把第一个满足条件的的and给去掉
    -->
    <select id="findUserByCondition2" parameterType="user" resultType="user">
        select * from t_user
        <where>
           <include refid="condition"></include>
        </where>
    </select>


<!--    抽取这个SQL片段-->
    <sql id="condition">
        <if test="userId!=null">
            and userId=#{userId}
        </if>
        <if test="username!=null and username!=''">
            and username=#{username}
        </if>
        <if test="password!=null and password!=''">
            and password=#{password}
        </if>
    </sql>



    <!--    组合条件查询用户数据
             prefixOverrides:覆盖第一个and 简单的说满足条件的第一个and去掉
             suffixOverrides="":覆盖最后一个啥
             prefix="":添加一个前缀
             suffix="":添加一个什么后缀
     -->
    <select id="findUserByCondition3" parameterType="com.qfedu.edu.vo.UserQueryVo" resultType="user">
      select * from t_user
      <if test="user!=null">
          where
          <trim prefixOverrides="and" >
              <if test="user.userId!=null">
                  and userId=#{user.userId}
              </if>
              <if test="user.username!=null and user.username!=''">
                  and username=#{user.username}
              </if>
              <if test="user.password!=null and user.password!=''">
                  and password=#{user.password}
              </if>
          </trim>
      </if>
    </select>
</mapper>
10.2、测试
java 复制代码
package com.qfedu.edu.dynamicsql;

import com.qfedu.edu.pojo.User;
import com.qfedu.edu.vo.UserQueryVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

import static com.qfedu.edu.utils.MyBatisUtilsMax.close;
import static com.qfedu.edu.utils.MyBatisUtilsMax.getSqlSession;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/2  15:24
 */
public class Test001 {


    @Test
    public void testFindUserByCondition() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);


//        UserQueryVo userQueryVo = new UserQueryVo();

        User user = new User();
        user.setUserId(123);
        user.setUsername("小波波");

//        userQueryVo.setUser(null);
        //再调用这个接口中的方法
        List<User> userList = userMapper.findUserByCondition(user);
        System.out.println("返回来的这个用户对象是:" + userList);
        close();
    }
}
10.3、UserQueryVo的编写
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserQueryVo {
    //这里维护这个User对象
    private User user;
}

11、mybatis中动态SQL的讲解(下)

需求:我们要查询 一连串用户id对应的用户数据

List ids

Integer[] ids

那么这种情况下 如何构建咋们的SQL语句呢

11.1、编写SQL描述
     传递集合的时候 这个类型就是list
     这个集合的名字 也是 list
     item:这个是每一次遍历出来的数据
     open:SQL语句以什么开始
     close:SQL语句以什么结束
     separator:遍历出来的值与之之间使用,分割
xml 复制代码
<!--    下面研究传递集合和数组的问题-->
    <select id="listByIds1" parameterType="list" resultType="user">
        select * from t_user
         <foreach collection="list" item="userId" open="where userId in(" close=")" separator=",">
             #{userId}
         </foreach>
    </select>

<!--    这个研究的是传递数组类型的参数
        传递数组类型参数的时候 那么这个集合的名字叫做 array
 -->
    <select id="listByIds2" parameterType="integer[]" resultType="user">
        select * from t_user
         <foreach collection="array" item="userId" open="where userId in(" close=")" separator=",">
             #{userId}
         </foreach>
    </select>
11.2、编写测试
java 复制代码
 @Test
    public void testListByIds1() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        List<Integer> ids=new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(3);
        //再调用这个接口中的方法
        List<User> userList = userMapper.listByIds1(ids);
        System.out.println("返回来的这个用户对象是:" + userList);
        close();
    }
    @Test
    public void testListByIds2() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        Integer[] ids={1,2,3,4};
        //再调用这个接口中的方法
        List<User> userList = userMapper.listByIds2(ids);
        System.out.println("返回来的这个用户对象是:" + userList);
        close();
    }

12、对应关系问题

这个对应关系 一定是在 某种业务场景下 才有意义

假设 一个人 只有一个身份证

一个身份证 也只是隶属于一个人的话 那么人和身份证之间 就是一个一对一的关联关系

一对一的关联关系 如何创建表呢?

其中任何一方维护另一方的主键

将两张表的数据整合成 一张表 这样就违反了第三范式的设计原则引入了传递依赖

12.1、一对一的问题
12.1.1、编写XML描述
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.qfedu.edu.one2one.PeopleMapper">

    <select id="list" resultMap="listResultMap">
        select *
        from t_people t1,
             t_idcard t2
        where t1.cardId = t2.cardId
    </select>

    <resultMap id="listResultMap" type="people">
        <!--        映射主键-->
        <id property="pId" column="pId"></id>
        <!--        映射其他字段-->
        <result property="pName" column="pName"></result>
        <result property="gender" column="gender"></result>
        <result property="age" column="age"></result>
        <result property="cardId" column="cardId"></result>
        <!--        下面还要配置 一对一的关联映射-->
        <association property="idCard" javaType="idcard">
            <!--            映射主键-->
            <id property="cardId" column="cardId"></id>
            <!--            映射其他的字段-->
            <result property="cardNum" column="cardNum"></result>
            <result property="startTime" column="startTime"></result>
            <result property="endTime" column="endTime"></result>
        </association>
    </resultMap>

</mapper>
12.1.2、编写测试
java 复制代码
package com.qfedu.edu.one2one;

import com.qfedu.edu.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.qfedu.edu.utils.MyBatisUtilsMax.close;
import static com.qfedu.edu.utils.MyBatisUtilsMax.getSqlSession;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/2  15:24
 */
public class Test001 {


    @Test
    public void testFindUserByCondition() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);
        List<User> list = peopleMapper.list();

        System.out.println("返回来的这个用户对象是:" + peopleMapper);
        close();
    }

}
12.1.3、编写User实体
java 复制代码
package com.qfedu.edu.pojo;

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

/**
 * @author xiaobobo
 * @title: People
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/3  9:32
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {

 private Integer pId;

 private String pName;

 private Integer gender;

 private Integer age;

 private Integer cardId;

 //这个表示的是 一个人只有一个身份证  如果是一对多的话 那么维护的就是集合
 //现在我们的需求是 查询人的数据的时候要将这个身份证信息 一起给查询出来
 private IdCard idCard;

}
12.1.4、编写IdCard实体
java 复制代码
package com.qfedu.edu.pojo;

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

import java.util.Date;

/**
 * @author xiaobobo
 * @title: IdCard
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/3  9:33
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IdCard {
   private Integer cardId;

   private String cardNum;

   private Date startTime;

   private Date endTime;
}
12.2、一对多的关系

如果是一个部分有多个员工

一个员工 只隶属于一个部门的话 那么部门和员工之间就是一个一对多的关联关系

一对多的关联关系 如何创建表呢? 在多的一方维护一的一方的主键

12.2.1、编写SQL描述
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.qfedu.edu.one2many.DeptMapper">

    <!--    查询所有的部门信息 同时将员工信息查询出来-->
    <select id="list" resultMap="listResultMap">
        select *
        from t_dept t1,
             t_emp t2
        where t1.deptId = t2.deptId
    </select>

    <resultMap id="listResultMap" type="dept">
        <!--        映射主键-->
        <id property="deptId" column="deptId"></id>
        <!--        映射其他字段-->
        <result property="deptName" column="deptName"></result>
        <result property="deptDes" column="deptDes"></result>
        <!--        下面配置一对多的关联映射-->
        <collection property="emps" ofType="emp">
            <!--            映射主键-->
            <id property="empId" column="empId"></id>
            <!--            映射其他的字段-->
            <result property="empName" column="empName"></result>
            <result property="age" column="age"></result>
            <result property="deptId" column="deptId"></result>
        </collection>
    </resultMap>


</mapper>
12.2.2、编写测试
java 复制代码
  @Test
    public void testFindUserByCondition() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
        List<Dept> list = deptMapper.list();
        System.out.println("返回来的这个用户对象是:" + list);
        close();
    }

13、mybaits中懒加载的讲解

使用的时候 才加载 这个就成为懒加载

我们在查询这个people的时候是不是 直接将 身份证信息也给查询出来了... 这个就不是懒加载 而是积极的加载 因为这个身份证信息 我都没用 你也直接给我 查询出来了... 所以这个不是懒加载

我们想实现的功能是:我们要使用这个身份证信息的时候你再给我们查询这个身份证的信息

13.1、编写PeopleMapper
java 复制代码
public interface PeopleMapper {

 /**
  * 查询所有人的数据 同时还要将身份证信息封装成对象显示在People对象中
  * @return
  */
   List<People> list();

}
13.2、编写IdCardMapper
java 复制代码
public interface IdCardMapper {
 /**
  * 通过身份证id查询身份证信息
   * @param cardId
  * @return
  */
 IdCard listByCardId(Integer cardId);
}
13.3、编写PeopleMapperLazy.xml
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.qfedu.edu.lazy.PeopleMapper">

    <select id="list" resultMap="listResultMap">
        select *  from t_people
    </select>

    <resultMap id="listResultMap" type="people">
        <!--        映射主键-->
        <id property="pId" column="pId"></id>
        <!--        映射其他字段-->
        <result property="pName" column="pName"></result>
        <result property="gender" column="gender"></result>
        <result property="age" column="age"></result>
        <result property="cardId" column="cardId"></result>
        <!--        下面还要配置 一对一的关联映射
                    column:表示的是我们要调用 Mapper.xml中的某一个方法 要传递的数据是谁
                    这个column的值 必须是 上面配置的映射中的一个字段才行
                    select:这个就表示的是我们要使用的方法是谁 命名空间.id
        -->
        <association property="idCard" javaType="idcard" column="cardId" select="com.qfedu.edu.lazy.IdCardMapper.listByCardId"></association>
    </resultMap>

</mapper>
13.4、编IdCardMapper.xml
xml 复制代码
<mapper namespace="com.qfedu.edu.lazy.IdCardMapper">

    <!--    通过身份证id查询身份证信息-->
    <select id="listByCardId" resultType="idcard">
        select *
        from t_idcard
        where cardId = #{value}
    </select>

</mapper>
13.5、编写设置
xml 复制代码
  <!--        关闭积极的加载  打开懒加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="lazyLoadingEnabled" value="true"/>

14、mybaits中一级缓存的讲解(没用)

一级缓存有个名字 叫做Session缓存

这个缓存的生命周期 就是在 Session存活的过程中 Session死了 缓存也就失效了...

java 复制代码
package com.qfedu.edu.cache.first;

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.Reader;
import java.util.List;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/2  9:43
 */
public class Test001 {

    public static void main(String[] args) throws Exception {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();


        //第四步:操作数据库
        List<Object> objects1 = sqlSession.selectList("UserMapper.list");

        sqlSession.clearCache();

        List<Object> objects2 = sqlSession.selectList("UserMapper.list");

        List<Object> objects3 = sqlSession.selectList("UserMapper.list");

        List<Object> objects4 = sqlSession.selectList("UserMapper.list");

        List<Object> objects5 = sqlSession.selectList("UserMapper.list");

        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();
        resourceAsReader.close();
    }
}
java 复制代码
package com.qfedu.edu.cache.first;

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.Reader;
import java.util.List;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/2  9:43
 */
public class Test002 {

    public static void main(String[] args) throws Exception {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);


        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库
        List<Object> objects1 = sqlSession.selectList("UserMapper.list");
        //最后我们就来关闭资源
        sqlSession.commit();
        sqlSession.close();


        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        //第四步:操作数据库
        List<Object> objects2 = sqlSession1.selectList("UserMapper.list");
        //最后我们就来关闭资源
        sqlSession1.commit();
        sqlSession1.close();

    }
}

14、mybaits中二级缓(有用)

text 复制代码
最大的特点就是能跨越Session实现缓存

我们这里的二级缓存是要借助于咋们的这个第三方来完成的 

这里的第三方就是咋们的ehcache
14.1、导包
xml 复制代码
<!--在这里导入缓存的这个包-->
        <!-- ehcache核心jar包 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.11</version>
        </dependency>

        <!-- MyBatis与ehcache整合jar包 -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
14.2、编写ehcache.xml文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false">

    <!--
     diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
     user.home -- 用户主目录
     user.dir  -- 用户当前工作目录
     java.io.tmpdir -- 默认临时文件路径
   -->
    <!--这个是我们的数据在硬盘上的存储位置-->
    <diskStore path="G:\\mytemp"/>
    <!--
  
  -->

    <!-- 数据过期策略 -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
    />
</ehcache>
14.3、配置支持二级缓存

name:缓存名称。

maxElementsInMemory:缓存最大数目

maxElementsOnDisk:硬盘最大缓存个数。

eternal:对象是否永久有效,一但设置了,timeout将不起作用。

overflowToDisk:是否保存到磁盘,当系统当机时

timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。

timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。

diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.

diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。

diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。

memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。

clearOnFlush:内存数量最大时是否清除。

memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。

FIFO,first in first out,这个是大家最熟的,先进先出。

LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。

LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

xml 复制代码
 <!--        这里要打开咋们的二级缓存-->
        <setting name="cacheEnabled" value="true"/>
14.4、测试

15、mybaits中Pageelper插件的使用(分页)

15.1、导包
xml 复制代码
 <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.4</version>
</dependency>
15.2、编写配置文件
xml 复制代码
<!--    这里就可以配置插件了-->
    <plugins>
<!--        配置是咋们的分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
15.3、测试
java 复制代码
package com.qfedu.edu.page;

import static com.qfedu.edu.utils.MyBatisUtilsMax.*;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInterceptor;
import com.qfedu.edu.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/3  14:27
 */
public class Test001 {
    /**
     * 这个就是测试咋们的分页功能
     */
    @Test
    public void testPage() throws IOException {
        //先设置要查询哪一页  每页查询多少条数
        //第一个参数是页码  第二个参数 是每页的数据量
        PageHelper.startPage(1,2);
        //接下来就可以查询了....
        SqlSession sqlSession = getSqlSession();
        Page<User> page = (Page)(sqlSession.selectList("UserMapper.list"));
        System.out.println("数据一共的条目是:"+page.getTotal());
        System.out.println("查询出来的数据是:"+page.getResult());
        int pageNum = page.getPageNum();
        int pages = page.getPages();
        close();
    }
}

24、代理设计模式

text 复制代码
设计模式:为了实现某一个功能 前人总结出的一个好的方法和步骤

代理的设计模式  是为了解决什么问题呢?

他可以动态的监控一个类中 方法在什么时候 执行   以及可以在方法执行的前后 动态植入我们的代码

注意:静态代理 和 动态代理都有一个代理的前提 就是我们的被代理的类 一定要实现接口  或者自己就是接口

说白了 代理的设计模式 最终的目的 就是对类中的方法进行增强
16.1、静态代理
16.1.1、编写接口
java 复制代码
public interface IUserService {
    /**
     * 更新的方法
     */
    void update();

    /**
     * 添加数据的方法
     */
    void add();
}
16.1.2、编写接口实现类
java 复制代码
package com.qfedu.edu.proxy.static1;

/**
 * @author xiaobobo
 * @title: UserService
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个类就成为被代理的类
 * 现在我们有一个要求:
 *    就是Service类中所有方法在执行之前都需要 输出一句话 打开事务
 *    在所有方法执行完成之后  我们都需要输出一句话  关闭和提交事务
 * @date 2024/9/3  14:45
 */
public class UserService implements IUserService {

    public void update() {
        System.out.println("更新完成");
    }

    public void add() {
        System.out.println("添加完成....");
    }

}
16.1.3、编写代理类
java 复制代码
package com.qfedu.edu.proxy.static1;

import static com.qfedu.edu.proxy.utils.TransactionUtils.*;

/**
 * @author xiaobobo
 * @title: UserServiceProxy
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 静态代理的第一步:编写一个代理类和被代理的类实现相同的接口
 * @date 2024/9/3  14:49
 */
public class UserServiceProxy implements IUserService {

    //静态代理的第二步:在代理类中维护被代理类的对象
    private IUserService userService;

    //静态代理的第三步:在构造器中去实例化这个成员变量
    public UserServiceProxy(IUserService userService) {
        this.userService = userService;
    }

    //静态代理的第四步:在代理中的方法中 调用被代理类 相同名字的方法
    public void update() {
        beginTransaction();
        this.userService.update();
        closeCommitTransaction();
    }

    public void add() {
        beginTransaction();
        this.userService.add();
        closeCommitTransaction();
    }
}
16.1.4、编写测试
java 复制代码
public class Test001 {
    @Test
    public void testProxy() {
     UserServiceProxy userServiceProxy = new UserServiceProxy(new UserService());
     userServiceProxy.add();
    }
}
16.2、动态代理
text 复制代码
动态代理又名 JDK代理 简单的说 就是整个代理的过程JDK帮你实现了 你直接用就可以了...
16.2.1、编写接口
java 复制代码
package com.qfedu.edu.proxy.dynamic;

/**
 * @author xiaobobo
 * @title: IUserService
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个是被代理的类实现的接口
 * @date 2024/9/3  14:44
 */
public interface IUserService {
    /**
     * 更新的方法
     */
    void update();

    /**
     * 添加数据的方法
     */
    void add();
}
16.2.2、编写被代理类
java 复制代码
public class UserService implements IUserService {

    public void update() {
        System.out.println("更新完成");
    }

    public void add() {
        System.out.println("添加完成....");
    }

}
16.2.3、测试代理类
java 复制代码
package com.qfedu.edu.proxy.dynamic;

import com.qfedu.edu.proxy.utils.TransactionUtils;
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/3  15:19
 */
public class Test001 {

    /**
     * 测试动态代理的地方
     */
    @Test
    public void testDynamicProxy() {
        //首先生成代理类对象
        /**
         * 第一个参数是类加载器 :固定写法  被代理的类.class.getClassLoader
         * 第二个参数是被代理的类实现的接口
         *       1>、如果被代理的是类
         *           类.class.getInterfaces()
         *       2>、如果被代理的是接口
         *           new Class[]{接口.class}
         * 第三个参数:回调函数
         * JDK代理实际上生成的是 接口的实现类 兄弟
         * 在JDK代理中第三个参数是最重要的 因为可以监控方法在什么时候执行
         */
        IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
                UserService.class.getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 这个方法就是监控被代理类中 方法在什么时候执行的回调函数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String name = method.getName();
                        System.out.println("当前执行的方法的名字是:" + name);
                        TransactionUtils.beginTransaction();
                        //放行执行到目标类中去(这个类的实例 应该是目标类对象)
                        Object invoke = method.invoke(new UserService(), args);
                        TransactionUtils.closeCommitTransaction();
                        return invoke;
                    }
                });
        userServiceProxy.update();
    }

}
16.2.4、模拟生成的代理类对象
java 复制代码
package com.qfedu.edu.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author xiaobobo
 * @title: UserServiceProxy
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 反推出这个代理类 应该长啥样?
 * @date 2024/9/3  15:30
 */
public class UserServiceProxy implements IUserService {

    //相当于把这个接口传递过来了(这个相当于是爹的这个class对象)
    private Class interfaces;

    private InvocationHandler invocationHandler;

    public UserServiceProxy(Class interfaces, InvocationHandler invocationHandler) {
        this.interfaces = interfaces;
        this.invocationHandler = invocationHandler;
    }

    public void update() {
        //这里怎么做呢?
        //通过父亲(接口) 去找他爹里面相同名字的方法(反射)
        //这个method是谁里面的method? 爹里面的method
        try {
            Method method = interfaces.getMethod("update");
            //接下来怎么做呢?
            this.invocationHandler.invoke(this, method, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

    }

    public void add() {
         //这里怎么做呢?
        //通过父亲(接口) 去找他爹里面相同名字的方法(反射)
        //这个method是谁里面的method? 爹里面的method
        try {
            Method method = interfaces.getMethod("add");
            //接下来怎么做呢?
            this.invocationHandler.invoke(this, method, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}
16.3、CGLIB代理

现在有个问题:

就是不论咋们的静态代理 还是 CGLIB代理 都有一个代理的前提

这个代理的前提是:被代理的类 必须实现接口 或者本身就是接口

假设现在有一个类 没有实现接口 但是我们依然想给他进行功能的拓展 我们怎么办呢?

于是CGLIB代理就应运而生了...

记住CGLIB代理的代理类 肯定不需要我们去实现了 只是需要我们去获取代理类对象就可以了 跟JDK代理是一样的

这里需要记住 这个CGLIB代理类生成的是 子类 生成的是 被代理类的子类

16.3.1、导包
xml 复制代码
<!--        这个就是咋们的CGLIb代理需要的这个包-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
16.3.2、编写被代理类
java 复制代码
public class UserService{

    public void update() {
        System.out.println("更新完成");
    }

    public void add() {
        System.out.println("添加完成....");
    }

}
16.3.3、编写工厂
java 复制代码
package com.qfedu.edu.proxy.cglib;

import com.qfedu.edu.proxy.utils.TransactionUtils;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author xiaobobo
 * @title: UserServiceProxyFactory
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: 这个类的主要作用是进行CGLIB代理类的生产
 * @date 2024/9/4  9:29
 */
public class UserServiceProxyFactory implements MethodInterceptor {
    /**
     * 这个方法的主要作用就是生成咋们的这个代理类对象
     *
     * @return
     */
    public UserService getUserServiceProxy() {
        Enhancer enhancer = new Enhancer();
        //设置他爹是谁
        enhancer.setSuperclass(UserService.class);
        //设置这个拦截对象
        enhancer.setCallback(this);
        return (UserService) enhancer.create();
    }

    /**
     * 这个方法主要就是为了实现这个方法执行时候的拦截的
     *
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里你就可以对方法进行增强了
        TransactionUtils.beginTransaction();
        Object invoke = method.invoke(new UserService(), objects);
        TransactionUtils.closeCommitTransaction();
        return invoke;
    }
}
16.3.4、编写测试
java 复制代码
package com.qfedu.edu.proxy.cglib;

import org.junit.Test;

/**
 * @author xiaobobo
 * @title: Test001
 * @projectName cd-java-fy-2401-framwork-demo
 * @description: TODO
 * @date 2024/9/4  9:37
 */
public class Test001 {
    @Test
    public void testCGLIB() {
        UserService userServiceProxy = new UserServiceProxyFactory().getUserServiceProxy();
        userServiceProxy.update();
    }
}
相关推荐
界面开发小八哥5 分钟前
「Java EE开发指南」如何用MyEclipse构建一个Web项目?(一)
java·前端·ide·java-ee·myeclipse
王伯爵7 分钟前
<packaging>jar</packaging>和<packaging>pom</packaging>的区别
java·pycharm·jar
Q_192849990630 分钟前
基于Spring Boot的个人健康管理系统
java·spring boot·后端
m0_748245171 小时前
Web第一次作业
java
小码的头发丝、1 小时前
Java进阶学习笔记|面向对象
java·笔记·学习
m0_548514771 小时前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript
坊钰2 小时前
【Java 数据结构】移除链表元素
java·开发语言·数据结构·学习·链表
chenziang12 小时前
leetcode hot100 LRU缓存
java·开发语言
会说法语的猪2 小时前
springboot实现图片上传、下载功能
java·spring boot·后端