mybatis

Mybatis讲解的大纲

1.本堂课程的简介

2.mybatis 简介

model:pojo实体+自身的DAO+自身的service

DAO:数据访问对象

在这里插入图片描述

2.1 什么是mybatis

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也基本上不会用

JDBC

JDBC(Java Database Connectivity)是使用Java语言操作关系型数据库的一套API,使得Java应用程序能够与数据库进行交互,无论这些数据库位于何处,以及它们是如何实现的。通过使用JDBC,Java开发者可以编写出独立于数据库平台的数据库应用程序

操作:
  1. 注册驱动 Class.forName(“com.mysql.cj.jdbc.Driver”);
  2. 获取连接对象 Connection connection = DriverManager.getConnection(url, username, password);
  3. 执行SQL语句,返回执行结果 ResultSet rs = statement.executeQuery(sql);
  4. 处理执行结果 while (rs.next()){ int id = rs.getInt(“id”); }
  5. 释放资源 statement、connection、rs.close();

原始的JDBC程序,存在以下几点问题:

  1. 数据库链接的四要素(驱动、链接、用户名、密码)全部硬编码在java代码中
  2. 查询结果的解析及封装非常繁琐
  3. 每一次查询数据库都需要获取连接,操作完毕后释放连接, 资源浪费, 性能降低

mybatis中,是如何解决这些问题的:

  1. 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件 application.properties中
  2. 查询结果的解析及封装,由mybatis自动完成映射封装,我们无需关注
  3. 在mybatis中使用了数据库连接池技术,从而避免了频繁的创建连接、销毁连接而带来的资源浪费。

数据库连接池是个容器,负责分配、管理数据库连接(Connection)

  • 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象

允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

  • 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)

释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏

  • 客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象

数据库连接池的好处:

  1. 资源重用
  2. 提升系统响应速度
  3. 避免数据库连接遗漏
DbUtils

DbUtils是Apache组织提供的一个开源JDBC工具类库。它是对传统JDBC操作的简单封装,旨在减少数据库操作中的冗余代码,提高开发效率,同时保持程序的性能不受影响。eg:Druid (德鲁伊) 连接池

  • 数据库操作简化:DbUtils通过封装JDBC操作,使得数据库操作更加简洁明了,减少了冗余代码
  • 性能优化:虽然DbUtils是对JDBC的封装,但它并不会引入额外的性能开销,保持了JDBC原有的性能优势

DbUtils是Java编程中数据库操作的一个实用工具,它通过封装JDBC操作,简化了数据库编程的复杂性,提高了开发效率。同时,它保持了JDBC原有的性能优势,是处理数据库操作的一个良好选择

Hibernate

Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,将POJO实体类与数据库表建立映射关系。Hibernate作为一个全自动的ORM框架,能够自动生成SQL语句并自动执行,无需直接编写大量的SQL语句

  1. 全自动ORM:Hibernate通过映射文件(如.hbm.xml)或注解将Java对象与数据库表关联起来,实现了对象与关系数据库的自动映射
  2. 简化数据访问:Hibernate对JDBC进行了封装,大大简化了数据访问层DAO的编码工作,降低了数据库操作的复杂度
  3. 跨数据库支持:Hibernate支持多种关系型数据库,如MySQL、Oracle、SQL Server等,通过简单的配置即可切换底层数据库
  4. 缓存机制:Hibernate提供了多种缓存机制,包括一级缓存和二级缓存,以提高数据访问的效率
  5. 事务管理:Hibernate提供了完善的事务管理机制,支持JDBC事务和JTA(Java Transaction API)事务

Hibernate适用于在需要频繁进行数据库操作、对数据库性能要求较高的场景中。通过使用Hibernate,开发者可以更加专注于业务逻辑的实现,而无需过多关注数据库操作的细节。同时,Hibernate的灵活性和可扩展性也使得它成为许多Java项目中的首选ORM框架

2.2 mybatis能干什么

数据库的增删改查,而且还提供了缓存、插件等功能供我们使用,eg:分页插件

3.mybatis的第一个helloworld程序

3.1 导包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.qfedu.edu</groupId>
        <artifactId>cd-java-fy-2401-framwork-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>mybatis-demo1</artifactId>
    <packaging>jar</packaging>

    <name>mybatis-demo1</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <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>8.0.26</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.7.16</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
        <scope>test</scope>
        </dependency>

    </dependencies>
</project>
3.2 编写mybatis.xml配置文件

在main包新建一个resources包,再新建一个mybatis.xml文件

mybatis.xml

<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--    这个约束的文件中主要配置的是数据库连接的四要素
            default:表示的是默认使用哪一个环境
            这个一般就和下面environment的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>
                <!--连接的地址 &amp;取地址符-->
                <property name="url" value="jdbc:mysql:///cd-fy-2401-mybatis?useUnicode=true&amp;characterEncoding=utf-8"></property>
                <!--数据库的用户名-->
                <property name="username" value="root"></property>
                <!--连接密码-->
                <property name="password" value="123456"></property>
            </dataSource>
        </environment>

        <!--数据库的连接信息是可以配置多个的 -->
        <environment id="Oracle">
            <transactionManager type=""></transactionManager>
            <dataSource type=""></dataSource>
        </environment>
    </environments>

    <!--将Mapper.xml申明到这个文件中来      重要!!!!-->
    <!--使用什么mapper.xml就要配置在这里,否则找不到-->
    <mappers>
        <mapper resource="UserMapper.xml"></mapper>
    </mappers>

</configuration>

建数据库和表

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.3 编写实体

新建一个pojo包,新建一个User实体类,要与数据库字段一一对应

package com.qfedu.edu.pojo;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer userId;
    private String username;
    private String password;
}
3.4 编写UserMapper.xml

实体对应的映射文件

注意:这个Mapper.xml要在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">

<!--
    namespace:可以随便写 只要唯一就可以了,但是一般都见名之意,一般写成  实体的名字Mapper
-->
<mapper namespace="UserMapper">
<!--    这个文件是实体对应的映射文件
        这个文件中主要写的是 数据库的增删改查的描述文件
-->
<!--    接下来 我们就可以写增删改查了...-->
    <select id="list" resultType="com.qfedu.edu.pojo.User">
        select * from t_user
    </select>

</mapper>
3.5 编写测试
package com.qfedu.edu.helloworld;

public class Test01 {

    public static void main(String[] args) throws IOException {
        //首先第一步:要找到这个mybatis.xml的配置文件
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
        //第二步:找到sqlSessionFactory对象 构建者设计模式
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
        //第三步:通过工厂创建会话对象(操作数据库的对象)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第四步:操作数据库  namespace.id  
        List<Object> objects = sqlSession.selectList("UserMapper.list");
        System.out.println("查询出来的数据:" + objects);

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

}

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

4.1 UserMapper.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">
<!--    这个文件是实体对应的映射文件
        这个文件中主要写的是 数据库的增删改查的描述文件
-->
<!--    接下来 我们就可以写增删改查了...-->
    
    <!--查询数据  resultType:结果集类型-->
    <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、测试文件的编写
package com.qfedu.edu.crud;

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下标签和方法混合调用问题

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

使用这个sqlSession.update(“”)

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

为什么查询不能互换呢?

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

6.基本参数的问题

6.1 传递单一参数
6.1.1 编写mapper.xml文件

新建一个UserParamMapper.xml,在mybatis.xml中导入一下

数据类型的别名 :一般都是数据类型的小写,即可:string,map,int,long

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

<!--    这个是研究参数的传递问题的-->
<!--    传递单一的参数-->
<!--    需求:通过用户名找用户
         parameterType:这个参数写啥呢?
           1、写这个参数类型在Java中的全路径
           2、写当前类型在mybatis中取的别名  一般都是数据类型的小写即可:string,map,int,long

        如果传递的是单一的参数的话 #{随便写都是对的} 但是做开发的时候一般写 value
        注意事项:parameterType:这个只能在一个描述中用一次
-->
<mapper namespace="UserMapper">

    <select id="listByUsername" parameterType="string" resultType="com.qfedu.edu.pojo.User">
        select * from t_user where username =#{value}
    </select>
</mapper>

6.1.2 编写测试

使用了parameterType=“string”,这里需要传参

 @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 传递对象类型的参数 parameterType
6.2.1 编写UserMapper.xml

对象别名:typeAliases标签配置

	<!--    添加数据到数据库
            传递对象是可以写 这个类型的全路径
            还可以写这个类型的别名 配置一下就是小写名字即可
            下面引用这个值的时候,直接写 对象中 成员变量的变量名 username、password
    -->
    <insert id="insert" parameterType="user">  
        insert into t_user(username, password)
        values (#{username},#{password})
    </insert>
6.2.2 编写测试
 	@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 取别名

在mybatis.xml中配置别名

    <typeAliases>
<!--        取别名的第一种方式-->
<!--        <typeAlias type="com.qfedu.edu.pojo.User" alias="user"></typeAlias>-->
        
<!--        取别名的第二种方式
            这种方式取别名别名是:类名的所有单词小写-->
        <package name="com.qfedu.edu.pojo"/>
    </typeAliases>
6.3 传递集合类型的参数(map)

在使用mybatis做开发的时,传入的参数只能有一个,那么就产生问题了,如果我想传递多个参数如何玩呢?
那么这个时候要使用功能的话,要么封装成 map,要么封装成 对象去传递

6.3.1、编写mapper.xml

parameterType=“map”,Map的别名就是map

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

    <delete id="deleteByUsernameAndPassword" parameterType="map">
        delete from t_user where username=#{username} and password=#{password}
    </delete>
6.3.2、编写测试文件
 @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.参数的返回问题 resultType

7.1 返回单一的参数
7.1.1 编写xml描述
<!--    参数的返回问题-->
<!--    返回简单参数-->
<!--    通过id查询用户名
        resultType:这个就表示的是返回数据的问题
          这里可以写 返回数据的全路径 同时也可以写 别名
 -->
    <select id="listUsernameByUserId" parameterType="int" resultType="string">
        select username from t_user where userId=#{value}
    </select>
7.1.2、测试

有返回值的,需要接收

@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描述
<!--    通过id查询用户信息
         这个位置返回的是对象类型的参数
         resultType:这个结果集上面可以写 这个类的全路径
           还可以写这个对象的别名
-->
    <select id="listUserByUserId" parameterType="int" resultType="user">
        select * from t_user where userId=#{value}
    </select>
7.2.2 编写测试
 @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、给类取别名
    <typeAliases>
<!--        取别名的第一种方式-->
<!--        <typeAlias type="com.qfedu.edu.pojo.User" alias="user"></typeAlias>-->
<!--        取别名的第二种方式
            这种方式取别名别名是:类名的所有单词小写
-->
        <package name="com.qfedu.edu.pojo"/>
    </typeAliases>
7.3 返回集合类型的参数

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

7.3.1 编写xml描述
<!--    返回集合类型的参数
        返回集合类型的参数的时候 resultType中写什么呢?写集合中  泛型的数据类型
        比如:我们查询所有的用户信息 那么就应该返回  List<User>
        所以下面就应该写User的类型  因为User这个类的类型又取了别名
        所以直接写 User的别名  user就可以了
-->

    <select id="list" resultType="user">
        select * from t_user
    </select>
7.3.2、编写测试
	//查询所有的用户信息
    @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对象的字段不一样怎么办?

在pojo包中的user实体类的属性与数据库字段不一样

package com.qfedu.edu.pojo;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
//    private Integer userId;
//    private String username;
//    private String password;

    private Integer id;
    private String name;
    private String pwd;
}
解决方案1:取别名(AS)
<select id="list" resultType="user">
        SELECT userId AS 'id',username AS 'name',PASSWORD AS 'pwd' FROM t_user
</select>
解决方案2:定义映射关系(resultMap)
<!--
       下面的resultMap表示的是定义了结果集的映射
         type:表示数据库查询出来映射的Java类是哪一个

		property:java实体类的属性
		column:数据库的字段
    -->
    <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 占位符的问题(面试题)

SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法

由于没有对用户输入的数据进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击

mybatis中的占位符 有两个 #{} (预处理占位符) 一个是 ${}(字符串替换占位符)

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

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

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

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

#{…}

  • 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值
  • 使用时机:参数传递,都使用#{…}

${…}

  • 拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题
  • 使用时机:如果对表名、列表进行动态设置时使用

注意事项:在项目开发中,建议使用#{…},生成预编译SQL,防止SQL注入安全。

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

MyBatisUtilsMax工具类

/**
 * 这个就是咋们的mybatis的工具类
 */
public class MyBatisUtils {
    //全局申明一个流的对象
    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);
        }
    }

    //第一部分
    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();
        }
    }
}

测试,静态导入

package com.qfedu.edu.Param;

//静态导入,下面直接使用方法就可以了
import static com.qfedu.edu.utils.MyBatisUtils.*;

public class Test01 {

    @Test
    public void testList() throws IOException {

        //方法调用
        SqlSession sqlSession = getSqlSession();
        //第四步:操作数据库
        List<Object> objects = sqlSession.selectList("UserMapper.list");
        System.out.println("查询到的所有角色是:"+objects);

        //最后我们就来关闭资源
        //方法调用,省略类名
        close();
    }
}

9、mybatis硬编码形式的源码分析

10.1、mybatis.xml中properties节点的解析分析
10.2、mybatis.xml中Settings节点的解析分析
10.3、mybatis.xml中typeAliases节点的解析分析
10.4、mybatis.xml中plugins节点的解析分析
10.5、mybatis.xml中environments节点的解析分析
10.6、mybatis.xml中typeHandlers节点的解析分析
10.7、mybatis.xml中mappers节点的解析分析(上)
10.8、mybatis.xml中mappers节点的解析分析(中)
10.9、mybatis.xml中mappers节点的解析分析(下)
10.11、mybatis.xml文件解析的总结
10.12、openSession初始化之事务的创建
10.13、openSession初始化之执行器的分析
10.14、openSession初始化之执行器涉及到的设计模式分析
10.15、openSession初始化之整个过程初始化总结
10.16、sqlsession调用API之MappedStatement的获取
10.17、sqlsession调用API之execute执行SQL语句的设计模式分析
10.18、sqlsession调用API之Statement对象的获取
10.19、sqlsession调用API之SQL语句的执行

11.mybatis中接口引入的代码编写(重要)

11.1 为什么要引入接口

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

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

11.2、引入接口的代码编写
11.2.1、编写UserMapper这个接口

这个就是访问数据库的接口,这个接口是不需要编写实现类的

package com.qfedu.edu.interface1;

/**
 * 这个就是访问数据库的接口,这个接口是不需要编写实现类的
 */
public interface UserMapper {
    
    //通过用户名查询用户对象(这里的参数和返回值类型都要与mapper.xml对应)
    User listUserByUsername(String username);
}
11.2.2、编写描述文件userMapper.xml

在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:

  1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)

  2. XML映射文件的namespace属性为Mapper接口全限定名一致

  3. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

namespace:命名空间不能随便写了,要写这个Mapper对象的接口的全路径

记得导入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">

<!--引入接口之后的写法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>
11.2.3、编写测试

接口有对象吗?

接口没有对象

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

这句代码中,为什么还能给这个接口赋值对象呢?

说明这里是多态,接口类型的变量指向了接口的实现类对象

接口的实现类又在哪里呢? 没有

是框架通过 代理的设计模式动态生成的

    /**
     * 接口有对象吗?
     *    接口没有对象
     *    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     *    这句代码中 为什么还能给这个接口赋值对象呢?
     *    说明这里是多态 接口类型的变量指向了 接口的实现类对象
     *    接口的实现类又在哪里呢? 没有
     *    是框架通过 代理的设计模式动态生成的
     */
package com.qfedu.edu.interface1;
import static com.qfedu.edu.utils.MyBatisUtils.*;

public class Test01 {
    @Test
    public void test01() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //写的是接口.class
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //调用接口中的方法名
        User user = mapper.listUserByUsername("yyy");
        System.out.println("返回来的这个用户对象是:" + user);
        close();
    }
}

13、mybatis引入接口的源码分析

13.1、mybaits引入接口之后的mybaits.xml解析的问题
13.2、mybaits引入接口之后mapper的获取问题
13.3、mybaits引入mapper之后调用入口的分析
13.4、mybaits引入mapper之后调用API的最终分析

14.mybaits中动态SQL的讲解(上)

记标签

解决组合条件查询的问题

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

14.1、编写SQL描述
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

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

    <!--    组合条件查询用户数据 trim
             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">   <!-- 这个user是UserQueryVo的user属性-->
          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>
14.2、测试
package com.qfedu.edu.interface1;

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

public class Test01 {

    @Test
    public void findUserByCondition() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUserId(3);
        user.setUsername("yyy");
        //再调用这个接口中的方法
        User user1 = mapper.findUserByCondition(user);
        System.out.println("返回来的这个用户对象是:" + user1);
        close();
    }

    @Test
    public void findUserByCondition1() throws IOException {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUserId(3);
        user.setUsername("yyy");
        User user1 = mapper.findUserByCondition(user);
        System.out.println("返回来的这个用户对象是:" + user1);
        close();
    }

    @Test
    public void findUserByCondition3() throws IOException {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        UserQueryVo userQueryVo = new UserQueryVo();
        User user = new User();
        user.setUserId(3);
        user.setUsername("yyy");
        userQueryVo.setUser(user);
        User user1 = mapper.findUserByCondition(user);
        System.out.println("返回来的这个用户对象是:" + user1);
        close();
    }

}
14.3、UserQueryVo的编写
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserQueryVo {
    //这里维护这个User对象
    private User user;
}

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

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

List ids

Integer[] ids

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

15.1 编写SQL描述

新建UserMapperSQL.xml文件

<!--    下面研究传递集合和数组的问题
         传递集合的时候 这个类型就是list,这个集合的名字 也是 list
         item:这个是每一次遍历出来的数据
         open:SQL语句以什么开始
         close:SQL语句以什么结束
         separator:遍历出来的值与之之间使用,分割

		list:List<Integer> 的小写,返回值才是取泛型
		user:List<User>泛型
-->
    <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
		integer[]:Integer[]别名小写
		user:List<User>泛型
		数组的foreach的collection使用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>
15.2 接口
package com.qfedu.edu.sql;

public interface UserMapper {

    //通过一连串的id查询数据(传递集合类型)
    List<User> listByIds1(List<Integer> ids);

    //通过一连串的id查询数据(传递数组类型)
    List<User> listByIds2(Integer[] ids);

}
15.3 编写测试
package com.qfedu.edu.sql;

public class Test01 {

    @Test
    public void test01() throws IOException {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        ArrayList<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(3);

        List<User> list = mapper.listByIds1(ids);
        System.out.println("返回来的这个用户对象是:" + list);
        close();
    }

    @Test
    public void test02() throws IOException {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Integer[] ids={1,2,3};
        List<User> list = mapper.listByIds2(ids);
        System.out.println("返回来的这个用户对象是:" + list);
        close();
    }
}

15.对应关系问题

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

假设:一个人只有一个身份证,一个身份证也只是隶属于一个人的话 ,么人和身份证之间 就是一个一对一的关联关系

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

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

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

15.1 一对一的问题 association
package com.qfedu.edu.oneToOne;

public interface PeopleMapper {

    List<People> list();
}
15.1.1、编写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.oneToOne.PeopleMapper">

    <select id="list" resultMap="listResultMap">
        select * from people p1,idcard i1 where p1.cardId = i1.cardId
    </select>

    <resultMap id="listResultMap" type="people">
        <!--映射主键-->
        <id property="pId" column="pId"/>
        <!--映射其他字段-->
        <result property="pName" column="pName"></result>
        <result property="gender" column="gender"></result>
        <result property="age" column="age"></result>
        <result property="cardId" column="cardId"></result>

        <!--下面还要配置 一对一的关联映射   idCard:别名-->
        <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>
15.1.2、编写测试
package com.qfedu.edu.oneToOne;

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

public class Test01 {

    @Test
    public void test01() throws IOException {

        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class);
        List<People> list = mapper.list();
        System.out.println("返回来的这个用户对象是:" +list);
        close();
    }

}

运行结果:

返回来的这个用户对象是:[People(pId=1, pName=xxx, gender=1, age=18, cardId=1, idCard=IdCard(cardId=1, cardNum=51102, startTime=Tue Sep 03 00:00:00 CST 2024, endTime=Thu Sep 05 00:00:00 CST 2024)), People(pId=2, pName=yyy, gender=2, age=20, cardId=2, idCard=IdCard(cardId=2, cardNum=511028, startTime=Wed Sep 11 00:00:00 CST 2024, endTime=Wed Sep 18 00:00:00 CST 2024)), People(pId=3, pName=zzz, gender=3, age=36, cardId=3, idCard=IdCard(cardId=3, cardNum=50028, startTime=Wed Aug 28 00:00:00 CST 2024, endTime=Sat Sep 21 00:00:00 CST 2024))]

15.1.3、编写People实体
package com.qfedu.edu.pojo;

@Data
public class People {
    private Integer pId;
    private String pName;
    private Integer gender;
    private Integer age;
    private Integer cardId;

    //这个表示的是 一个人只有一个身份证  如果是一对多的话 那么维护的就是集合
    //现在我们的需求是 查询人的数据的时候要将这个身份证信息 一起给查询出来
    private IdCard idCard;
}
15.1.4、编写IdCard实体
package com.qfedu.edu.pojo;

import lombok.Data;

import java.util.Date;

@Data
public class IdCard {
    private Integer cardId;
    private String cardNum;
    private Date startTime;
    private Date endTime;
}
15.2、一对多的关系 collection

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

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

注意:多对多如何创建表呢? 需要建一个中间表

15.2.1、编写SQL描述
package com.qfedu.edu.oneToMany;

public interface DeptMapper {

    //查询所有的部门 同时还需要将员工信息给查询出来
    List<Dept> list();
}

右键接口名,点击copy/paste special,选择第一个,就复制了接口的全路径

<?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 dept t1,
             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>
15.2.2、编写测试
  @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();
    }

运行结果:

返回来的这个用户对象是:[Dept(deptId=1, deptName=Java开发部, deptDes=搞后端的, emps=[Emp(empId=1, empName=xxx, age=18, deptId=1)]), Dept(deptId=2, deptName=web部, deptDes=搞页面的, emps=[Emp(empId=2, empName=yyy, age=20, deptId=2)])]

Dept

package com.qfedu.edu.pojo;

@Data
public class Dept {
    private Integer deptId;
    private String deptName;
    private String deptDes;
    //一个部门有多个员工 如何来表示呢
    //现在我们的需求是 查询部门的时候 需要将员工信息 一起给查询出来
    private List<Emp> emps;
}

Emp实体类

package com.qfedu.edu.pojo;

@Data
public class Emp {
    private Integer empId;
    private String empName;
    private Integer age;
    private Integer deptId;
}

16、mybaits中懒加载的讲解

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

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

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

MyBatis的延迟加载(也称为懒加载)是一种优化数据库操作性能的策略,它允许应用程序在真正需要某个对象或对象的某个属性时,才从数据库中加载这些数据。这种策略的主要目的是减少不必要的数据库访问,提高查询效率,并降低内存消耗

优点

  • 提高查询效率:通过减少不必要的数据库访问,提高查询效率
  • 减少内存消耗:避免一次性加载大量数据,减少内存消耗
  • 提高性能:减少对数据库的压力,提高系统的整体性能

缺点

  • 在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,影响用户体验
  • 需要额外的配置和管理,增加了开发的复杂度
16.1、编写PeopleMapper
public interface PeopleMapper {

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

}
16.2、编写IdCardMapper
public interface IdCardMapper {
 /**
  * 通过身份证id查询身份证信息
  */
 IdCard listByCardId(Integer cardId);
}

16.3、编写PeopleMapperLazy.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 people
    </select>

    <resultMap id="listResultMap" type="people">
        <!--映射主键-->
        <id property="pId" column="pId"/>
        <!--映射其他字段-->
        <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

					select:关联idcard信息,在测试中使用了idcard,才运行idcard的对象,通过people的cardid关联idcard
        -->
        <association property="idCard" javaType="idCard" column="cardId" select="com.qfedu.edu.lazy.IdCardMapper.listByCardId"></association>
    </resultMap>

</mapper>
16.4、编IdCardMapper.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.IdCardMapper">

  <!--    通过身份证id查询身份证信息,这里不需要写参数,参数是people里传过来的-->
  <select id="listByCardId" resultType="idcard" >
    select * from idcard where cardId = #{value}
  </select>

</mapper>
16.5、编写设置
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!--  设置使用 Log4j2 作为日志工具  -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>

        <!--关闭积极的加载  打开懒加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="lazyLoadingEnabled" value="true"/>

    </settings>

    <typeAliases>
        <package name="com.qfedu.edu.pojo"/>
    </typeAliases>

    <!--    这个约束的文件中主要配置的是数据库连接的四要素
            default:表示的是默认使用哪一个环境
            这个一般就和下面的id名字一样就好了
    -->
    <environments default="mysql">
        <!--        id是可以随便写的 一般写环境的名字-->
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <!--            数据源-->
            <dataSource type="POOLED">
                <!--                这里面就要配置数据库连接的四要素了-->
                <!--数据库的驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <!--连接的地址-->
                <property name="url" value="jdbc:mysql:///cd-fy-2401-mybatis?useUnicode=true&amp;characterEncoding=utf-8"></property>
                <!--数据库的用户名-->
                <property name="username" value="root"></property>
                <!--连接密码-->
                <property name="password" value="123456"></property>
            </dataSource>
        </environment>

     
    </environments>

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

        <!--配置两个xml文件-->
        <mapper resource="PeopleMapperLazy.xml"></mapper>
        <mapper resource="IdCardMapper.xml"></mapper>
    </mappers>

</configuration>  
测试
package com.qfedu.edu.lazy;

public class Test01 {

    public static void main(String[] args) throws IOException {
        SqlSession sqlSession = getSqlSession();
        //获取接口对象
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);
        List<People> list = peopleMapper.list();

        //下面演示使用到了身份证信息  这句话不写就是打印上面的people对象
        //写了这句话,才让懒加载启动,运行idcardMapper.xml
        //list.get(0)people的第一个人,带入idcard中查询对象
        IdCard idCard = list.get(0).getIdCard();

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

运行结果:

返回来的这个用户对象是:IdCard(cardId=1, cardNum=51102, startTime=Tue Sep 03 00:00:00 CST 2024, endTime=Thu Sep 05 00:00:00 CST 2024)

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

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

这个缓存的生命周期就是在 Session存活的过程中,Session死了 缓存也就失效了…
注意:一级缓存不能跨session

MyBatis的一级缓存和二级缓存是MyBatis框架提供的两种缓存机制,它们的主要目的是为了提高数据库查询的效率,减少数据库的访问次数,从而减轻数据库的压力

定义:一级缓存也称为本地缓存,它是SqlSession级别的缓存。当MyBatis开启一个SqlSession会话时,会为这个会话创建一个本地缓存区域,用于存储会话期间查询到的数据

工作流程

当执行查询操作时,MyBatis会先在一级缓存中查找是否有相同的查询及其结果

如果找到,则直接返回缓存中的数据,无需再次查询数据库

如果没有找到,则执行数据库查询,并将查询结果存入一级缓存中

失效情况

不同的SqlSession之间的一级缓存是相互独立的

同一个SqlSession中,如果执行了增删改操作(DML),并且这些操作影响了查询结果,那么一级缓存中的数据会被清空

手动调用SqlSession的clearCache()方法也会清空一级缓存

package com.qfedu.edu.cache.first;

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();
    }
}

不清除缓存,SQL语句只运行一次,清除了运行两次

package com.qfedu.edu.cache.first;

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();

    }
}

sql语句只运行一次

18、mybaits中二级缓存(有用)

二级缓存最大的特点就是能跨越Session实现缓存

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

定义:二级缓存是Mapper级别的缓存,它是多个SqlSession共享的。当多个SqlSession使用同一个Mapper进行操作时,它们可以共享同一个二级缓存区域

工作流程
  • 当执行查询操作时,MyBatis会先在一级缓存中查找,如果没有找到,则会去二级缓存中查找
  • 如果二级缓存中也没有找到,则执行数据库查询,并将查询结果存入二级缓存中
  • 注意,二级缓存的数据是在SqlSession关闭或提交后才会写入缓存的

失效情况

  • 如果在两次查询之间执行了增删改操作(DML),并且这些操作影响了查询结果,那么一级和二级缓存都会失效
  • 手动调用SqlSession的clearCache()方法会同时清空一级和二级缓存

总的来说,MyBatis的一级缓存和二级缓存都是为了提高查询效率而设计的。一级缓存是SqlSession级别的,作用范围较小但效率高;二级缓存是Mapper级别的,作用范围更广但需要更多的配置和注意事项

18.1、导包
<!--在这里导入缓存的这个包-->
        <!-- 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>
18.2、编写ehcache.xml文件

不用写,直接复制

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false">

    <!--
     diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
     user.home – 用户主目录
     user.dir  – 用户当前工作目录
     java.io.tmpdir – 默认临时文件路径
   -->
    <!--这个是我们的数据在硬盘上的存储位置-->
    <diskStore path="G:\\mytemp"/>
    <!--
     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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  -->

    <!-- 数据过期策略 -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
    />
</ehcache>
18.3、配置支持二级缓存
<!--这里要打开咋们的二级缓存-->
<setting name="cacheEnabled" value="true"/>
18.4、测试
package com.qfedu.edu.cache.fsecond;

public class Test01 {
    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();


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

    }
}

SQL语句执行了三次

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

19.1、导包
 <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.4</version>
</dependency>
19.2、编写配置文件

放在取别名之后

<!--这里就可以配置插件了-->
<plugins>
	<!--配置是咋们的分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
19.3、测试
package com.qfedu.edu.page;

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

//这个就是测试咋们的分页功能
public class Test01 {
    public static void main(String[] args) 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();
    }
}

数据一共的条目是:1
查询出来的数据是:Page{count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=1, pages=1, reasonable=false, pageSizeZero=false}[User(userId=3, username=yyy, password=147258)]

20、mybaits中四大拦截对象对象的分析

20.1、拦截器的基本应用
20.1.1、编写拦截器
20.1.2、编写配置
20.1.3、测试

21、mybaits中拦截器原理的分析

22、mybatis中PageHelper插件的源码分析上

23、mybaits中PageHelper插件的源码分析下

24、代理设计模式

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

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

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

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

说白了 代理的设计模式 最终的目的 就是对类中的方法进行增强

代理的设计模式是一种结构型设计模式,其核心思想是为其他对象提供一种代理以控制对这个对象的访问。在这种模式下,代理对象在客户端和目标对象之间起到中介的作用,使得客户端并不直接访问目标对象,而是通过代理对象间接地访问

定义:代理模式为其他对象提供一种代理以控制对这个对象的访问

特点
  • 职责清晰:通过代理对象将客户端与目标对象分离,使得系统的职责更加清晰。
  • 高扩展性:代理对象可以在不修改目标对象的前提下,增加额外的功能,如权限控制、日志记录等。
  • 智能化:代理对象可以根据实际情况,决定是否将请求传递给目标对象,或者对请求进行预处理和后处理
分类
  • 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。静态代理在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。适用于代理对象数量不多的情况,但如果需要为多个被代理的类提供代理,则会导致代理类过多,不便于管理和维护。
  • 动态代理:在运行时动态生成代理类,不需要预先定义接口。Java中常见的动态代理实现方式有JDK动态代理和CGLib动态代理。JDK动态代理是基于Java反射机制实现的,只能对实现了接口的类进行代理;而CGLib动态代理则是对类进行代理,无需实现接口。动态代理通常比静态代理更灵活,适用于目标行为不确定的情况。
优点
  • 职责清晰,有助于系统的高内聚低耦合
  • 高扩展性,可以在不修改目标对象的前提下增加额外的功能
  • 智能化,可以根据实际情况灵活处理请求
缺点
  • 由于在客户端和真实主题之间增加了代理对象,因此可能会造成请求的处理速度变慢
  • 实现代理模式需要额外的工作,特别是动态代理的实现可能比较复杂

综上所述,代理设计模式是一种强大的设计模式,通过代理对象控制对目标对象的访问,可以在不修改目标对象的前提下增加额外的功能,提高系统的灵活性和扩展性。然而,也需要注意其可能带来的性能开销和实现复杂性

在MyBatis中,代理模式(Proxy Pattern)被广泛使用,尤其是动态代理。MyBatis通过动态代理为Mapper接口创建代理对象,从而实现对数据库的操作。以下是代理模式在MyBatis中的具体使用方式:

1. Mapper接口的定义

在MyBatis中,开发者只需要定义Mapper接口(相当于Dao接口),接口中声明了需要执行的数据库操作方法。例如:

2. MyBatis框架创建代理对象

MyBatis框架在运行时,会根据Mapper接口的定义,自动创建该接口的动态代理对象。这个代理对象会在被调用时,执行相应的SQL语句,完成数据库操作

3. 动态代理的实现机制

MyBatis使用了JDK动态代理或CGLib动态代理(具体使用哪种方式取决于MyBatis的配置和接口的实现情况)来实现这一过程。在JDK动态代理中,MyBatis会创建一个实现了InvocationHandler接口的类,这个类在invoke方法中调用SqlSession来执行具体的SQL语句

4. Mapper XML文件

与Mapper接口相对应的,是Mapper XML文件,它包含了SQL语句的映射信息。MyBatis通过解析Mapper XML文件,将接口方法与SQL语句关联起来

5. 代理对象的使用

在MyBatis中,通过SqlSession的getMapper方法,可以获取Mapper接口的代理对象。然后,开发者就可以像使用普通Java对象一样,调用代理对象的方法来执行数据库操作了

6. 优点与意义

代理模式在MyBatis中的使用,带来了以下优点和意义:

  • 解耦:客户端(即业务层代码)与数据库操作(即DAO层代码)之间通过Mapper接口进行了解耦,提高了代码的灵活性和可维护性。
  • 简化开发:开发者只需要编写Mapper接口和Mapper XML文件,MyBatis会自动处理接口的动态代理实现,降低了开发难度。
  • 增强功能:MyBatis可以在不修改Mapper接口的情况下,通过修改Mapper XML文件或添加插件等方式,为数据库操作增加额外的功能(如日志记录、权限控制等)。
24.1、静态代理

现在我们有一个需求:

就是Service类中所有方法在执行之前都需要 输出一句话 打开事务

在所有方法执行完成之后 我们都需要输出一句话 关闭和提交事务

24.1.1、编写接口
public interface IUserService {

    //更新的方法
    void update();

     //添加数据的方法
    void add();
}
24.1.2、编写接口实现类
package com.qfedu.edu.proxy.static1;

/**
 * 这个类就成为被代理的类
 * 现在我们有一个要求:
 *    就是Service类中所有方法在执行之前都需要 输出一句话 打开事务
 *    在所有方法执行完成之后  我们都需要输出一句话  关闭和提交事务

 */
public class UserService implements IUserService {

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

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

}
24.1.3、编写代理类

静态代理的第一步:编写一个代理类和被代理的类实现相同的接口

静态代理的第二步:在代理类中维护被代理类的对象

静态代理的第三步:在构造器中去实例化这个成员变量

静态代理的第四步:在代理中的方法中 调用被代理类 相同名字的方法

package com.qfedu.edu.proxy.static1;

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

/**
 * 静态代理的第一步:编写一个代理类和被代理的类实现相同的接口
 */
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();
    }
}

工具类

package com.qfedu.edu.proxy.utils;

public class TransactionUtils {

    public static void beginTransaction() {
        System.out.println("打开事务");
    }

    public static void closeCommitTransaction() {
        System.out.println("关闭和提交事务");
    }

}
24.1.4、编写测试
public class Test001 {
    @Test
    public void testProxy() {
     UserServiceProxy userServiceProxy = new UserServiceProxy(new UserService());
     userServiceProxy.add();
    }
}

运行结果:

打开事务
添加完成…
关闭和提交事务

24.2、动态代理

动态代理又名 JDK代理 简单的说 就是整个代理的过程JDK帮你实现了 你直接用就可以了…

24.1.1、编写接口
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();
}

24.1.2、编写被代理类
public class UserService implements IUserService {

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

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

}
24.1.3、测试代理类
package com.qfedu.edu.proxy.dynamic;

public class Test001 {

    /**
     * 测试动态代理的地方
     */
    @Test
    public void testDynamicProxy() {
        //首先生成代理类对象
        /**
         * 第一个参数是类加载器 :固定写法  被代理的类.class.getClassLoader
         * 第二个参数是被代理的类实现的接口
         *       1>、如果被代理的是类
         *           类.class.getInterfaces()
         *       2>、如果被代理的是接口
         *           new Class[]{接口.class}
         * 第三个参数:回调函数 new InvocationHandler()
         * 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();
    }

}
24.1.4、模拟生成的代理类对象
package com.qfedu.edu.proxy.dynamic;

/**
 * 反推出这个代理类 应该长啥样?
 */
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);
        }
    }
}
24.3、CGLIB代理

现在有个问题:

就是不论咋们的静态代理 还是 动态代理 都有一个代理的前提,这个代理的前提是:被代理的类 必须实现接口 或者本身就是接口

假设现在有一个类 没有实现接口 但是我们依然想给他进行功能的拓展 我们怎么办呢?
于是CGLIB代理就应运而生了…

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

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

24.3.1、导包
<!--        这个就是咋们的CGLIb代理需要的这个包-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
24.3.2、编写被代理类
public class UserService{

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

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

}
24.3.3、编写工厂
package com.qfedu.edu.proxy.cglib;

/**
 * 这个类的主要作用是进行CGLIB代理类的生产
 */
public class UserServiceProxyFactory implements MethodInterceptor {
    /**
     * 这个方法的主要作用就是生成咋们的这个代理类对象
     */
    public UserService getUserServiceProxy() {
        Enhancer enhancer = new Enhancer();
        //设置他爹是谁
        enhancer.setSuperclass(UserService.class);
        //设置这个拦截对象
        enhancer.setCallback(this);
        return (UserService) enhancer.create();
    }

    /**
     * 这个方法主要就是为了实现这个方法执行时候的拦截的
     */
    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;
    }
}
24.3.4、编写测试
package com.qfedu.edu.proxy.cglib;

public class Test001 {
    @Test
    public void testCGLIB() {
        UserService userServiceProxy = new UserServiceProxyFactory().getUserServiceProxy();
        userServiceProxy.update();
    }
}

打开事务
更新完成
关闭和提交事务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A 北枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值