一 添加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>
</dependencies>
二 配置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="xdclass.net"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/VideoMapper.xml"/>
</mappers>
</configuration>
三 配置VideoMapper.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.xdclass.online_class.dao.VideoMapper">
<select id="selectById" resultType="net.xdclass.online_class.domain.Video">
select * from video where id = #{video_id}
</select>
</mapper>
获取参数中的值
- 注意 :取java对象的某个值,属性名大小写要一致
#{value} : 推荐使用, 是java的名称 ${value} : 不推荐使用,存在sql注入风险
四 编写代码 获取 SqlSession,以xml方式读取数据库
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());
}
}
五 Mybatis使用流程
-
创建mybatis-config.xml 全局的配置文件
-
创建XXXMapper.xml配置文件
-
创建SqlSessionFactory
-
用SqlSessionFactory创建SqlSession对象
-
用SqlSession执行增删改查CRUD
内置的日志工厂提供日志功能, 使用log4j配置打印sql,添加依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
在应用的classpath中创建名称为log4j.properties
的文件
log4j.rootLogger=ERROR, stdout
log4j.logger.net.xdclass=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
-
mysql自带函数使用
<select id="selectByPointAndTitleLike" resultType="net.xdclass.online_class.domain.Video"> select * from video where point=#{point} and title like concat('%', #{title},'%') </select>
新增一条视频记录
<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" >
六 Mybatis foreach批量插入语法的使用
-
批量插入多条视频记录
-
foreach: 用于循环拼接的内置标签,常用于 批量新增、in查询等常见
包含以下属性: collection:必填,值为要迭代循环的集合类型,情况有多种 入参是List类型的时候,collection属性值为list 入参是Map类型的时候,collection 属性值为map的key值 item:每一个元素进行迭代时的别名 index:索引的属性名,在集合数组情况下值为当前索引值,当迭代对象是map时,这个值是map的key open:整个循环内容的开头字符串 close:整个循环内容的结尾字符串 separator: 每次循环的分隔符
例子
<!--批量插入--> <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>
七 更新操作
<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>
if test标签介绍
-
if 标签可以通过判断传入的值来确定查询条件,test 指定一个OGNL表达式
-
常见写法
//当前字段符合条件才更新这个字段的值 <if test='title != null and id == 87 '> title = #{title}, </if> <if test="title!=null"> title = #{title}, </if>
delete删除语法
- 需求:删除某个时间段之后 且金额大于 10元的数据
<delete id="deleteByCreateTimeAndPrice" parameterType="java.util.Map"> delete from video where create_time <![CDATA[ > ]]> #{createTime} and price <![CDATA[ >= ]]> #{price} </delete>
大于等于 <![CDATA[ >= ]]> 小于等于 <![CDATA[ <= ]]>
八 Mybatis的mybatis-config.xml常见配置
核心配置文件(dom节点顺序要求,不然报错)
- 记住常用的,不常用的简单介绍,
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件,少用) environments(环境配置,不配多环境,基本在Spring里面配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
九 Mybatis的查询类别名typeAlias的使用
-
typeAlias
- 类型别名,给类取个别名,可以不用输入类的全限定名
<!--<select id="selectById" parameterType="java.lang.Integer" resultType="net.xdclass.online_class.domain.Video">--> <select id="selectById" parameterType="java.lang.Integer" resultType="Video"> select * from video where id = #{video_id,jdbcType=INTEGER} </select>
如果有很多类,是否需要一个个配置?
- 不用一个个配置,使用包扫描即可
<typeAliases> <!--<typeAlias type="net.xdclass.online_class.domain.Video" alias="Video"/>--> <package name="net.xdclass.online_class.domain"/> </typeAliases>
-
本身就内置很多别名,比如Integer、String、List、Map 等
- 类型别名,给类取个别名,可以不用输入类的全限定名
十 Sql片段使用
什么是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>
十一 Mybatis的resultMap讲解
-
Mybatis的SQL语句返回结果有两种
-
resultType
- 查询出的字段在相应的pojo中必须有和它相同的字段对应,或者基本数据类型
- 适合简单查询
-
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>
-
十二 ResultMap的association一对一查询
- association: 映射到POJO的某个复杂类型属性,比如订单order对象里面包含 user对象
<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>
十三 ResultMap的collection一对多查询
- collection: 一对多查询结果查询映射,比如user有多个订单
<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>
十四 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>
十五 Mybatis一级缓存
-
什么是缓存
- 程序经常要调用的对象存在内存中,方便其使用时可以快速调用,不必去数据库或者其他持久化设备中查询,主要就是提高性能
-
Mybatis一级缓存
- 简介:一级缓存的作用域是SQLSession,同一个SqlSession中执行相同的SQL查询(相同的SQL和参数),第一次会去查询数据库并写在缓存中,第二次会直接从缓存中取
- 基于PerpetualCache 的 HashMap本地缓存
- 默认开启一级缓存
-
失效策略:当执行SQL时候两次查询中间发生了增删改的操作,即insert、update、delete等操作commit后会清空该SQLSession缓存; 比如sqlsession关闭,或者清空等
十六 Mybatis二级缓存
-
Mybatis二级缓存
- 简介:二级缓存是namespace级别的,多个SqlSession去操作同一个namespace下的Mapper的sql语句,多个SqlSession可以共用二级缓存,如果两个mapper的namespace相同,(即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中,但是最后是每个Mapper单独的名称空间)
- 基于PerpetualCache 的 HashMap本地缓存,可自定义存储源,如 Ehcache/Redis等
- 默认是没有开启二级缓存
- 操作流程:第一次调用某个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>
-
配置实操
-
如果需要控制全局mapper里面某个方法不使用缓存,可以配置 useCache="false"
<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>
-
一级缓存和二级缓存使用顺序
- 优先查询二级缓存-》查询一级缓存-》数据库
十七 Mybatis3.x的懒加载
- 什么是懒加载: 按需加载,先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能,并不是所有场景下使用懒加载都能提高效率
- Mybatis懒加载: resultMap里面的association、collection有延迟加载功能
<!--全局参数设置--> <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>
- dubug模式测试懒加载不准确,可以直接run
// resultmap association关联查询(测试懒加载) VideoOrderMapper videoOrderMapper = sqlSession.getMapper(VideoOrderMapper.class); List<VideoOrder> videoOrderList = videoOrderMapper.queryVideoOrderListLazy(); System.out.println(videoOrderList.size()); //课程里面演示是6条订单记录,但是只查询3次用户信息,是因为部分用户信息走了一级缓存sqlsession for(VideoOrder videoOrder : videoOrderList){ System.out.println(videoOrder.getVideoTitle()); System.out.println(videoOrder.getUser().getName()); }
十八 整合Mysql数据库事务
-
使用JDBC的事务管理
- 使用 java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())
-
使用MANAGED的事务管理
- MyBatis自身不会去实现事务管理,而让程序的容器如(Spring, JBOSS)来实现对事务的管理
-
<environment id="development"> <!-- mybatis使用jdbc事务管理方式 --> <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="xdclass.net"/> </dataSource> </environment>
事务工厂TransactionFactory 的两个实现类
- JdbcTransactionFactory->JdbcTransaction
- ManagedTransactionFactory->ManagedTransaction
- 注意:如果不是web程序,然后使用的事务管理形式是MANAGED, 那么将没有事务管理功能
十九 Mysql的Innodb和MyISAM引擎的区别
区别项 | Innodb | myisam |
---|---|---|
事务 | 支持 | 不支持 |
锁粒度 | 行锁,适合高并发 | 表锁,不适合高并发 |
是否默认 | 默认 | 非默认 |
支持外键 | 支持外键 | 不支持 |
适合场景 | 读写均衡,写大于读场景,需要事务 | 读多写少场景,不需要事务 |
全文索引 | 可以通过插件实现, 更多使用ElasticSearch | 支持全文索引 |