• MyBatis框架


    MyBatis框架

    1 介绍

    MyBatis 是一个开源的持久层框架,它简化了数据库交互的过程。与许多其他持久层框架不同,MyBatis 不会强制你使用对象关系映射(ORM)的范式。它采用了一种将 Java 对象和数据库记录进行映射的灵活方式,可以通过 XML 或者注解配置 SQL 映射关系,使得开发者可以自由地定制 SQL,而不需要像 Hibernate 那样需要遵循特定的对象模型。

    以下是 MyBatis 的一些主要特点和优势:

    1. 简单易学: MyBatis 不需要学习复杂的配置,简单易上手。

    2. 灵活性: MyBatis 允许使用原生 SQL、存储过程和高级映射。

    3. 动态 SQL: MyBatis 提供了强大的动态 SQL 支持,可以根据不同的条件生成不同的 SQL。

    4. 自动映射: MyBatis 提供了自动将查询结果映射到 Java 对象的功能,无需手动设置映射规则。

    5. 与 Spring 和其他框架集成: MyBatis 易于与 Spring 等框架集成,可以方便地用于各种 Java 项目。

    6. 缓存支持: MyBatis 提供了一级缓存和二级缓存的支持,可以有效地提高查询性能。

    7. 可插拔的: MyBatis 的设计允许开发者编写自定义的插件来扩展框架功能。

    8. 不需要使用 DAO 接口: 在 MyBatis 中,可以直接使用映射文件中配置的 SQL,而不需要创建繁琐的 DAO 接口。

    2 JDBC局限性

    JDBC(Java Database Connectivity)是 Java 中用于与数据库进行交互的标准接口。尽管 JDBC 是一个强大的工具,但它也有一些局限性:

    1. 冗长的代码: 使用 JDBC 进行数据库操作需要编写很多冗长的代码,包括数据库连接的建立和关闭、异常处理、SQL 语句的构建等。这些代码量较大,容易出错,降低了开发效率。

    2. 手动处理异常: 在 JDBC 中,开发者需要手动处理 SQL 异常,这意味着在每次数据库操作时都需要编写大量的异常处理代码,增加了代码的复杂性。

    3. 硬编码的 SQL 语句: 在 JDBC 中,SQL 语句通常是硬编码在 Java 代码中的,这样的做法不利于代码的维护和修改。如果需要修改 SQL 语句,开发者需要修改 Java 代码,这样的耦合性较高。

    4. 性能: JDBC 的性能通常较低,特别是在频繁进行数据库操作的时候。每次数据库操作都需要建立连接、执行 SQL 语句、关闭连接,这些操作会增加数据库的负担。

    5. 缺乏对象关系映射(ORM): JDBC 是一种底层的数据库操作接口,它不提供对象关系映射的功能。这意味着开发者需要手动将数据库查询结果映射到 Java 对象,增加了开发的复杂性。

    6. 事务管理: 在 JDBC 中,事务管理需要手动编写代码来处理,包括事务的开始、提交、回滚等操作。这些操作容易出错,并且增加了代码的复杂性。

    为了解决这些问题,很多 Java 开发者转向了使用持久层框架,如Hibernate、MyBatis 等。这些框架提供了更高级、更方便的数据库操作方式,能够简化开发流程,提高开发效率。使用这些框架,开发者可以更专注于业务逻辑的实现,而不是花费大量的时间在处理数据库操作上。

    3 MyBatis框架底层

    MyBatis是一个基于Java的持久化框架,它的底层原理主要围绕着数据库访问和SQL映射展开。以下是MyBatis框架底层原理的主要组成部分:

    1. 配置文件(mybatis-config.xml):
      MyBatis的配置文件包含了框架的核心配置信息,包括数据源、事务管理、缓存配置等。它定义了框架的全局行为。

    2. 映射文件(Mapper.xml):
      映射文件是MyBatis的核心组成部分,它包含了SQL语句的定义以及这些SQL语句与Java方法之间的映射关系。在映射文件中,可以定义SQL查询、插入、更新、删除等操作,以及它们的输入参数和输出结果的映射。

    3. SqlSessionFactory:
      SqlSessionFactory 是 MyBatis 的核心接口,它负责创建 SqlSession 实例。SqlSessionFactory 的实现类 DefaultSqlSessionFactory 负责解析配置文件,构建并管理 SqlSession 的生命周期。

    4. SqlSession:
      SqlSession 是 MyBatis 框架中用于执行 SQL 操作的主要接口。它提供了各种查询、插入、更新、删除等方法,以及提交事务、关闭资源等功能。

    5. Executor:
      Executor 负责 SQL 的执行,它会将映射文件中定义的 SQL 语句解析成数据库可以执行的语句,执行SQL 并返回结果。

    6. StatementHandler、ParameterHandler、ResultSetHandler、TypeHandler:
      这些组件负责具体的 SQL 语句处理,比如 StatementHandler 负责 PreparedStatement 的创建和参数设置,ParameterHandler 负责 SQL 参数的处理,ResultSetHandler 负责结果集的处理,TypeHandler 负责 Java 类型与数据库类型之间的转换。

    在 MyBatis 的工作流程中,SqlSessionFactory 负责初始化并加载配置文件,创建 SqlSession 实例。SqlSession 通过映射文件执行 SQL 语句,调用 Executor 执行具体的数据库操作。执行结果由ResultSetHandler 处理,最终返回给调用者。

    MyBatis 的工作流程图:

    在这里插入图片描述

    4 入门案例

    4.1 全局配置文件 SqlMapConfig.xml

    
    DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <settings>
            
            <setting name="logImpl" value="STDOUT_LOGGING" />
        settings>
        
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql:///mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="kdx010908"/>
                dataSource>
            environment>
        environments>
        
        <mappers>
            <mapper resource="com/kdx/entity/User.xml"/>
            <mapper resource="com/kdx/mapper/UserMapper.xml"/>
        mappers>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4.2 实体类User

    User.java

    public class User {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        public User() {
        }
    
        public User(String username) {
            this.username = username;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    4.3 User.xml

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="test">
        <select id="findUserById" resultType="com.kdx.entity.User" parameterType="int">
            select * from user where id = #{id}
        select>
    
        <insert id="addUser" parameterType="com.kdx.entity.User">
            INSERT INTO USER (username) VALUES (#{username})
        insert>
    
        <update id="updateUser" parameterType="int">
            UPDATE USER SET username = #{username}  WHERE id = #{id}
        update>
    
        <delete id="deleteUser" parameterType="int">
            DELETE FROM USER WHERE id = #{id}
        delete>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.4 测试

    UserTest .java

    public class UserTest {
    
        @Test
        public void findUserById() throws Exception{
            String resource = "SqlMapConfig.xml";
            //读配置文件
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            //创建会话工厂
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            //打开会话
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = sqlSession.selectOne("test.findUserById",1);
            System.out.println(user);
            //关闭会话
            sqlSession.close();
        }
    
        @Test
        public void addUser() throws Exception{
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            sqlSession.insert("test.addUser",new User("kdx"));
            sqlSession.commit();
            System.out.println("ok");
            sqlSession.close();
        }
    
        @Test
        public void updateUser() throws Exception{
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            sqlSession.update("test.updateUser",16);
            sqlSession.commit();
            System.out.println("ok");
            sqlSession.close();
        }
    
        @Test
        public void deleteUser() throws Exception{
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            sqlSession.delete("test.deleteUser",37);
            sqlSession.commit();
            System.out.println("ok");
            sqlSession.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    5 Dao开发模式

    5.1 UserDao接口

    public interface UserDao {
    
        //根据id查询用户
        public User findUserById(Integer id)throws  Exception;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.2 UserDaoImpl

    public class UserDaoImpl implements UserDao {
    
        private SqlSessionFactory sqlSessionFactory;
    
        public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
            this.sqlSessionFactory = sqlSessionFactory;
        }
    
        @Override
        public User findUserById(Integer id) throws Exception {
            //会话
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user=sqlSession.selectOne("test.findUserById",id);
            sqlSession.close();
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.3 UserDaoTest

    public class UserDaoTest {
        SqlSessionFactory factory =null;
        @Before
        public void beforeMethod() throws IOException {
            String resource="SqlMapConfig.xml";
            //解析
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 构建 sqlSessionFactory
            factory = new SqlSessionFactoryBuilder().build(inputStream);
        }
    
        @Test
        public void testFind() throws Exception {
            //创建 UserDao的对象
            UserDao userDao = new UserDaoImpl(factory);
            User user = userDao.findUserById(1);
            System.out.println(user);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6 mapper代理模式的开发

    6.1 规范

    1.在 mapper.xml 中 namespace 为 mapper 接口地址

    <mapper namespace="com.kdx.mapper.UserMapper">mapper>
    
    • 1

    2.在 mapper.xml 中 statementID 要和 mapper 接口中的方法名保持一致
    例如:UserMapper .xml中的

    <select id="findAll" resultType="com.kdx.entity.User">
        select id,username,birthday,sex,address from user
    select>
    
    • 1
    • 2
    • 3

    对应 UserMapper 接口中的

    public List<User> findAll();
    
    • 1

    3.在 mapper.xml 中的 resultType 要和 mapper.java 中输出参数类型保持一致[返回值]
    例如上面例子中 resultType 和 User 对应

    4.在 mapper.xml 中的 parameterType 要和 mapper.java 中输入参数类型保持一致[形参]
    例如:UserMapper .xml中的 parameterType

    <delete id="delByUserId" parameterType="int">
        DELETE FROM USER WHERE id = #{id}
    delete>
    
    • 1
    • 2
    • 3

    对应 UserMapper 接口中的 int

    public void delByUserId(int id);
    
    • 1

    6.2 案例

    6.2.1 UserMapper.xml

    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.kdx.mapper.UserMapper">
        <resultMap id="UserResultMap" type="com.kdx.entity.User">
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <result column="birthday" property="birthday"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        resultMap>
    
        <select id="findAll" resultMap="UserResultMap">
            select id user_id,username,birthday,sex,address from user
        select>
    
        <insert id="addUser">
            INSERT INTO USER (username) VALUES (#{username})
        insert>
    
        <update id="updateUser">
            update USER
            <set>
                <if test="username != null and username != ''">
                    username = #{username}
                if>
            set>
            where id = #{id}
        update>
    
        <delete id="delByUserId" parameterType="int">
            DELETE FROM USER WHERE id = #{id}
        delete>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    6.2.2 UserMapper接口

    public interface UserMapper {
    
        List<User> findAll();
    
        void addUser(User user);
    
        void updateUser(User user);
    
        void delByUserId(Integer id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    6.2.3 UserMapperTest 测试

    public class UserMapperTest {
        
        private SqlSessionFactory factory = null;
        private SqlSession sqlSession = null;
        
        @Before
        public void before() throws IOException {
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            sqlSession = factory.openSession();
        }
        
        @Test
        public void findAll() {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = userMapper.findAll();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    
        @Test
        public void addUser() {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            userMapper.addUser(new User("hhh"));
            sqlSession.commit();
            System.out.println("ok");
            sqlSession.close();
        }
    
        @Test
        public void updateUser(){
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = new User();
            user.setUsername("kdx");
            user.setId(36);
            userMapper.updateUser(user);
            sqlSession.commit();
            System.out.println("ok");
            sqlSession.close();
        }
    
        @Test
        public void delByUserId(){
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            userMapper.delByUserId(40);
            sqlSession.commit();
            System.out.println("ok");
            sqlSession.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    7 动态 SQL

    MyBatis框架提供了一种称为动态SQL的功能,允许根据不同的条件动态构建SQL语句。这在实际应用中非常有用,因为它可以帮助你避免编写大量重复的SQL语句,提高了SQL语句的可维护性和灵活性。

    参考网址:https://mybatis.net.cn/dynamic-sql.html

    以下是MyBatis中动态SQL的两种常见用法:
    1.if 元素

    <select id="findUserList" parameterType="com.kdx.vo.UserQueryVo" resultType="com.kdx.entity.UserCustom">
            select * from user
            <where>
                <if test="userCustom != null">
                    <if test="userCustom.username != null and userCustom.username != ''">
                        and user.username like '%${userCustom.username}%'
                    if>
    
                    <if test="userCustom.sex != null and userCustom.sex != ''">
                        and user.sex = #{userCustom.sex}
                    if>
                if>
            where>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    以上示例中,在查询用户时,若传入用户的名字则在 sql 语句中拼接 and user.username like '%${userCustom.username}%',若传入用户的性别则在 sql 语句中拼接 and user.sex = #{userCustom.sex}

    标签相当于 sql 语句中的 where 1=1

    2.foreach 元素:对集合进行遍历(尤其是在构建 IN 条件语句的时候)
    标签中各个属性解释如下:

    • collection : 输入对象中属性的集合
    • item 每个遍历生成的对象
    • open :开始遍历时拼接的串
    • close : 遍历结束时拼接的串
    • separator : 两个对象中拼接需要的串

    例如查询 id 为1,10,15用户的信息,sql语句有两种写法
    若为:

    SELECT * FROM USER WHERE id = 1 OR id = 10 OR id =15
    
    • 1

    xml 文件如下

    <foreach collection="ids" item="user_id" open="and (" close=")" separator="or">
         id = #{user_id}
    foreach>
    
    • 1
    • 2
    • 3

    若为:

    SELECT * FROM USER WHERE id IN (1,10,15)
    
    • 1

    xml 文件如下

    <foreach collection="ids" item="user_id" open="and id in(" close=")" separator=",">
         #{user_id}
    foreach>
    
    • 1
    • 2
    • 3

    8 SQL 片段

    SQL 片段是 MyBatis 中的一种重用 SQL代码 的方法。它能够将一部分 SQL 代码定义成一个可重用的片段,然后在需要的地方进行引用,避免了代码的重复。SQL 片段通常定义在 MyBatis 的XML配置文件中,可以通过标签来创建。

    例如:

    <sql id="findUserNameSex">
         <if test="userCustom != null">
              <if test="userCustom.username != null and userCustom.username != ''">
                  and user.username like '%${userCustom.username}%'
              if>
    
              <if test="userCustom.sex != null and userCustom.sex != ''">
                  and user.sex = #{userCustom.sex}
              if>
          if>
    sql>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    然后,在其他地方的 SQL 语句中,可以通过标签来引用这个 SQL 片段:

    <select id="findUserList" parameterType="com.kdx.vo.UserQueryVo" resultType="com.kdx.entity.UserCustom">
          select * from user
          <where>
              <include refid="findUserNameSex"/>
          where>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    9 面试题:${} 和 #{}区别

    #{}是预编译处理, ${}是字符串替换。

    Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;

    Mybatis 在处理${}时,就是把${}替换成变量的值。这样可能会存在SQL注入的风险。因为参数值会被直接拼接到SQL语句中,不会被预编译,可能导致安全问题。而使用#{}可以有效的防止 SQL 注入,提高系统安全性。

    10 关联查询

    例如有四张表,user、orders、orderdetail、items,它们的关系如下:
    user --> orders:一个用户可以创建多个订单,一对多

    orders --> user: 一个订单只能由一个用户创建,一对一

    orders -->orderdetail:一个订单可以有多个订单明细,一对多

    orderdetail --> orders:一个订单明细只能属于一个订单,一对一

    orderdetail --> items:一个订单明细中只能有一个商品,一对一

    items --> orderdetail: 一个商品可以出现在多个订单明细中,一对多

    10.1 一对一

    需求: 查询订单,关联查询创建订单的用户信息
    思路:
    确定查询主表:订单表
    确定查询关联表: 用户表
    使用内连接
    确定SQL:

    SELECT orders.*,user.`username`,user.`sex`,user.`address`
    FROM USER,orders
    WHERE user.`id`=orders.`user_id`
    
    • 1
    • 2
    • 3

    订单实体类 Orders:

    public class Orders implements Serializable {
        private Integer id;
        private Integer userId;
        private String number;
        private Date createtime;
        private String note;
       
        private User user; 
        
        public Orders() {
        }
    
        public Orders(Integer userId, String number, Date createtime) {
            this.userId = userId;
            this.number = number;
            this.createtime = createtime;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public Date getCreatetime() {
            return createtime;
        }
    
        public void setCreatetime(Date createtime) {
            this.createtime = createtime;
        }
    
        public String getNote() {
            return note;
        }
    
        public void setNote(String note) {
            this.note = note;
        }
    
        @Override
        public String toString() {
            return "Orders{" +
                    "id=" + id +
                    ", userId=" + userId +
                    ", number='" + number + '\'' +
                    ", createtime=" + createtime +
                    ", note='" + note + '\'' +
                    ", user=" + user +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    使用定制类OrdersCustom接收:

    public class OrdersCustom extends Orders{
        private String username;
        private String sex;
        private String address;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    UserMapper接口:

    public interface UserMapper {
     	//查询用户及订单
        //resultType
        List<OrdersCustom> findOrderAndUser();
        //resultMap
        List<OrdersCustom> findOrderAndUserResultMap();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    UserMapper.xml
    若用 resultType

    <select id="findOrderAndUser" resultType="com.kdx.entity.OrdersCustom">
          SELECT orders.*,user.`username`,user.`sex`,user.`address`
          FROM USER,orders
          WHERE orders.`user_id`=user.`id`
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    若用 resultMap

    <select id="findOrderAndUserResultMap" resultMap="findOrderAndUserMap">
          SELECT orders.*,user.`username`,user.`sex`,user.`address`
          FROM USER,orders
          WHERE orders.`user_id`=user.`id`
    select>
    
    <resultMap id="findOrderAndUserMap" type="com.kdx.entity.OrdersCustom">
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
    
        <association property="user" javaType="com.kdx.entity.User">
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        association>
    resultMap>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    resultType 和 resultMap 总结
    resultType 使用比较简单,如果pojo中的属性和数据库中的列名完全一致, 即可以使用它完成映射。没有特殊要求的查询使用 resultType 完成映射

    resultMap 需要单独写 resultMap,有点儿小麻烦,如果对查询有特殊要求【比如添加user属性】 用resultMap 完成映射

    resultMap可以实现延迟加载,resultType无法实现延迟加载。

    10.2 一对多

    需求:查询订单以及订单明细
    思路:
    确定查询主表:订单表
    确定查询关联表: 订单明细表
    使用内连接
    确定SQL:

    SELECT orders.*,
           user.`username`,user.`sex`,user.`address`,
           orderdetail.`items_id`,orderdetail.`items_num`,orderdetail.`orders_id`
    FROM USER,orders,orderdetail
    WHERE user.`id`=orders.`user_id` AND orders.`id`=orderdetail.`orders_id`
    
    • 1
    • 2
    • 3
    • 4
    • 5

    订单明细实体类 OrderDetail:

    public class OrderDetail implements Serializable {
        private Integer id;
        private Integer ordersId;
        private Integer itemsId;
        private Integer itemsNum;
        
        public OrderDetail() {
        }
    
        public OrderDetail(Integer ordersId, Integer itemsId) {
            this.ordersId = ordersId;
            this.itemsId = itemsId;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public Integer getOrdersId() {
            return ordersId;
        }
    
        public void setOrdersId(Integer ordersId) {
            this.ordersId = ordersId;
        }
    
        public Integer getItemsId() {
            return itemsId;
        }
    
        public void setItemsId(Integer itemsId) {
            this.itemsId = itemsId;
        }
    
        public Integer getItemsNum() {
            return itemsNum;
        }
    
        public void setItemsNum(Integer itemsNum) {
            this.itemsNum = itemsNum;
        }
    
        @Override
        public String toString() {
            return "OrderDetail{" +
                    "id=" + id +
                    ", ordersId=" + ordersId +
                    ", itemsId=" + itemsId +
                    ", itemsNum=" + itemsNum +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    在 用户实体类 User 中加入:

    //订单
    private List<Orders> ordersList = new ArrayList<>();
    
    public List<Orders> getOrdersList() {
        return ordersList;
    }
    
    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在 订单实体类 Orders 中加入:

    //订单明细
    private List<OrderDetail> orderdetails = new ArrayList<>();
    
    public List<OrderDetail> getOrderDetailList() {
        return orderdetails;
    }
    
    public void setOrderDetailList(List<OrderDetail> orderdetails) {
        this.orderdetails = orderdetails;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在 UserMapper接口 加入:

    //查询订单及订单明细 
    List<OrderDetail> findOrderAndUserAndOrderDetail();
    
    • 1
    • 2

    UserMapper.xml:

    <select id="findOrderAndUserAndOrderDetail" resultMap="findOrderAndUserAndOrderDetailResultMap">
           SELECT orders.*,
           	      user.`username`,user.`sex`,user.`address`,
                  orderdetail.`items_id`,orderdetail.`items_num`,orderdetail.`orders_id`
     	   FROM USER,orders,orderdetail
    	   WHERE user.`id`=orders.`user_id` AND orders.`id`=orderdetail.`orders_id`
    select>
    
    
    <resultMap id="findOrderAndUserAndOrderDetailResultMap" type="com.kdx.entity.Orders" extends="findOrderAndUserMap"> 
    
        <collection property="orderdetails" ofType="com.kdx.entity.OrderDetail">
            <id column="orderdetail_id" property="id"/>
            <result column="orders_id" property="ordersId"/>
            <result column="items_id" property="itemsId"/>
            <result column="items_num" property="itemsNum"/>
        collection>
    
    resultMap>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    10.3 多对多

    需求: 查询用户及用户购买的商品
    思路:
    确定查询主表: 用户表
    确定查询关联表:由于用户和商品没有直接关联,所以通过订单表、订单明细表以及商品表进行关联
    使用内连接
    确定SQL:

    SELECT orders.*,
           user.`username`,user.`sex`,user.`address`,orderdetail.`id` orderdetail_id,
           orderdetail.`items_id`,orderdetail.`items_num`,orderdetail.`orders_id`,
           items.`name` item_name,items.`price` item_price,items.`detail` item_detail
    FROM USER,orders,orderdetail,items
    WHERE user.`id`=orders.`user_id` AND orders.`id`=orderdetail.`orders_id` AND orderdetail.`items_id`=items.`id`
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    由于在一对多的例子中已经做了许多工作,在此不再重复
    商品实体类 Items:

    public class Items implements Serializable {
        private Integer id;
        private String name;
        private Double price;
        private String detail;
        private String pic;
        private Date createtime;
    
        public Items() {
        }
    
        public Items(Integer id, String name, Double price, String detail, String pic, Date createtime) {
            this.id = id;
            this.name = name;
            this.price = price;
            this.detail = detail;
            this.pic = pic;
            this.createtime = createtime;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Double getPrice() {
            return price;
        }
    
        public void setPrice(Double price) {
            this.price = price;
        }
    
        public String getDetail() {
            return detail;
        }
    
        public void setDetail(String detail) {
            this.detail = detail;
        }
    
        public String getPic() {
            return pic;
        }
    
        public void setPic(String pic) {
            this.pic = pic;
        }
    
        public Date getCreatetime() {
            return createtime;
        }
    
        public void setCreatetime(Date createtime) {
            this.createtime = createtime;
        }
    
        @Override
        public String toString() {
            return "Items{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", price=" + price +
                    ", detail='" + detail + '\'' +
                    ", pic='" + pic + '\'' +
                    ", createtime=" + createtime +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    在订单明细实体类 OrderDetail 中加入:

    //商品
    private Items items;
    
    public Items getItems() {
        return items;
    }
    
    public void setItems(Items items) {
        this.items = items;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在 UserMapper接口 加入:

    //查询用户及其购买的商品
    List<User> findUserAndItemResultMap();
    
    • 1
    • 2

    UserMapper.xml:

    <select id="findUserAndItemResultMap" resultMap="findUserItemResultMap">
         SELECT orders.*,
                 user.`username`,user.`sex`,user.`address`,orderdetail.`id` orderdetail_id,
                 orderdetail.`items_id`,orderdetail.`items_num`,orderdetail.`orders_id`,
                 items.`name` item_name,items.`price` item_price,items.`detail` item_detail
          FROM USER,orders,orderdetail,items
          WHERE user.`id`=orders.`user_id` AND orders.`id`=orderdetail.`orders_id` AND orderdetail.`items_id`=items.`id`
    select>
    
        <resultMap id="findUserItemResultMap" type="com.kdx.entity.User">
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <result column="birthday" property="birthday"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
    
            <collection property="ordersList" ofType="com.kdx.entity.Orders">
                <id column="id" property="id"/>
                <result column="user_id" property="userId"/>
                <result column="number" property="number"/>
                <result column="createtime" property="createtime"/>
                <result column="note" property="note"/>
    
                <collection property="orderdetails" ofType="com.kdx.entity.OrderDetail">
                    <id column="orderdetail_id" property="id"/>
                    <result column="orders_id" property="ordersId"/>
                    <result column="items_id" property="itemsId"/>
                    <result column="items_num" property="itemsNum"/>
    
                    <association property="items" javaType="com.kdx.entity.Items">
                        <id column="items_id" property="id"/>
                        <result column="item_name" property="name"/>
                        <result column="item_price" property="price"/>
                        <result column="item_detail" property="detail"/>
                    association>
                collection>
            collection>
        resultMap>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    11 延迟加载

    MyBatis 中的延迟加载(Lazy Loading,又叫懒加载、慢加载)是一种性能优化技术,它可以在需要的时候才从数据库中加载数据,而不是在对象初始化时就立即加载。这样可以避免在查询主对象时同时加载关联对象,提高了查询性能和减少了数据库查询次数,也就是说可以按需查询数据库。

    延迟加载只能使用 来实现, 都具备延迟加载功能。

    示例:查询订单表关联查询用户信息
    1.全局配置文件 SqlMapConfig.xml 中加入以下内容

    <settings>
           
           <setting name="lazyLoadingEnabled" value="true"/>
           
           <setting name="aggressiveLazyLoading" value="false"/>
    settings>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.在UserMapper.xml 中加入以下内容

    	<select id="findUserById" resultType="com.kdx.entity.User" parameterType="int">
            select * from user where id = #{id}
        select>
    
        <select id="findOrdersAndUserLazyLoading" resultMap="lazyLoading">
            select * from orders
        select>
    
        <resultMap id="lazyLoading" type="com.kdx.entity.Orders">
            <id column="id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>
    
            <association property="user" javaType="com.kdx.entity.User" select="findUserById" column="user_id">
    
            association>
        resultMap>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.在UserMapper 接口中加入以下内容

    //根据id查询用户
    User findUserById(int id);
    //延迟加载查询订单及用户信息
    List<Orders> findOrdersAndUserLazyLoading();
    
    • 1
    • 2
    • 3
    • 4

    4.测试延迟加载

    public class UserMapperTest {
    	private SqlSessionFactory factory = null;
        private SqlSession sqlSession = null;
        
        //在JUnit测试中,@Before 注解标记的方法会在每个测试方法运行之前被执行。
        @Before
        public void before() throws IOException {
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            sqlSession = factory.openSession();
        }
        
        //测试延迟加载
    	@Test
        public void findOrdersAndUserLazyLoading(){
            sqlSession = factory.openSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<Orders> ordersList = userMapper.findOrdersAndUserLazyLoading();
    
            for (int i = 0; i < ordersList.size(); i++) {
                System.out.println("------------------------------");
                Orders orders = ordersList.get(i);
                User user = orders.getUser();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在 fori 循环之前打断点进行调试:
    在这里插入图片描述

    12 缓存支持

    MyBatis提供了两级缓存机制来提高性能:一级缓存(本地缓存)和二级缓存(全局缓存)

    12.1 一级缓存(Local Cache / Session Cache)

    一级缓存是指在同一个 SqlSession 中进行的缓存。默认情况下,MyBatis 会开启一级缓存。当执行一个查询时,查询的结果会被放入 SqlSession 的缓存中,如果后续的查询使用相同的 SqlSession 对象并且参数也相同,MyBatis 会直接从缓存中取得结果,而不会再次查询数据库。

    一级缓存的生命周期是和 SqlSession 绑定的,当 SqlSession 关闭时,缓存数据也会被清空。

    测试一级缓存:

    public class UserMapperTest {
    	private SqlSessionFactory factory = null;
        private SqlSession sqlSession = null;
        
        //在JUnit测试中,@Before 注解标记的方法会在每个测试方法运行之前被执行。
        @Before
        public void before() throws IOException {
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            sqlSession = factory.openSession();
        }
        
        //测试一级缓存
    	@Test
        public void testCache1(){
            sqlSession = factory.openSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user1 = userMapper.findUserById(1);
            System.out.println("-------------------------");
            User user2 = userMapper.findUserById(1);
            System.out.println(user1==user2);
            sqlSession.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在查询用户信息之前打断点进行调试,调试结果如下:
    在这里插入图片描述
    从调试结果来看,第一次查询用户信息时,通过数据库查询数据,第二次查询用户信息时则从一级缓存中查询到了用户信息,并且两次查询得到的用户哈希地址也相同。

    12.2 二级缓存(Global Cache)

    二级缓存是指在同一个 Mapper 的不同 SqlSession 之间进行的缓存。开启二级缓存需要在Mapper的映射文件中进行配置:(在UserMapper.xml进行如下配置)

    <mapper namespace="com.example.mapper.UserMapper" >
    	
        <cache
                eviction="FIFO"
                flushInterval="60000"
                size="512"
                readOnly="true"/> 
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在MyBatis的配置文件中,也需要开启全局缓存:(在全局配置文件 SqlMapConfig.xml 中加入以下内容)

    <settings>
    	
        <setting name="cacheEnabled" value="true"/>
    settings>
    
    • 1
    • 2
    • 3
    • 4

    开启二级缓存后,当一个 SqlSession 执行完毕并提交或关闭时,这个 Session 中的所有数据会被提交到二级缓存中。当另一个 SqlSession 需要执行相同的查询时,MyBatis 会先从二级缓存中查找是否有相应的数据,如果有,就直接返回,不再查询数据库。

    注意:为了使得某个 Mapper 的某个查询开启二级缓存,该 Mapper 的对应的 POJO 需要实现序列化接口(Serializable),因为二级缓存的数据可能需要被序列化到磁盘中,以便在应用重启后能够重新加载。

    测试二级缓存

    public class UserMapperTest {
    	private SqlSessionFactory factory = null;
    	   
        //在JUnit测试中,@Before 注解标记的方法会在每个测试方法运行之前被执行。
        @Before
        public void before() throws IOException {
            String resource = "SqlMapConfig.xml";
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        }
        
        //测试二级缓存
    	@Test
        public void testCache2(){
            SqlSession sqlSession1 = factory.openSession();
            SqlSession sqlSession2 = factory.openSession();
            UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
            User user1 = userMapper1.findUserById(1);
            sqlSession1.close();//需要关闭sqlSession1后再查询
            System.out.println("---------------------------------");
            UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
            User user2 = userMapper2.findUserById(1);
            System.out.println(user1==user2);
            sqlSession2.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在查询用户信息之前打断点进行调试,调试结果如下:
    在这里插入图片描述
    从调试结果来看,第一次查询用户信息时,由于缓存中没有相应数据,所以需要通过数据库查询数据;第二次查询用户信息时,由于 sqlSession1 执行完毕并关闭,MyBatis 会先从二级缓存中查找是否有相应的数据,如果有,就直接返回,不再查询数据库。并且两次查询得到的用户哈希地址也相同。

  • 相关阅读:
    vue diff 算法学习
    在Spring Boot中使用POI完成一个excel报表导入数据到MySQL的功能
    35、Lua 中的模块与module函数
    【Linux】Linux权限
    strlen · strcpy | 使用场景与模拟实现
    MyBatis注解开发实现学生管理页面(分页pagehelper,多条件搜索,查看课程信息)
    使用 Tesseract 和 OpenCV 基于深度学习的 OCR 文本识别
    计算机毕业设计Java菜市场的疫情防控管理系统(源码+系统+mysql数据库+lw文档)
    linux下端口映射
    Figma转Sketch文件教程,超简单!
  • 原文地址:https://blog.csdn.net/k010908/article/details/133799089