引言:

1.mybatis是一款半自动的ORM框架

2.ORM是对象关系映射

MyBatis的CRUD操作:

查询:

标签:< select id=“” resultType=“” >

id与接口中的相关方法是同名的,表示该方法对应的mapper中的sql语句为上面的!

resultType是查询返回结果的类型!

序号参数绑定:

1
2
3
4
public interface UserDao {
//使用原生参数绑定
public User selectUserByIdAndPwd(Integer id , String pwd);
}
1
2
3
4
5
6
7
8
9
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{arg0} AND password = #{arg1} <!--arg0 arg1 arg2 ...-->
</select>

<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{param1} AND password = #{param2} <!--param1 param2 param3 ...-->
</select>

注解参数绑定:

1
2
3
4
5
6
import org.apache.ibatis.annotations.Param; //引入注解

public interface UserDao {
//使用MyBatis提供的@Param进行参数绑定
public User selectUserByIdAndPwd(@Param("id") Integer id , @Param("pwd") String pwd);
}
1
2
3
4
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{id} AND password = #{pwd} <!-- 使用注解值 @Param("pwd") -->
</select>

Map参数绑定:

1
2
3
4
public interface UserDao {
//添加Map进行参数绑定
public User selectUserByIdAndPwd_map(Map values);
}
1
2
3
4
Map values = new HashMap(); //测试类创建Map
values.put("myId",1); //自定义key,绑定参数
values.put("myPwd","123456");
User user = userDao.selectUserByIdAndPwd_map(values);
1
2
3
4
<select id="selectUserByIdAndPwd_map" resultType="user">
SELECT * FROM t_users
WHERE id = #{myId} AND password = #{myPwd} <!-- 通过key获得value -->
</select>

对象参数绑定:

1
2
3
4
public interface UserDao {
//使用对象属性进行参数绑定
public User selectUserByUserInfo(User user);
}
1
2
3
4
<select id="selectUserByUserInfo" resultType="user">
SELECT * FROM t_users
WHERE id = #{id} AND password = #{password} <!-- #{id}取User对象的id属性值、#{password}同理 -->
</select>

模糊查询:

1
2
3
public interface UserDao {
public List<User> selectUsersByKeyword(@Param("keyword") String keyword);
}
1
2
3
4
5
6
<mapper namespace="com.qf.mybatis.part1.different.UserDao">
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_users
WHERE name LIKE concat('%',#{keyword},'%') <!-- 拼接'%' -->
</select>
</mapper>

删除:

标签:< delete id=“” parameterType=“” >

parameterType:指定sql语句中的参数类型

1
2
3
4
 <delete id="deleteUser" parameterType="int">
DELETE FROM t_users
WHERE id = #{id} <!--只有一个参数时,#{任意书写}-->
</delete>

修改:

标签:< update id=“” parameterType=“” >

1
2
3
4
<update id="updateUser" parameterType="user">
UPDATE t_users SET name=#{name}, password=#{password}, sex=#{sex}, birthday=#{birthday}
WHERE id = #{id} <!--方法参数为对象时,可直接使用#{属性名}进行获取-->
</update>

添加:

标签:< insert id=“” parameterType=“” >

这里的id和mapper相关接口同名,表示其相关方法对应!

1
2
3
<insert id="insertUser" parameterType="user">
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#{birthday},NULL);
</insert>

主键回填:

通过last_insert_id()查询主键:

标签:< selectKey id=“” parameterType=“” order=“AFTER|BEFORE”>

一些情况下,新增一条数据信息,但其主键(id)是数据库自动在数据库生成(自增),而有些业务逻辑的处理是需要要到这个生成的主键(id)。上面的标签就是用来获取这个生成的主键(id);

1
2
3
4
5
6
7
8
9
<insert id="add" parameterType="com.demo.pojo.User">
<!--通过mybatis框架提供的selectKey标签获得自增产生的ID值-->
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
select LAST_INSERT_ID()
</selectKey>
insert into user(code,name,remark,sex)
values
(#{code},#{name},#{remark},#{sex})
</insert>

1.selectKey 会将 SELECT LAST_INSERT_ID()的结果放入到传入的model的主键里面,即获取数据库里自动生成的id。

2.keyProperty:对应的model(这里的model就是User对象,这里表示User对象中的id对应数据库中的id)中的主键的属性名,跟数据库的主键对应。

3.order:AFTER 表示 SELECT LAST_INSERT_ID() 在insert执行之后执行,多用与自增主键;

4.BEFORE 表示 SELECT LAST_INSERT_ID() 在insert执行之前执行,这样的话就拿不到主键了,适合那种主键不是自增的类型
resultType:主键类型;

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<insert id="add" parameterType="com.demo.pojo.User">
<!--通过mybatis框架提供的selectKey标签获得自增产生的ID值;下面标签的意思是将插入后的主键值赋值给model也就是User的id-->
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
select LAST_INSERT_ID()
</selectKey>
insert into user(code,name,remark,sex)
values
(#{code},#{name},#{remark},#{sex})
</insert>

UserServiceImpl:

@Override
public void add(User user) {
//新增
UserDao.add(user);
Integer userId = user.getId();//其查询结果的主键值是赋值给model也就是User的id的!
System.out.println("添加信息的id为:" + userId);

通过uuid()查询主键:

1
2
3
4
create table t_order(
id varchar(32) primary key, # 字符型主键
name varchar(50)
)default charset = utf8;
1
2
3
4
5
class Order{
private String id;
private String name;
//set+get ...
}
1
2
3
4
5
6
7
8
9
10
<mapper namespace="com.qf.mybatis.part1.basic.OrderDao">
<insert id="insertOrder" parameterType="order">
<selectKey keyProperty="id" resultType="String" order="BEFORE"><!-- 插入之前,之所以选择插入之前是因为我们的目的是先生成UUID然后把该uuid作为主键插入到表中 -->
SELECT REPLACE(UUID(),'-','')
<!-- 适用于字符类型主键 ;
SELECT REPLACE(UUID(),'-',''):这是实际的SQL语句。它使用MySQL的UUID()函数生成一个通用唯一标识符(UUID),然后 使用REPLACE()函数将其中的破折号(-)替换为空字符串,从而生成一个没有破折号的字符串类型的主键。-->
</selectKey>
INSERT INTO t_order(id,name) VALUES(#{id},#{name})
</insert>
</mapper>

MyBatis自动(反射 get和set)ORM失效:

MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。

image-20240121190543443

方案一:列的别名:

在SQL中使用 as 为查询字段添加列别名,以匹配属性名。

1
2
3
4
5
6
7
<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
<select id="selectManagerByIdAndPwd" resultType="com.qf.mybatis.part2.orm.Manager">
SELECT mgr_id AS id , mgr_name AS username , mgr_pwd AS password
FROM t_managers
WHERE mgr_id = #{id} AND mgr_pwd = #{pwd}
</select>
</mapper>

方案二:结果映射(ResultMap - 查询结果的封装规则):

通过< resultMap id=“” type=“” >映射,匹配列名与属性名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
<!--定义resultMap标签-->
<resultMap id="managerResultMap" type="com.qf.mybatis.part2.orm.Manager">
<!--关联主键与列名-->
<id property="id" column="mgr_id" />
<!--关联属性与列名-->
<result property="username" column="mgr_name" />
<result property="password" column="mgr_pwd" />
</resultMap>
<!--使用resultMap作为ORM映射依据-->
<select id="selectAllManagers" resultMap="managerResultMap">
SELECT mgr_id , mgr_name , mgr_pwd
FROM t_managers
</select>
</mapper>

MyBatis处理关联关系-多表连接:

使用对象关系查询:

OneToOne:

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
<mapper namespace="com.qf.mybatis.part2.one2one.PassengerDao">

<!-- 结果映射(查询结果的封装规则) -->
<resultMap id="passengerResultMap" type="com.qf.mybatis.part2.one2one.Passenger">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="sex" column="sex" />
<result property="birthday" column="birthday" />

<!-- 关系表中数据的封装规则 --> <!-- 指定关系表的实体类型 -->
<association property="passport" javaType="com.qf.mybatis.part2.one2one.Passport">
<id property="id" column="passport_id" />
<result property="nationality" column="nationality" />
<result property="expire" column="expire" />
<result property="passenger_id" column="passenger_id" />
</association>
</resultMap>

<!-- 多表连接查询 --> <!-- 结果映射(查询结果的封装规则)-->
<select id="selectPassengerById" resultMap="passengerResultMap">
<!-- 别名(避免与p1.id冲突) -->
SELECT p1.id , p1.name , p1.sex , p1.birthday , p2.id as passport_id , p2.nationality , p2.expire , p2.passenger_id
FROM t_passengers p1 LEFT JOIN t_passports p2
ON p1.id = p2.passenger_id
WHERE p1.id = #{id}
</select>
</mapper>
  • 注意:指定“一方”关系时(对象),使用< association javaType=“” >

OneToMany:

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
<mapper namespace="com.qf.mybatis.part2.one2many.DepartmentDao">

<!-- 封装规则 -->
<resultMap id="departmentResultMap" type="com.qf.mybatis.part2.one2many.Department">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="location" column="location" />

<!-- 关系表中数据的封装规则 --> <!-- 指定关系表的实体类型 -->
<collection property="emps" ofType="com.qf.mybatis.part2.one2many.Employee">
<id property="id" column="emp_id" />
<result property="name" column="emp_name" />
<result property="salary" column="salary" />
<result property="dept_id" column="dept_id" />
</collection>
</resultMap>
<!-- 多表连接查询 --> <!-- 封装规则 -->
<select id="selectDepartmentById" resultMap="departmentResultMap" >
<!-- 别名(避免与d.id、d.name冲突)-->
SELECT d.id , d.name , d.location , e.id AS emp_id , e.name emp_name , e.salary , e.dept_id
FROM t_departments d LEFT JOIN t_employees e
ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
</mapper>
  • 注意:指定“多方”关系时(集合),使用< collection ofType=“” >

关系总结:

动态SQL:

< sql >:

1
2
3
4
5
6
7
8
9
10
<mapper namespace="com.qf.mybatis.part2.dynamic.BookDao">
<sql id="BOOKS_FIELD"> <!-- 定义SQL片段 -->
SELECT id,name,author,publish,sort
</sql>

<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
<include refid="BOOKS_FIELD" /> <!-- 通过ID引用SQL片段 -->
FROM t_books
</select>
</mapper>

< if >:

1
2
3
4
5
6
7
8
9
10
<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
<include refid="BOOKS_FIELD" /> <!-- 通过ID引用SQL片段 -->
FROM t_books
<if test="name!=null">
name=#{name}
</if>
<if test="author!=null">
and author=#{author}
</if>
</select>

< where >:

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
<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
SELECT id , name , author , publish , sort
FROM t_books
<where>
<if test="id != null"> <!-- WHERE,会自动忽略前后缀(如:and | or) -->
id = #{id}
</if>

<if test="name != null">
and name = #{name}
</if>

<if test="author != null">
and author = #{author}
</if>

<if test="publish != null">
and publish = #{publish}
</if>

<if test="sort != null">
and sort = #{sort}
</if>
</where>
</select>

< set >:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<update id="updateBookByCondition">
UPDATE t_books
<set>
<if test="name != null"><!-- where子句中满足条件的if,会自动忽略后缀(如:,) -->
name = #{name} ,
</if>

<if test="author != null">
author = #{author} ,
</if>

<if test="publish != null">
publish = #{publish} ,
</if>

<if test="sort != null">
sort = #{sort} ,
</if>
</set>
WHERE id = #{id}
</update>

< trim >:

< trim prefix=“” suffix=“” prefixOverrides=“” suffixOverrides=“” >代替< where > 、< set >

prefix:表示该字段的前缀; suffix:表示要去除多余的前缀; suffixOverrides:表示要去除多余的后缀;

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
<select id="selectBookByCondition" resultType="com.qf.mybatis.day2.dynamic.Book">
SELECT id,name,author,publish,sort
FROM t_books
<trim prefix="WHERE" prefixOverrides="AND|OR"> <!-- 增加WHERE前缀,自动忽略前缀 -->
<if test="id != null">
and id = #{id}
</if>

<if test="name != null">
and name = #{name}
</if>

<if test="author != null">
and author = #{author}
</if>

<if test="publish != null">
and publish = #{publish}
</if>

<if test="sort != null">
and sort = #{sort}
</if>
</trim>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<update id="updateBookByCondition">
UPDATE t_books
<trim prefix="SET" suffixOverrides=","> <!-- 增加SET前缀,自动忽略后缀 -->
<if test="name != null">
name = #{name} ,
</if>

<if test="author != null">
author = #{author} ,
</if>

<if test="publish != null">
publish = #{publish} ,
</if>

<if test="sort != null">
sort = #{sort}
</if>
</trim>
WHERE id = #{id}
</update>

< foreach >:

1
2
3
4
5
6
7
<delete id="deleteBookByIds">
DELETE FROM t_books
WHERE id IN
<foreach collection="list" open="(" separator="," close=")" item="id" index="i">
#{id}
</foreach>
</delete>
参数 描述 取值
collection 容器类型 list、array、map
open 起始符 (
close 结束符 )
separator 分隔符 ,
index 下标号 从0开始,依次递增
item 当前项 任意名称(循环中通过 #{任意名称} 表达式访问)

缓存(Cache):

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

一级缓存:

SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。

  • 注意:无需任何配置,默认开启一级缓存。

二级缓存:

SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。

  • 注意:在sqlSession.commit()或者sqlSession.close()之后生效。
  • 二级缓存:没有默认开启,需要手动开启,其存储的范围是Mapper NameSpace。即,其范围是Mapper映射器的某个命名空间

开启全局缓存:

< settings >是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。

1
2
3
4
5
6
7
8
9
10
<configuration>
<properties .../>

<!-- 注意书写位置 -->
<settings>
<setting name="cacheEnabled" value="true"/> <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
</settings>

<typeAliases></typeAliases>
</configuration>

八股文:

**#{}${}**的区别是什么?:

#{}是预编译处理,${}是字符串替换。使用#{}可以有效的防止SQL注入,提高系统安全性。

模糊查询like语句该怎么写**?**:

1.让传递的参数为模糊处理过的:

image-20240121205328569

2.在mapper查询的时候使用函数:

1
2
3
4
5
6
<mapper namespace="com.qf.mybatis.part1.different.UserDao">
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_users
WHERE name LIKE concat('%',#{keyword},'%') <!-- 拼接'%' -->
</select>
</mapper>

分页插件:

分页插件针对ResultSet结果集执行的内存分页,而非物理分 页。分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截 待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

延迟加载:

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加 载lazyLoadingEnabled=true|false。

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如 调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接 着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

延迟加载:在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫按需查询(懒加载)

立即加载:不管用不用,只要一调用方法,马上发起查询。

II