一、介绍
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的持久层框架。MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJO映射成数据库中的记录。
Mybatis的入门程序与配置可以参考:http://www.mybatis.org/mybatis-3/getting-started.html。
每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来获得。SqlSessionFactoryBuilder对象可以通过XML配置文件,或从以往使用惯例中准备好的 Configuration 类实例中来构建 SqlSessionFactory对象。
范围和生命周期
- SqlSessionFactoryBuilder:当创建了SqlSessionFactory后,这个类就不需要存在了,它的最佳范围是方法范围(也就是本地方法变量)。
- SqlSessionFactory:一旦被创建,SqlSessionFactory 实例应该在应用程序执行期间都存在,没有理由处理或重新创建它。因此,SqlSessionFactory的最佳实践是在程序运行期间不要重复创建多次,最佳范围是应用范围。可以考虑使用单例模式或者依赖注入容器。
- SqlSession:每个线程都应该有它自己的 SqlSession 实例,它是线程不安全的。因此最佳的范围是请求或方法范围,确保使用finally块来关闭它。
- Mapper:Mapper(映射器)是创建绑定映射语句的接口,可以从 SqlSession 中获得。Mapper的最宽范围和SqlSession是相同的,但它的最佳范围是方法范围(不需要明确地关闭)。
二、常用语法
select
示例:
<select id=”selectPerson” parameterType=”int” resultType=”hashmap”>
SELECT * FROM PERSON WHERE ID = #{id}
</select>
这个语句名称是selectPerson,使用一个int( 或Integer)类型的参数,并返回一个HashMap 类型的对象,其中的键是列名,值是列对应的值。
注意:使用 #{} 格式的语法会让MyBatis创建PreparedStatement(预处理语句)来处理参数;而使用 ${} 直接在 SQL 语句中插入一个不改变的字符串,例如:ORDER BY ${columnName}。过多使用 ${} 是不安全的,会导致潜在的 SQL 注入攻击。
select语句的可选属性:
| 属性 | 描述 |
|---|---|
| id | 在命名空间中唯一的标识符,可以被用来引用这条语句 |
| parameterType | 将会传入这条语句的参数类的完全限定名或别名 |
| resultType | 从这条语句中返回的期望类型的类的完全限定名或别名 |
| resultMap | 命名引用外部的 resultMap。resultMap 和 resultType 不能同时使用 |
| flushCache | 将其设置为 true,无论语句什么时候被调用,都会导致缓存被清空。默认值:false |
| useCache | 将其设置为 true,将会导致本条语句的结果被缓存。默认值:true。 |
| timeout | 这个设置驱动程序等待数据库返回请求结果,并抛出异常时间的最大等待值。默认不设置。 |
| fetchSize | 这是暗示驱动程序每次批量返回的结果行数。默认不设置 |
insert,update,delete
示例:
<insert id="insertAuthor" parameterType="domain.blog.Author">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor" parameterType="domain.blog.Author">
update Author set username = #{username}, password = #{password}, email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor” parameterType="int">
delete from Author where id = #{id}
</delete>
自增主键的处理方式:
-
对于支持自动生成主键的数据库(如MySQL),可以设置 useGeneratedKeys=”true”,并设置 keyProperty 到目标字段上。例如:
<insert id="insertAuthor" parameterType="domain.blog.Author" useGeneratedKeys=”true” keyProperty=”id”> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert> -
对于不支持主键自增的情况,可以使用selectKey:
<insert id="insertAuthor" parameterType="domain.blog.Author"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR} ) </insert>
sql代码段
示例:
<sql id=”userColumns”> id,username,password </sql>
这个 SQL 片段可以被包含在其他语句中,例如:
<select id=”selectUsers” parameterType=”int” resultType=”hashmap”>
select <include refid=”userColumns”/> from some_table
where id = #{id}
</select>
resultMap
示例:
<select id=”selectUsers” parameterType=”int” resultType=”com.someapp.model.User”>
select user_id as “id”, user_name as “userName”, hashed_password as “hashedPassword”
from some_table
where id = #{id} </select>
MyBatis 会在幕后自动创建一个 ResultMap,基于属性名来映射列到JavaBean 的属性上。如果列名没有精确匹配,你可以在列名上使用 select 字句的别名来匹配标签。
或者使用外部resultMap:
<!-- 在XML配置文件中-->
<typeAlias type=”com.someapp.model.User” alias=”User”/>
<!-- 在SQL映射的XML文件中-->
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/> <result property="password" column="hashed_password"/>
</resultMap>
引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
<select id=”selectUsers” parameterType=”int” resultMap=”userResultMap”>
select user_id, user_name, hashed_password from some_table
where id = #{id}
</select>
resultMap结构的其他属性:
- constructor:类在实例化时,用来注入结果到构造方法中
- association:类型关联
- collection:复杂类型的集
- discriminator:使用结果值来决定使用哪个结果映射,类似于switch
动态SQL
MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。
-
if
<select id=”findBlog” parameterType=”Blog” resultType=”Blog”> SELECT * FROM BLOG WHERE state = "ACTIV" <if test=”title != null”> AND title like #{title} </if> </select> -
choose(when,otherwise)
<select id=”findActiveBlogLike” parameterType=”Blog” 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 title like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select> -
trim(where,set)
<select id=”findActiveBlogLike” parameterType=”Blog” 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 title like #{author.name} </if> </where> </select>
大部分情况下where都能满足需求。如果where没有做出你想要的,可以用trim来自定义,例如和where元素相等的trim元素是:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
-
foreach
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
三、Mybatis缓存
MyBatis将数据缓存设计成两级结构,分为一级缓存、二级缓存:
Mybatis缓存
MyBatis 默认开启了一级缓存,一级缓存是在SqlSession 层面进行缓存的。即同一个SqlSession ,多次调用同一个Mapper和同一个方法的同一个参数,只会进行一次数据库查询,然后把数据缓存到缓冲中,以后直接先从缓存中取出数据,不会直接去查数据库。
二级缓存是Application应用级别的缓存,它的是生命周期很长,跟Application的声明周期一样,也就是说它的作用范围是整个Application应用。
要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:<cache/>
这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如 no Flush Interval,没有刷新间隔),缓存不会以任何时间顺序来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用 。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
所有的这些属性都可以通过缓存元素的属性来修改。比如:
<cache eviction = "FIFO" flushInterval="60000" size="512" readOnly="true"/>
除了这些自定义缓存的方式,你也可以通过实现你自己的缓存或为其他第三方缓存方案创建适配器来完全覆盖缓存行为,只要实现 org.mybatis.cache.Cache 接口即可。
参考资料:
[1] http://www.mybatis.org/mybatis-3/
[2] https://www.cnblogs.com/moongeek/p/7689683.html











网友评论