Geeks_Z の Blog Geeks_Z の Blog
首页
  • 学习笔记

    • 《HTML》
    • 《CSS》
    • 《JavaWeb》
    • 《Vue》
  • 后端文章

    • Linux
    • Maven
    • 汇编语言
    • 软件工程
    • 计算机网络概述
    • Conda
    • Pip
    • Shell
    • SSH
    • Mac快捷键
    • Zotero
  • 学习笔记

    • 《数据结构与算法》
    • 《算法设计与分析》
    • 《Spring》
    • 《SpringMVC》
    • 《SpringBoot》
    • 《SpringCloud》
    • 《Nginx》
  • 深度学习文章
  • 学习笔记

    • 《PyTorch》
    • 《ReinforementLearning》
    • 《MetaLearning》
  • 学习笔记

    • 《高等数学》
    • 《线性代数》
    • 《概率论与数理统计》
  • 增量学习
  • 哈希学习
GitHub (opens new window)

Geeks_Z

AI小学生
首页
  • 学习笔记

    • 《HTML》
    • 《CSS》
    • 《JavaWeb》
    • 《Vue》
  • 后端文章

    • Linux
    • Maven
    • 汇编语言
    • 软件工程
    • 计算机网络概述
    • Conda
    • Pip
    • Shell
    • SSH
    • Mac快捷键
    • Zotero
  • 学习笔记

    • 《数据结构与算法》
    • 《算法设计与分析》
    • 《Spring》
    • 《SpringMVC》
    • 《SpringBoot》
    • 《SpringCloud》
    • 《Nginx》
  • 深度学习文章
  • 学习笔记

    • 《PyTorch》
    • 《ReinforementLearning》
    • 《MetaLearning》
  • 学习笔记

    • 《高等数学》
    • 《线性代数》
    • 《概率论与数理统计》
  • 增量学习
  • 哈希学习
GitHub (opens new window)
  • Linux

  • 数据结构与算法

  • 算法设计与分析

  • Java

  • Python

  • 设计模式

  • 计算机网络

  • Spring笔记

  • SpringMVC笔记

  • SpringBoot笔记

  • SpringSecurity

  • Elasticsearch笔记

  • RabbitMQ笔记

  • Docker笔记

  • MySQL

  • Redis

  • Mybatis

    • MyBatis
      • MyBatis
        • Reference
        • 框架
        • 定义
        • 三层架构
        • 持久层技术解决方案
        • 对原生jdbc程序中的问题总结
        • jdbc编程步骤
        • 问题总结
        • 参考代码
        • MyBatis框架概述
        • 搭建 Mybatis 开发环境
        • 基于配置文件
        • 基于注解
        • 环境搭建的注意事项
        • mybatis原理分析
        • 代理Dao实现CRUD操作
        • 实体类属性名与数据库列名不一致
        • 参数深入
        • OGNL表达式
        • resultType
        • parameterType
        • properties
        • typeAliases
        • mappers
        • resultMap
        • Mybatis 与 JDBC 编程的比较
        • 连接池与事务
        • mybatis中的连接池
        • mybatis中的事务
        • SQL
        • where/if
        • foreach
        • 一对一(多对一)
        • 方式一:定义po类
        • 方式二:定义resultMap
        • 一对多
        • 多对多
        • 加载
        • 一对一(association)
        • 一对多(collection)
        • 缓存
        • 注解开发
        • 单表CRUD
        • 多表一对一
        • 多表一对多
        • Reference
  • MybatisPlus

  • Nginx

  • Kubernetes笔记

  • Git

  • Software

  • 微服务笔记

  • bug

  • BackEndNotes
  • Mybatis
Geeks_Z
2022-08-14
目录

MyBatis

MyBatis

Reference

  • krislinzhao/StudyNotes (opens new window)

框架

定义

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。 简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。

三层架构

表现层:展示数据

业务层:处理业务需求

持久层:与数据库交互

01三层架构

02持久层总图

持久层技术解决方案

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

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

对原生jdbc程序中的问题总结

jdbc编程步骤

  1. 加载数据库驱动
  2. 创建并获取数据库链接
  3. 创建jdbc statement对象
  4. 设置sql语句
  5. 设置sql语句中的参数(使用preparedStatement)
  6. 通过statement执行sql并获取结果
  7. 对sql执行结果进行解析处理
  8. 释放资源(resultSet、preparedstatement、connection)

问题总结

1.数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。

使用数据库连接池管理数据库连接。

2.将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。

将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。

3.向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。

将sql语句及占位符号和参数全部配置在xml中。

4.从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。

将查询的结果集,自动映射成java对象。

参考代码

package com.iot.mybatis.jdbc;

//import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Created by Administrator on 2016/2/21.
 */
public class JdbcTest {
    public static void main(String[] args) {
        //数据库连接
        Connection connection = null;
        //预编译的Statement,使用预编译的Statement提高数据库性能
        PreparedStatement preparedStatement = null;
        //结果集
        ResultSet resultSet = null;

        try {
            //加载数据库驱动
            Class.forName("com.mysql.jdbc.Driver");

            //通过驱动管理类获取数据库链接
            connection =  DriverManager.getConnection("jdbc:mysql://120.25.162.238:3306/mybatis001?characterEncoding=utf-8", "root", "123");
            //定义sql语句 ?表示占位符
            String sql = "select * from user where username = ?";
            //获取预处理statement
            preparedStatement = connection.prepareStatement(sql);
            //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
            preparedStatement.setString(1, "王五");
            //向数据库发出sql执行查询,查询出结果集
            resultSet =  preparedStatement.executeQuery();
            //遍历查询结果集
            while(resultSet.next()){
                System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //释放资源
            if(resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    }

}

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

MyBatis框架概述

mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身, 而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。 mybatis 通过 ==xml== 或==注解==的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。 采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

搭建 Mybatis 开发环境

基于配置文件

参考代码 mybatis

  1. 编写持久层接口的映射文件IUserDao.xml

    image-20220124100704994
  2. Code

    SqlMapConfig.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--mybatis主配置文件-->
    <configuration>
        <!--配置环境-->
        <environments default="mysql">
        <!--配置mysql的环境-->
            <environment id="mysql">
                <!-- 配置事务类型-->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 配置数据源(连接池)-->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/exercise"/>
                    <property name="username" value="root"/>
                    <property name="password" value="1024"/>
                </dataSource>
            </environment>
        </environments>
    
    <!--    指定映射配置文件的位置,映射配置文件是指每个dao独立的配置文件-->
    <mappers>
        <!-- 基于配置文件-->
        <mapper resource="cn/edu/buaa/basic/dao/IUserDao.xml"></mapper>
    </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
    28

    IUserDao.java

    public interface IUserDao {
    
        /**
         * 查询所有
         * @return
         */
        List<User> findAll();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    IUserDao.xml

    <mapper namespace="cn.edu.buaa.basic.dao.IUserDao">
        <!--配置查询所有-->
        <select id="findAll" resultType="cn.edu.buaa.basic.domain.User">
            select * from user
        </select>
    </mapper>
    
    1
    2
    3
    4
    5
    6

    MyBatisTest.java

     public static void main(String[] args)throws Exception {
            //1.读取配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建SqlSessionFactory工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(in);
            //3.使用工厂生产SqlSession对象
            SqlSession session = factory.openSession();
            //4.使用SqlSession创建Dao接口的代理对象
            IUserDao userDao = session.getMapper(IUserDao.class);
            //5.使用代理对象执行方法
            List<User> users = userDao.findAll();
            for(User user : users){
                System.out.println(user);
            }
            //6.释放资源
            session.close();
            in.close();
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

基于注解

参考代码:mybatis_annoation

  1. Code

    IUserDao.java

    public interface IUserDao {
    
        /**
         * 查询所有操作
         * @return
         */
        @Select("select * from user")
        List<User> findAll();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    修改 SqlMapConfig.xml

    <!-- 告知 mybatis 映射配置的位置 --> 
    <mappers>
    		<mapper class="com.itheima.dao.IUserDao"/>
    </mappers>
    
    1
    2
    3
    4
  2. 在使用基于注解的 Mybatis 配置时,请移除 xml 的映射配置(IUserDao.xml)。

环境搭建的注意事项

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

mybatis原理分析

入门案例的分析

查询所有的分析

04mybatis的分析

代理Dao实现CRUD操作

IUserDao.java

package cn.edu.buaa.basic.dao;

import cn.edu.buaa.basic.domain.QueryVo;
import cn.edu.buaa.basic.domain.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @InterfaceName IUser
 * @Description TODO
 * @Author Geeks_Z
 * @Data 2022/1/16 21:37
 * @Version 1.0
 * 用户持久层接口
 **/
public interface IUserDao {

    /**
     * 查询所有
     * @return
     */
    List<User> findAll();

    /**
     * save user
     * @param user
     */
    void saveUser(User user);
    void updateUser(User user);
    void deleteUser(Integer userId);
    User findById(Integer userId);

    /**
     * 根据名称模糊查询用户信息
     * @param username
     * @return
     */
    List<User> findByName(String username);
    int findTotal();
    /**
     * 根据queryvo中的条件查询用户
     * @param vo
     * @return
     */
    List<User> findUserByVo(QueryVo vo);
}

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

IUserDao.xml

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

    <!--配置查询所有-->
    <select id="findAll" resultType="cn.edu.buaa.basic.domain.User">
        select * from user;
    </select>

    <!--保存用户-->
    <insert id="saveUser" parameterType="cn.edu.buaa.basic.domain.User">
        insert into user(username,address,sex,birthday) values(#{username}, #{address}, #{sex}, #{birthday});
        <!--配置插入操作后,获取插入数据的id-->
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
    </insert>

    <!--更新用户-->
    <update id="updateUser" parameterType="cn.edu.buaa.basic.domain.User">
        update user set username=#{username},address=#{address}, sex=#{sex},birthday=#{birthday} where id=#{id};
    </update>

    <!--删除用户-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <!--根据id查询-->
    <select id="findById" resultType="cn.edu.buaa.basic.domain.User" parameterType="int">
        select * from user where id=#{uid}
    </select>

    <!--根据名称查询-->
    <select id="findByName" resultType="cn.edu.buaa.basic.domain.User" parameterType="string">
        select * from user where username like#{username};
    </select>
    <!-- 获取用户的总记录条数 -->
    <select id="findTotal" resultType="int">
        select count(id) from user;
    </select>

    <!-- 根据queryvo的条件查询用户   -->
    <select id="findUserByVo" parameterType="cn.edu.buaa.basic.domain.QueryVo" resultType="cn.edu.buaa.basic.domain.User">
        select * from user where username like #{user.username}
    </select>
</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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

细节查看Mybatis第二天讲义

实体类属性名与数据库列名不一致

User.java

private Integer userId;
private String userName;
private String userAddress;
private String userSex;
private Date userBirthday;
1
2
3
4
5

数据库

image-20220124131353216

方式一:修改sql语句

<select id="findAll" resultMap="userMap">
        <!--解决数据库列名与实体类属性名不一致-->
        select id as userId,username as userName,address as userAddress,sex as userSex,birthday as userBirthday from user;
    </select>
1
2
3
4

方式二:修改配置文件

IUserDao.xml

<!--配置 查询结果的列名和实体类的属性名的对应关系
        property为实体类属性名
        column为数据库列名
    -->
<!-- type对应查询的是哪个实体类-->
    <resultMap id="userMap" type="UsEr">
        <!-- 主键字段的对应 -->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
    </resultMap>

<!--配置查询所有-->
    <select id="findAll" resultMap="userMap">
        <!--解决数据库列名与实体类属性名不一致-->
        <!--select id as userId,username as userName,address as userAddress,sex as userSex,birthday as userBirthday from user;-->
        select * from user;
    </select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

参数深入

OGNL表达式

​ Object Graphic Navigation Language ​ 对象 图 导航 语言

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

resultType

用于指定结果集的类型。可以指定结果集的类型,它支持基本类型和实体类类型。如果注册过类型别名的,可以直接使用别名。同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。

parameterType

用于指定传入参数的类型。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类。

基本类型和 String 我们可以直接写类型名称,也可以使用包名.类名的方式,例如: java.lang.String。 实体类类型,目前我们只能使用全限定类名。 究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名, 而我们的是实体类并没有注册别名,所以必须写全限定类名。

properties

指定属性配置时,可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息。

image-20220123152544275

typeAliases

用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就不再区分大小写

<package>:用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写

<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="com.itheima.domain.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<<package name="com.itheima.domain"/> 
<package name="其它包"/>
</typeAliases>

1
2
3
4
5
6
7
8

mappers

映射器

<mapper resource=""/>

 使用相对于类路径的资源
如:<mapper resource="com/itheima/dao/IUserDao.xml" />
1
2

<mapper class=""/>

使用 mapper 接口类路径
如:<mapper class="com.itheima.dao.UserDao"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
1
2
3

<package name=""/>

package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了

注册指定包下的所有 mapper 接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
1
2
3

resultMap

resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。

在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。

<mapper namespace="cn.edu.buaa.basic.dao.IUserDao">

    <!--配置 查询结果的列名和实体类的属性名的对应关系
        property为实体类属性名(本例中属性名与数据库列名一致)
        column为数据库列名
    -->
        <resultMap id="userMap" type="user"> <!--type不区分大小写-->
        <!-- 主键字段的对应 -->
        <id property="id" column="id"/>
        <!-- 非主键字段的对应 -->
        <result property="address" column="address"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <result property="birthday" column="birthday"/>
        </resultMap>
id 标签:用于指定主键字段 
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名 
property 属性:用于指定实体类属性名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Mybatis 与 JDBC 编程的比较

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

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

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

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

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

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

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

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

连接池与事务

mybatis中的连接池

​ mybatis连接池提供了3种方式的配置:

配置的位置:

主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。

type属性的取值:

  • POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
  • UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
  • JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
    • 注意:如果不是web或者maven的war工程,是不能使用的。
    • 我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。

mybatis中的事务

什么是事务

事务的四大特性ACID

不考虑隔离性会产生的3个问题

解决办法:四种隔离级别

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

SQL

where/if

<!--根据条件查询-->
    <!--根据条件查询-->
    <select id="findUserByCondition" resultType="cn.edu.buaa.domain.User" parameterType="user">
        select * from user
        <where>
            <if test="username != null">
                and username = #{username}
            </if>
            <if test="sex != null">
                and sex = #{sex}
            </if>
        </where>
    </select>
<!--注意:<if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。 另外要注意 where 1=1 的作用~!-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14

foreach

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

<!--SQL 语句:
select 字段 from user where id in (?)
<foreach>标签用于遍历集合,它的属性: collection:代表要遍历的集合元素,注意编写时不要写#{} open:代表语句的开始部分
close:代表结束部分-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

一对一(多对一)

Code: mybatis_one2many

本次案例主要以最为简单的用户和账户的模型来分析Mybatis多表关系。用户为User 表,账户为Account 表。一个用户(User)可以有多个账户(Account)。具体关系如下:

image-20220128194246645

需求 查询所有账户信息,关联查询下单用户信息。 注意: 因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如 果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。

方式一:定义po类

定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

PO即持久对象(Persistent Object),它们是由一组属性和属性的get和set方法组成,PO对应于数据库的表。 User.java

public class User {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    //set get toString方法自动生成
}
1
2
3
4
5
6
7
8

Account.java

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;
    //set get toString方法自动生成
}
1
2
3
4
5
6
7

AccountUser.java

/* 为了能够封装上面SQL语句的查询结果,定义AccountCustomer类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser类时可以继承Account类。
*/
public class AccountUser extends Account {

    private String username;
    private String address;
    //set get 方法自动生成
    @Override
    public String toString() {
        return super.toString()+"        AccountUser{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

IAccountDao.java

public interface IAccountDao {

    /**
     * 查询所有账户 同时获取当前账户所属用户信息
     * @return
     */
    List<Account> findAll();

    /**
     * 查询所有账户 并且带有用户名称和地址信息
     * @return
     */
    List<AccountUser> findAllAccount();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

IAccountDao.xml

<!--查询所有账户同时包含用户名和地址信息-->
    <select id="findAllAccount" resultType="accountuser">
        select a.*,u.username,u.address from account a , user u where u.id = a.uid;
    </select>
1
2
3
4

方式二:定义resultMap

使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。 通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个User 类的对象来代表这个账户是哪个用户的。

Account.java

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    //从表实体应该包含一个主表实体的对象引用
    private User user;
   
}
1
2
3
4
5
6
7
8
9
10

IAccountDao.java

public interface IAccountDao {

    /**
     * 查询所有账户 同时获取当前账户所属用户信息
     * @return
     */
    List<Account> findAll();
    
}
1
2
3
4
5
6
7
8
9

IAccountDao.xml

<!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

一对多

需求: 查询所有用户信息及用户关联的账户信息。

分析:用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。

User.java

public class User {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    
    //一对多关系映射:主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
    
    //set get toString方法自动生成
}

1
2
3
4
5
6
7
8
9
10
11
12
13

IUserDao.xml

<mapper namespace="cn.edu.buaa.dao.IUserDao">

    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!-- 配置user对象中accounts集合的映射 -->
        <!-- collection是用于建立一对多中集合属性的对应关系
        ofType 用于指定集合元素的数据类型 -->
        <collection property="accounts" ofType="account">
            <id column="aid" property="id"></id>
            <result column="uid" property="uid"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userAccountMap">
        select u.*, a.id as aid,a.uid,a.money from user u left outer join account a on u.id = a.uid
    </select>

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

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

多对多

多对多关系其实我们看成是双向的一对多关系。

image-20220128214457611

步骤:

  1. 建立两张表:用户表,角色表
    • 让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
  2. 建立两个实体类:用户实体类和角色实体类
    • 让用户和角色的实体类能体现出来多对多的关系
    • 各自包含对方一个集合引用
  3. 建立两个配置文件
    • 用户的配置文件
    • 角色的配置文件
  4. 实现配置:
    • 当我们查询用户时,可以同时得到用户所包含的角色信息
    • 当我们查询角色时,可以同时得到角色的所赋予的用户信息

Role.java

public class Role implements Serializable {

    private Integer roleId;
    private String roleName;
    private String roleDesc;

    //多对多的关系映射:一个角色可以赋予多个用户
    private List<User> users;
}
1
2
3
4
5
6
7
8
9

RoleDao.java

public interface IRoleDao {


    /**
     * 查询所有角色
     * @return
     */
    List<Role> findAll();

}
1
2
3
4
5
6
7
8
9
10

IRoleDao.xml

<mapper namespace="cn.edu.buaa.dao.IRoleDao">

    <!--定义role表的ResultMap-->
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="user">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </collection>
    </resultMap>

    <!--查询所有-->
    <select id="findAll" resultMap="roleMap">
       select u.*,r.id as rid,r.role_name,r.role_desc from role r
        left outer join user_role ur  on r.id = ur.rid
        left outer join user u on u.id = ur.uid
    </select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

加载

延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

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

一对一(association)

SqlMapConfig.xml

<configuration>

    <!--配置参数-->
    <settings>
        <!--开启Mybatis支持延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>
    <!--其他省略-->
<configuration>
1
2
3
4
5
6
7
8
9
10

IAccountDao.java

public interface IAccountDao {

    /**
     * 查询所有账户 同时获取当前账户所属用户信息
     * @return
     */
    List<Account> findAll();
}
1
2
3
4
5
6
7
8

IAccountDao.xml

<mapper namespace="cn.edu.buaa.dao.IAccountDao">

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!-- 一对一的关系映射:配置封装user的内容
        select属性指定的内容:查询用户的唯一标识:
        column属性指定的内容:用户根据id查询时,所需要的参数的值(传递给 select 映射的参数)
        -->
        <association property="user" column="uid" javaType="user" select="cn.edu.buaa.dao.IUserDao.findById">
        </association>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="accountUserMap">
        select * from account
    </select>
    
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

IUserDao.xml

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

一对多(collection)

IUserDao.xml

<mapper namespace="cn.edu.buaa.dao.IUserDao">

    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!-- 配置user对象中accounts集合的映射 -->
        <!-- collection是用于建立一对多中集合属性的对应关系
        ofType 用于指定集合元素的数据类型 -->
        <collection property="accounts" ofType="account" select="cn.edu.buaa.dao.IAccountDao.findAccountByUid" column="id"> </collection>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userAccountMap">
        select * from user
    </select>

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

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

AccountDao.java

 List<Account> findAccountByUid(Integer uid);
1

IAccountDao.xml

<!-- 查询所有 -->
    <select id="findAccountByUid" resultType="account">
        select * from account where uid = #{uid}
    </select>
1
2
3
4

缓存

  1. 缓存:存在于内存中的临时数据。
  2. 好处:减少和数据库的交互次数,提高执行效率。
  3. 适用于缓存:
    • 经常查询并且不经常改变的。
    • 数据的正确与否对最终结果影响不大的。
  4. 不适用于缓存:
    • 经常改变的数据
    • 数据的正确与否对最终结果影响很大的。
    • 例如:商品的库存,银行的汇率,股市的牌价。
  5. 一级缓存:
    • 它指的是Mybatis中SqlSession对象的缓存。
    • 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
    • 该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用。
    • 当SqlSession对象消失时,mybatis的一级缓存也就消失了。
  6. 二级缓存:
    • 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
    • 二级缓存的使用步骤: 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置) 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置) 第三步:让当前的操作支持二级缓存(在select标签中配置)

注解开发

单表CRUD

IUserDao.java

public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
    void updateUser(User user);

    /**
     * 删除用户
     * @param userId
     */
    @Delete("delete from user where id=#{id} ")
    void deleteUser(Integer userId);

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user  where id=#{id} ")
    User findById(Integer userId);

    /**
     * 根据用户名称模糊查询
     * @param username
     * @return
     */
    //@Select("select * from user where username like #{username} ")
    @Select("select * from user where username like '%${value}%' ")
    List<User> findUserByName(String username);

    /**
     * 查询总用户数量
     * @return
     */
    @Select("select count(*) from user ")
    int findTotalUser();
}

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

多表一对一

IAccountDao.java

public interface IAccountDao {

    /**
     * 查询所有账户,并且获取每个账户所属的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id="accountMap",value = {
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one=@One(select="cn.edu.buaa.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();

    /**
     * 根据用户id查询账户信息
     * @param userId
     * @return
     */
    @Select("select * from account where uid = #{userId}")
    List<Account> findAccountByUid(Integer userId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

IUserDao.java

public interface IUserDao {

    /**
     * 查询所有用户
     *
     * @return
     */
    @Select("select * from user")
    @Results(id = "userMap", value = {
            @Result(id = true, column = "id", property = "userId"),
            @Result(column = "username", property = "userName"),
            @Result(column = "address", property = "userAddress"),
            @Result(column = "sex", property = "userSex"),
            @Result(column = "birthday", property = "userBirthday"),
            @Result(property = "accounts", column = "id",
                    many = @Many(select = "cn.edu.buaa.dao.IAccountDao.findAccountByUid",
                            fetchType = FetchType.LAZY))
    })
    List<User> findAll();

    /**
     * 根据id查询用户
     *
     * @param userId
     * @return
     */
    @Select("select * from user  where id=#{id} ")
    @ResultMap("userMap")
    User findById(Integer userId);

    /**
     * 根据用户名称模糊查询
     *
     * @param username
     * @return
     */
    @Select("select * from user where username like #{username} ")
    @ResultMap("userMap")
    List<User> findUserByName(String username);

}
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

多表一对多

Reference

https://blog.csdn.net/h3243212/article/details/51016271

上次更新: 2024/03/29, 08:56:31
缓存同步
Mybatis-Plus介绍

← 缓存同步 Mybatis-Plus介绍→

最近更新
01
并行训练
03-29
02
tensor维度转换
03-26
03
ResNet源码解读
03-23
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Geeks_Z | MIT License
京公网安备 11010802040735号 | 京ICP备2022029989号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式