Mybatis小总结

Mybatis常见面试题总结_Java_a745233700的博客-CSDN博客
mybatis 模糊查询 mapper.xml的写法 - sc6231565的专栏 - CSDN博客
Mybatis 获取自增主键值(Mysql,Oracle)
Mybatis-plus 小总结
【精选】MyBatis-@param注解详解_mybatis @param-CSDN博客 2023-11-22 10:25:52

概述

  1. 什么是框架?
    1
    2
    3
    它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。
    使用框架的好处:
    框架封装了很多的细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。
  2. 三层架构
    1
    2
    3
    4
    5
    6
    表现层:
    是用于展示数据的
    业务层:
    是处理业务需求
    持久层:
    是和数据库交互的
  3. 持久层技术解决方案
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    JDBC技术:
    Connection
    PreparedStatement
    ResultSet
    Spring的JdbcTemplate:
    Spring中对jdbc的简单封装
    Apache的DBUtils:
    它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装

    以上这些都不是框架
    JDBC是规范
    Spring的JdbcTemplate和Apache的DBUtils都只是工具类
  4. mybatis的概述
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    mybatis是一个持久层框架,用java编写的。
    它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程
    它使用了ORM思想实现了结果集的封装。

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

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

简单入门

配置方式实现

源码mybatis-test

在这里插入图片描述

SqlMapConfig.xml
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
<?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">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/student?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>

<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="dao/StudentDao.xml"/>
</mappers>
</configuration>
MyTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyTest
{
@Test
public void t1() 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接口的代理对象
StudentDao userDao = session.getMapper(StudentDao.class);
//5.使用代理对象执行方法
List<Student> students = userDao.findAll();
students.forEach(System.out::println);
//6.释放资源
session.close();
in.close();
}
}

注解实现

源码mybatis-test

只需要在上述步骤改动两个部分即可。
(1)在接口方法上加@select注解
(2)在mapper中配置class="tpf.Dao.StudentDao"来指定接口,同时也要注释之前的resource="dao/StudentDao.xml"
在这里插入图片描述

自定义Mybatis

  • 分析
    在这里插入图片描述
    在这里插入图片描述
  • 实现
    本地源码文件E:\Everything\Work\2019java课程完结版\HMjava\00 讲义+笔记+资料\4.主流框架\31.Mybatis\mybatis\mybatis_day01\代码\mybati第一天源码\day01_eesy_04mybatis_design
    在这里插入图片描述

CRUD

  • 根据ID查询
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    User findById(Integer userId); 

    <select id="findById" resultType="com.itheima.domain.User" parameterType="int">
    select * from user where id = #{uid}
    </select>

    id:方法名
    resultType:返回值类型
    parameterType:参数类型
    #{}:相当于jdbc中的?占位符【此时传参就一个基本类型,也可以不与参数名称一致】
  • 新增【记得要使用session.commit();进行提交】
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int saveUser(User user); 

    <insert id="saveUser" parameterType="com.itheima.domain.User">
    insert into user(username,birthday,sex,address)
    values(#{username},#{birthday},#{sex},#{address})
    </insert>

    parameterType:传入的参数是一个对象,所以需要全限定类名。
    #{}:此处写的是User对象的属性名称。它用的是ognl表达式。

    ognl 表达式:
    它是 apache 提供的一种表达式语言,全称是:
    Object Graphic Navigation Language 对象图导航语言
    它是按照 #{对象.对象} 获取数据的。
    【这里我们parameterType指定了User,所以可以直接写属性】
  • 新增并返回Id
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int savaStudent(Student student);

    <insert id="savaStudent" parameterType="tpf.entity.Student">
    <!-- 配置插入操作后,获取插入数据的id -->
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
    select last_insert_id();
    </selectKey>
    insert into student values(null ,#{stu_name});
    </insert>

    Student student = new Student(null, "5555");
    System.out.println(student);// Student(id=null, stu_name=5555)
    int i = userDao.savaStudent(student);
    System.out.println(i);// 1
    System.out.println(student);// Student(id=114, stu_name=5555)
  • 更新
    1
    2
    3
    4
    5
    6
    int updateUser(User user); 

    <update id="updateUser" parameterType="com.itheima.domain.User">
    update user set username=#{username},birthday=#{birthday},sex=#{sex},
    address=#{address} where id=#{id}
    </update>
  • 删除
    1
    2
    3
    4
    5
    int deleteUser(Integer userId); 

    <delete id="deleteUser" parameterType="java.lang.Integer">
    delete from user where id = #{uid}
    </delete>
  • 模糊查询
    1
    2
    3
    4
    5
    6
    7
    List<User> findByName(String username); 

    <select id="findByName" resultType="com.itheima.domain.User" parameterType="String">
    select * from user where username like #{username}
    </select>

    List<User> users = userDao.findByName("%王%");
  • #{}与${}的区别
    1
    2
    3
    4
    #{}表示一个占位符号:
    #{}可以有效防止 sql 注入,等同于jdbc中的?
    ${}表示拼接 sql 串 :
    如果parameterType 是简单类型,那么只能使用${value}
    在这里插入图片描述
  • 查询总记录数
    1
    2
    3
    4
    5
    6
    7
    int findTotal(); 

    <select id="findTotal" resultType="int">
    select count(*) from user;
    </select>

    int res = userDao.findTotal();
  • 注意事项
    基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式 ,例如:java.lang.String
    实体类类型,目前我们只能使用全限定类名。
    在这里插入图片描述
  • 传递包装类对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class QueryVo implements Serializable { 
    private User user;
    public User getUser() {return user; }
    public void setUser(User user) { this.user = user; }
    }

    List<User> findByVo(QueryVo vo);

    <select id="findByVo" resultType="com.itheima.domain.User"
    parameterType="com.itheima.domain.QueryVo">
    select * from user where username like #{user.username};
    </select>

流程分析

代理Dao方式

  • SELECT
    在这里插入图片描述
  • INSERT UPDATE DELETE
    在这里插入图片描述

Dao实现类方式(了解)

  • 写法
    在这里插入图片描述
  • 过程分析
    在这里插入图片描述

property&column不对应

  1. 使用别名(不推荐)
    1
    2
    3
    4
    <select id="findAll" resultType="com.itheima.domain.User">
    select id as userId,username as userName,birthday as userBirthday,
    sex as userSex,address as userAddress from user
    </select>
  2. resultMap(推荐)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!-- 建立 User 实体和数据库表的对应关系
    type 属性:指定实体类的全限定类名
    id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
    -->
    <resultMap type="com.itheima.domain.User" id="userMap">
    <id column="id" property="userId"/>
    <result column="username" property="userName"/>
    <result column="sex" property="userSex"/>
    <result column="address" property="userAddress"/>
    <result column="birthday" property="userBirthday"/>
    </resultMap>

    id 标签:用于指定主键字段
    result 标签:用于指定非主键字段
    column 属性:用于指定数据库列名
    property 属性:用于指定实体类属性名称

    <!-- 配置查询所有操作【resultMap指向上面的id】 -->
    <select id="findAll" resultMap="userMap">
    select * from user
    </select>

SqlMapConfig.xml

配置文件

配置内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-properties(属性)
--property
-settings(全局配置参数)
--setting
-typeAliases(类型别名)
--typeAliase
--package
-typeHandlers(类型处理器)
-objectFactory(对象工厂)
-plugins(插件)
-environments(环境集合属性对象)
--environment(环境子属性对象)
---transactionManager(事务管理)
---dataSource(数据源)
-mappers(映射器)
--mapper
--package

properties

(属性)

  1. (方式一 了解)直接写配置信息
    1
    2
    3
    4
    5
    6
    <properties>
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/>
    <property name="jdbc.username" value="root"/>
    <property name="jdbc.password" value="1234"/>
    </properties>
  2. (方式二 推荐)把内容写在properties中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    *1*在resource目录下定义db.properties文件
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/eesy
    jdbc.username=root
    jdbc.password=1234

    *2*properties 标签配置
    <!-- 配置properties
    可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息
    resource属性: (推荐)
    用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
    url属性:(了解)
    file指定本地文件,或 URL
    -->
    <!--<properties url="file:///D:/mybatisCRUD/src/main/resources/db.properties"></properties>-->
    <properties resource="db.properties"></properties>

    *3*此时我们的 dataSource 标签就变成了引用上面的配置
    <dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    </dataSource>

typeAliases

(类型别名)

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

mappers

(映射器)

1
2
3
4
5
6
1. xml配置文件方式
<mapper resource="dao/StudentDao.xml"/>
2. 注解方式
<mapper class="tpf.Dao.StudentDao"></mapper>
3. 注解或xml
<package name="tpf.Dao"/>

注意:在编译时,java和resource会合并到一个文件夹。
也就是说java下的tpf.dao目录中的东西会和resource下tpf.dao中的东西合并到一个文件夹中去。
在这里插入图片描述

连接池

  1. 概念
    1
    2
    我们在实际开发中都会使用连接池。
    因为它可以减少我们获取连接所消耗的时间。
    在这里插入图片描述
  2. 实现方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    mybatis连接池提供了3种方式的配置:

    配置的位置:
    主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
    type属性的取值:
    POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
    UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
    JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
    注意:如果不是web或者maven的war工程,是不能使用的。tomcat服务器,采用连接池就是dbcp连接池。
    在这里插入图片描述
  3. 原理
    在这里插入图片描述

事务

在这里插入图片描述

动态SQL

if

1
2
3
4
5
6
7
8
9
10
11
<select id="findByUser" resultType="user" parameterType="user">
select * from user where 1=1
<if test="username!=null and username != '' ">
and username like #{username}
</if>
<if test="address != null">
and address like #{address}
</if>
</select>
注意:<if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。
另外要注意 where 1=1 的作用~!

where

1
2
3
4
5
6
7
8
9
10
11
12
13
为了简化上面 where 1=1 的条件拼装,我们可以采用<where>标签来简化开发。

<select id="findByUser" resultType="user" parameterType="user">
select * from user
<where>
<if test="username!=null and username != '' ">
and username like #{username}
</if>
<if test="address != null">
and address like #{address}
</if>
</where>
</select>

foreach

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
传入多个 id 查询用户信息,用下边两个 sql 实现:
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16)

public class QueryVo implements Serializable {
private List<Integer> ids;
public List<Integer> getIds() {return ids; }
public void setIds(List<Integer> ids) {this.ids = ids; }
}

List<User> findInIds(QueryVo vo);

<select id="findInIds" resultType="user" parameterType="queryvo">
<!-- select * from user where id in (1,2,3,4,5); -->
select * from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="id in ( " close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>

SQL 语句:
select 字段 from user where id in (?)
<foreach>标签用于遍历集合,它的属性:
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符

SQL片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 抽取重复的语句代码片段 --> 
<sql id="defaultSql">
select * from user
</sql>

<!-- 配置查询所有操作 -->
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>

<!-- 根据 id 查询 -->
<select id="findById" resultType="UsEr" parameterType="int">
<include refid="defaultSql"></include>
where id = #{uid}
</select>

多表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
表之间的关系有几种:
一对多
多对一
一对一
多对多
举例:
用户和订单就是一对多
订单和用户就是多对一
一个用户可以下多个订单
多个订单属于同一个用户

人和身份证号就是一对一
一个人只能有一个身份证号
一个身份证号只能属于一个人

老师和学生之间就是多对多
一个学生可以被多个老师教过
一个老师可以交多个学生
特例:
如果拿出每一个订单,他都只能属于一个用户。
所以Mybatis就把多对一看成了一对一。

在这里插入图片描述


2023-09-27 00:09:04 补
在这里插入图片描述

延迟加载

SqlMapConfig.xml中的configuration模块中添加配置:

1
2
3
4
5
<!--配置参数-->
<settings>
<!--开启Mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>

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

缓存

  • 概念
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    什么是缓存
    存在于内存中的临时数据。
    为什么使用缓存
    减少和数据库的交互次数,提高执行效率。
    什么样的数据能使用缓存,什么样的数据不能使用
    适用于缓存:
    经常查询并且不经常改变的。
    数据的正确与否对最终结果影响不大的。
    不适用于缓存:
    经常改变的数据
    数据的正确与否对最终结果影响很大的。
    例如:商品的库存,银行的汇率,股市的牌价。

一级缓存

1
2
3
4
5
6
7
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。

注意:当调用 SqlSession 的修改,添加,删除,commit(),close(),clearCache()就会失效
  • 演示
    在这里插入图片描述

二级缓存

1
2
3
4
5
6
7
8
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml的configuration中配置)
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置)

在这里插入图片描述
在这里插入图片描述
注意:因为保存的是数据,所以要实行对象的序列化implements Serializable

2021-06-25 11:42:38
为什么不用二级缓存? ===> 会有脏数据【具体看下图】
怎么去解决?===> 使用同一个会话即可

在这里插入图片描述

注解实现

CRUD

Create Read Update Delete
在这里插入图片描述

property&column

在这里插入图片描述

多表查询,延迟加载

在这里插入图片描述

二级缓存

在这里插入图片描述