核心概念
mybatis的DAO接口是通过XXMapper.xml文件实现的,文件名与接口名相同,一个常见的mapper如下:
<mapper namespace="com.souche.repayment.dao.mapper.CalculationBaseDataDOMapper">
<resultMap id="BaseResultMap" type="com.souche.repayment.bean.entity.CalculationBaseDataDO">
<!--
WARNING - @mbggenerated
-->
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="order_no" property="orderNo" jdbcType="VARCHAR"/>
<result column="term_no" property="termNo" jdbcType="INTEGER"/>
<result column="version" property="version" jdbcType="INTEGER"/>
<result column="version_date" property="versionDate" jdbcType="DATE"/>
</resultMap>
<sql id="Base_Column_List">
<!--
WARNING - @mbggenerated
-->
id, order_no, term_no, version, version_date, base_term_nom_prin, base_term_nom_int,
base_term_ovd_prin, base_term_ovd_int, base_extension_amt, base_extension_var, gmt_create,
gmt_update
</sql>
<select id="selectMaxVersionByOrderNo" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM tb_calculation_base_data
where order_no = #{orderNo,jdbcType=VARCHAR}
and(term_no,`version`) in
(select
term_no,max(`version`)
from tb_calculation_base_data
where
order_no=#{orderNo,jdbcType=VARCHAR}
group by term_no)
</select>
<insert id="insertList" parameterType="java.util.List">
INSERT INTO
tb_calculation_base_data
(order_no, term_no, version, version_date, base_term_nom_prin, base_term_nom_int,
base_term_ovd_prin, base_term_ovd_int, base_extension_amt, base_extension_var, gmt_create,
gmt_update)
VALUES
<foreach collection="calculationBaseDataDOList" index="index" item="item" separator=",">
( #{item.orderNo}, #{item.termNo}, #{item.version},
#{item.versionDate}, #{item.baseTermNomPrin},#{item.baseTermNomInt}, #{item.baseTermOvdPrin},
#{item.baseTermOvdInt}, #{item.baseExtensionAmt},#{item.baseExtensionVar},now(), now())
</foreach>
</insert>
<select id="selectListByOrderNo" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM tb_calculation_base_data
WHERE order_no=#{orderNo,jdbcType=VARCHAR}
</select>
</mapper>
- resultMap
结果集映射,将数据库字段映射到java bean,需要制定jdbcType - 常用标签
select,update,delete,insert
sql: 定义常用的sql片段
include:引用sql片段 selectKey:为不支持自增主键的数据库提供主键生成策略,仅对update和insert有效
cache:开启二级缓存
还有很多标签用于动态sql - 参数传递
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#
{password},#{email},#{bio})
</insert>
- 动态sql
1)if
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
2) choose, when, otherwise
类似于java的switch-case
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
3) where, set, trim
where标签是为了解决当where语句中第一个if没有匹配到的话会以and|or开始,sql语法错误。set是为了解决update..set最后可能多一个逗号的情况。trim更加强大可以覆盖上面两种情况。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
前面两种情况都可以用trim搞定
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
4) foreach
动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
- 缓存
默认开启一级缓存,也就是基于sqlsession的缓存,mybatis每次执行sql会先用sqlsessionFactory(单例)创建一个sqlsession实例(每个线程一个),而每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件(Configuration.xml)构建出 SqlSessionFactory 的实例。
sqlSession可以直接面向数据库执行sql
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
说了这个多就是想说以及缓存是sqlSession层面的,多个sqlSession不共用缓存。
mybatis也可以开启二级缓存,是基于mapper层面的,如果该mapper开启了二级缓存,那多个sqlSession用这个mapper时共享缓存,开启方式就是加入标签。
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
单条语句比如select想开启的话,可以设置useCache=true
<select ... flushCache="false" useCache="true"/>
核心实体
juejin.cn/post/684490…
statement
sqlsession
sqlsessionFactory
mapper
plugin
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
注意,如果 localCacheScope 被设置为 SESSION,对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用。对返回的对象(例如 list)做出的任何修改将会影响本地缓存的内容,进而将会影响到在本次 session 中从缓存返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。
面试问题
1.mybatis日志怎么开启?
在配置文件configuration.xml中设置logImpl属性,开启全局log
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
</configuration>
logImpl 可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING
也可以对某个mapper的某个方法开启日志,假设用log4j,具体配置如下
引用jar包,然后创建一个log4j.properties文件
# 全局
log4j.rootLogger=ERROR, stdout
# 接口级别
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 方法级别
log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
2.mybatis执行流程
- 加载配置文件并初始化(SqlSession)
配置文件来源于两个地方,一个是配置文件(主配置文件conf.xml,mapper文件*.xml),一个是java代码中的注释,将sql的配置信息加载成为一个mappedstatement对象,存储在内存之中(包括传入参数的映射配置,结果映射配置,执行的sql语句)。
- 接收调用请求
调用mybatis提供的api,传入的参数为sql的id(有namespase和具体sql的id组成)和sql语句的参数对象,mybatis将调用请求交给请求处理层。
- 处理请求
根据sql的id找到对应的mappedstatament对象。
根据传入参数解析mappedstatement对象,得到最终要执行的sql。
获取数据库连接,执行sql,得到执行结果。
Mappedstatement对象中的结果映射对执行结果进行转换处理,并得到最终的处理结果。
释放连接资源。
- 返回处理结果
具体可以查看 www.cnblogs.com/dongying/p/…
juejin.cn/post/698763…
3. mybatis的缓存了解吗?
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,要加一个cache标签。
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
其他面试题看 github.com/whysoseriou…