1.Mybatis核心流程
- 每个基于Mybatis的应用都是一个SqlSessionFactory的实例为核心
- SqlSessionFactory的实例可以通过SqlSessionFactoryBulider获得
- SqlSessionFactoryBuilder可以从XML配置文件或一个预先配置的Configuration实例来构建SqlSessionFactory实例
- 工厂设计模式里面获取SqlSession,里面提供了在数据库执行SQL命令所需的所有方法。
2.Mybatis3.x快速入门
2.1 添加Maven依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- 使⽤用JDBC链接mysql的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependency>
2.2 配置mybatis-config.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">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/xdclass?
useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/FistMapper.xml"/>
</mappers>
</configuration>
2.3 配置FistMapper.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="net.lcj.dao.FistMapper">
<select id="selectById"resultType="net.lcj.domain.Video">
select * from video where id = #{video_id}
</select>
</mapper>
- 注:获取java实体中的值时,属性名大小写要一致,并且推荐使用#{value},${value}存在sql注入风险
2.4 通过sqlSession测试一下
public static void main(String [] args) throws IOException {
String resouce = "config/mybatis-config.xml";
//读取配置⽂文件
InputStream inputStream = Resources.getResourceAsStream(resouce);
//构建Session⼯工⼚厂
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
//获取Session
try(SqlSession sqlSession = sqlSessionFactory.openSession()){
VideoMapper videoMapper =
sqlSession.getMapper(VideoMapper.class);
Video video = videoMapper.selectById(44);
//System.out.println(video.toString());
List<Video> videoList = videoMapper.selectList();
System.out.println(videoList.toString());
}
}
2.5 Mybatis使用流程总结一下
- 创建mybatis-config.xml全局的配置文件
- 创建XXXMaper.xml配置文件
- 创建SqlSessionFactory
- 用SqlSessionFactory创建SqlSession对象
- 用SqlSession.getMapper获取Mapper接口进行crud操作。
3.MyBatis3.X玩转查询和新增操作
3.1 查询操作及单个参数别名使用
单个参数别名可以这样写
Video selectById(@Param("video_id") int videoId);
多个参数模糊查询
例:根据评分和标题模糊查询 注:参数记得取别名,方便识别和使用
Mapper接口如下:
List<Video> selectByPointAndTitleLike(@Param("point") double point,@Param("title" String title);
Xml实现如下:
<select id="selectByPointAndTitleLike" resultType ="net.lcj.domain.Video">
select * from video where point =#{point} and title like concat('%',#{title},'%')
</select>
3.2 数据库字段是下划线,java属性是驼峰映射方法
在mybatis-config.xml文件中加配置
<!--下划线⾃自动映射驼峰字段-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
取参数值,具体某个字段的类型时可以加上jdbcType = VARCHAR
- 常见的数据库类型和java列席对比
3.3 Mybatis实战插入语法新增和自增主键
例:新增一条记录
<insert id="add" parameterType="net.xdclass.online_class.domain.Video">
INSERT INTO `video` ( `title`, `summary`, `cover_img`, `price`,`create_time`, `point`)
VALUES
(#{title,jdbcType=VARCHAR},#{summary,jdbcType=VARCHAR},#{coverImg,jdbcType=VARCHAR},#{price,jdbcType=INTEGER},#{createTime,jdbcType=TIMESTAMP},#{point,jdbcType=DOUBLE});
</insert>
例:获取插入的自增主键
<insert id="add" parameterType="net.xdclass.online_class.domain.Video"
useGeneratedKeys="true" keyProperty="id" keyColumn="id" >
3.4 Mybatis的foreach批量插入语法
- foreach:用于凭借的内置标签
foreach包含属性:
collection:值为要迭代循环的集合类型,入参是List时,属性值为list,入参是Map时,collection属性为map的key值
item:每一个元素进行迭代时的别名
index:索引的属性名,在集合数组情况下值为当前索引值
open:整个循环内容的开头字符串
close:整个循环内容的结尾字符串
separator:每次循环的分隔符
例:Mapper接口:
/**
*批量插入
*/
int addBatch(List<Video> list);
<!--批量量插⼊入-->
<insert id="addBatch"
parameterType="net.xdclass.online_class.domain.Video"
useGeneratedKeys="true" keyProperty="id" keyColumn="id" >
INSERT INTO `video` ( `title`, `summary`, `cover_img`, `price`,`create_time`, `point`)
VALUES
<foreach collection="list" item="video" separator=",">
(#{video.title,jdbcType=VARCHAR},#{video.summary,jdbcType=VARCHAR},
#{video.coverImg,jdbcType=VARCHAR},#{video.price,jdbcType=INTEGER},
#{video.createTime,jdbcType=TIMESTAMP},#{video.point,jdbcType=DOUBLE})
</foreach>
</insert>
4 Mybatis3.X更新和删除操作
4.1更新操作1
更新一条记录的接口:
int updateVideo(Video video);
更新xml文件方法:此方法会将不涉及的字段更新为null或者默认值
<update id="updateVideo" parameterType="net.xdclass.online_class.domain.Video">
UPDATE video
set
title = #{title,jdbcType=VARCHAR},
summary = #{summary,jdbcType=VARCHAR},
cover_img = #{coverImg,jdbcType=VARCHAR},
price = #{price,jdbcType=INTEGER},
c_id = #{cId,jdbcType=INTEGER},
point = #{point,jdbcType=INTEGER},
learn_base = #{learnBase,jdbcType=VARCHAR},
learn_result = #{learnResult,jdbcType=VARCHAR},
total_episode = #{totalEpisode,jdbcType=INTEGER},
update_time = now()
WHERE
id = #{id}
</update>
4.2 Mybatis3.X动态字段更新之选择性更新标签
4.2.1 if test标签介绍
- if标签可以判断传入的值来确定查询条件,test指定的是一个OGNL表达式
- 常见写法
//当前字段符合条件才更更新这个字段的值
<if test='title != null and id == 87 '> title = #{title}, </if>
<if test="title!=null"> title = #{title}, </if>
更新的xml:
<update id="updateVideoSelective"
parameterType="net.xdclass.online_class.domain.Video">
update video
<trim 前缀+:prefix="set" 后缀-: suffixOverrides=",">
<if test="title != null "> title = #{title,jdbcType=VARCHAR},
</if>
<if test="summary != null "> summary = #
{summary,jdbcType=VARCHAR},</if>
<if test="coverImg != null "> cover_img = #
{coverImg,jdbcType=VARCHAR},</if>
<if test="price != 0 "> price = #{price,jdbcType=INTEGER},
</if>
<if test="createTime !=null "> create_time = #
{createTime,jdbcType=TIMESTAMP},</if>
<!-- 特别注意: ⼀一定要看pojo类⾥里里⾯面的是基本数据类型,还是包装数据类型,只有包装类型的字段才有判断-->
<if test="point != null "> point = #{point,jdbcType=DOUBLE},
</if>
</trim>
where
id = #{id}
</update>
4.3 Mybatis3.X删除语法和转义字符使用
- 需求:删除某个时间段之后 且金融大于10元的数据
<delete id="deleteByCreateTimeAndPrice" parameterType="java.util.Map">
delete from video where create_time <![CDATA[ > ]]> #{createTime}
and price <![CDATA[ >= ]]> #{price}
</delete
4.3.1为什么要使用转义字符(sql和Mybatis的xml冲突)
- 大于等于 <![CDATA[ >= ]]>
- 小于等于 <![CDATA[ <= ]]>
5 Mybatis3.X的复杂sql查询
5.1 Mybatis3.X的sql片段使用
<sql id="base_video_field">
id,title,summary,cover_img
</sql>
<select id="selectById" parameterType="java.lang.Integer" resultType="Video">
select <include refid="base_video_field"/> from video where
id = # {video_id,jdbcType=INTEGER}
</select>
<select id="selectListByXML" resultType="Video">
select <include refid="base_video_field"/> from video
</select>
5.2 Mybatis的resultMap详解
5.2.1 resultMap适合场景
- 需要自定义字段,或者多表查询,一对多等关系,比resultType更强大
- 适合复杂查询
自定义字段查询
<resultMap id="VideoResultMap" type="Video">
<!--
id 指定查询列列的唯⼀一标示
column 数据库字段的名称
property pojo类的名称
-->
<id column="id" property="id" jdbcType="INTEGER" />
<result column="video_tile" property="title" jdbcType="VARCHAR" />
<result column="summary" property="summary" jdbcType="VARCHAR" />
<result column="cover_img" property="coverImg" jdbcType="VARCHAR" />
</resultMap>
<select id="selectBaseFieldByIdWithResultMap" resultMap="VideoResultMap">
select id , title as video_tile, summary, cover_img from video where id = #
{video_id}
</select>
5.2.2 resultMap复杂结果一对一查询结果映射association
- association:映射到POJO的某个复杂类型属性,比如订单order对象里面包含user对象
例:xml如下:
<resultMap id="VideoOrderResultMap" type="VideoOrder">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="out_trade_no" property="outTradeNo"/>
<result column="create_time" property="createTime"/>
<result column="state" property="state"/>
<result column="total_fee" property="totalFee"/>
<result column="video_id" property="videoId"/>
<result column="video_title" property="videoTitle"/>
<result column="video_img" property="videoImg"/>
<!--
association 配置属性⼀一对⼀一
property 对应videoOrder⾥里里⾯面的user属性名
javaType 这个属性的类型
-->
<association property="user" javaType="User">
<id property="id" column="user_id"/>
<result property="name" column="name"/>
<result property="headImg" column="head_img"/>
<result property="createTime" column="create_time"/>
<result property="phone" column="phone"/>
</association>
</resultMap>
<!--⼀一对⼀一管理理查询订单, 订单内部包含⽤用户属性-->
<select id="queryVideoOrderList" resultMap="VideoOrderResultMap">
select
o.id id,
o.user_id ,
o.out_trade_no,
o.create_time,
o.state,
o.total_fee,
o.video_id,
o.video_title,
o.video_img,
u.name,
u.head_img,
u.create_time,
u.phone
from video_order o left join user u on o.user_id = u.id
</select>
5.2.2 resultMap复杂结果一对多查询结果映射collection
- collection:一对多查询结果映射,比如user有多个订单
例:xml如下:
<resultMap id="UserOrderResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="headImg" column="head_img"/>
<result property="createTime" column="create_time"/>
<result property="phone" column="phone"/>
<!--
property 填写pojo类中集合类属性的名称
ofType 集合⾥里里⾯面的pojo对象
-->
<collection property="videoOrderList" ofType="VideoOrder">
<!--配置主键,管理理order的唯⼀一标识-->
<id column="order_id" property="id"/>
<result column="user_id" property="userId"/>
<result column="out_trade_no" property="outTradeNo"/>
<result column="create_time" property="createTime"/>
<result column="state" property="state"/>
<result column="total_fee" property="totalFee"/>
<result column="video_id" property="videoId"/>
<result column="video_title" property="videoTitle"/>
<result column="video_img" property="videoImg"/>
</collection>
</resultMap>
<select id="queryUserOrder" resultMap="UserOrderResultMap">
select
u.id,
u.name,
u.head_img,
u.create_time,
u.phone,
o.id order_id,
o.out_trade_no,
o.user_id,
o.create_time,
o.state,
o.total_fee,
o.video_id,
o.video_title,
o.video_img
from user u left join video_order o on u.id = o.user_id
</select>
5.3 ResultMap的复杂对象查询总结
- association 映射的是⼀一个pojo类,处理理⼀一对⼀一的关联关系
- collection 映射的⼀一个集合列列表,处理理的是⼀一对多的关联关系
- 模板
<!-- column不不做限制,可以为任意表的字段,⽽而property须为type 定义的pojo属性-->
<resultMap id="唯⼀一的标识" type="映射的pojo对象">
<id column="表的主键字段,或查询语句句中的别名字段" jdbcType="字段类型" property="映射
pojo对象的主键属性" />
<result column="表的⼀一个字段" jdbcType="字段类型" property="映射到pojo对象的⼀一个属
性"/>
<association property="pojo的⼀一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo
对象的属性"/>
<result column="表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property 需要为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性名称" ofType="集合中单个的pojo对象类型">
<id column="集合中pojo对象对应在表的主键字段" jdbcType="字段类型" property="集合
中pojo对象的主键属性" />
<result column="任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>
6 Mybatis3.X多级缓存和懒加载
6.1 一级缓存
- 什么是缓存:程序经常要调⽤用的对象存在内存中,方便其使⽤用时可以快速调⽤用,不不必去数据库或者其他持久化设备中查询,主要就是提⾼高性能
- Mybatis一级缓存:⼀一级缓存的作⽤用域是SQLSession,同⼀一个SqlSession中执⾏行行相同的SQL查询(相同的SQL和参数),第一次会去查询数据库并写在缓存中,第二次会直接从缓存中取,基于PerpetualCache 的 HashMap本地缓存,默认开启⼀一级缓存。
- 失效策略:执⾏行行SQL时候两次查询中间发⽣生了了增删改的操作,即insert、 update、 delete等操作commit后会清空该SQLSession缓存; ⽐比如sqlsession关闭,或者清空等
6.2 二级缓存
- 简介:二级缓存是namespace级别的,多个SqlSession去操作同⼀一个namespace下的Mapper的sql语句句,多个SqlSession可以共⽤用二级缓存,如果两个mapper的namespace相 同,(即使是两个mapper,那么这两个mapper中执⾏行行sql查询到的数据也将存在相同的二级缓存区域中,但是最后是每个Mapper单独的名称空间),默认是没有开启⼆二级缓存,操作流程:第⼀一次调⽤用某个namespace下的SQL去查询信息,查询到的信息会存放该mapper对应的二级缓存区域。第二次调⽤用同个namespace下的mapper映射⽂文件中,相同的sql去查询信息,会去对应的二级缓存内取结果
- 失效策略略:执⾏行行同个namespace下的mapepr映射⽂文件中增删改sql,并执⾏行行了了commit操作,会清空该⼆二级缓存
- 注意:实现二级缓存的时候, MyBatis建议返回的POJO是可序列列化的, 也就是建议实Serializable接口
- 缓存淘汰策略略:会使⽤用默认的 LRU 算法来收回(最近最少使⽤用的)
- 如何开启某个⼆二级缓存 mapper.xml⾥里里⾯面配置
<!--开启mapper的namespace下的⼆二级缓存-->
<!--
eviction:代表的是缓存回收策略略,常⻅见下⾯面两种。
(1) LRU,最近最少使⽤用的,⼀一处最⻓长时间不不⽤用的对象
(2) FIFO,先进先出,按对象进⼊入缓存的顺序来移除他们
flushInterval:刷新间隔时间,单位为毫秒,这⾥里里配置的是100秒刷新,如果不不配置
它,当SQL被执⾏行行的时候才会去刷新缓存。
size:引⽤用数⽬目,代表缓存最多可以存储多少个对象,设置过⼤大会导致内存溢出
readOnly:只读,缓存数据只能读取⽽而不不能修改,默认值是false
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
全局配置:
<settings>
<!--这个配置使全局的映射器器(⼆二级缓存)启⽤用或禁⽤用缓存,全局总开关,这⾥里里关闭, mapper中开启
了了也没⽤用-->
<setting name="cacheEnabled" value="true" />
</settings>
- 一级缓存和二级缓存使⽤用顺序
- 优先查询二级缓存-》查询一级缓存-》数据库
- 某个方法不使用二级缓存
<select id="selectById" parameterType="java.lang.Integer"
resultType="Video" useCache="false">
select <include refid="base_video_field"/> from video where id = #
{video_id,jdbcType=INTEGER}
</select>
6.3 Mybatis懒加载
- 什么是懒加载:按需加载,先从单表查询,需要时再从关联表去关联查询,能⼤大提高数据库性能,并不是所有场景下使用懒加载都能提高效率
- Mybatis懒加载: resultMap里⾯的association、 collection有延迟加载功能
在Mybatis-config.xml中添加配置
<!--全局参数设置-->
<settings>
<!--延迟加载总开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将aggressiveLazyLoading设置为false表示按需加载,默认为true-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<resultMap id="VideoOrderResultMapLazy" type="VideoOrder">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="out_trade_no" property="outTradeNo"/>
<result column="create_time" property="createTime"/>
<result column="state" property="state"/>
<result column="total_fee" property="totalFee"/>
<result column="video_id" property="videoId"/>
<result column="video_title" property="videoTitle"/>
<result column="video_img" property="videoImg"/>
<!--
select: 指定延迟加载需要执⾏行行的statement id
column: 和select查询关联的字段
-->
<association property="user" javaType="User" column="user_id"
select="findUserByUserId"/>
</resultMap>
<!--⼀一对⼀一管理理查询订单, 订单内部包含⽤用户属性 懒加载-->
<select id="queryVideoOrderListLazy" resultMap="VideoOrderResultMapLazy">
select
o.id id,
o.user_id ,
o.out_trade_no,
o.create_time,
o.state,
o.total_fee,
o.video_id,
o.video_title,
o.video_img
from video_order o
</select>
<select id="findUserByUserId" resultType="User">
select * from user where id=#{id}
</select>