MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL映射文件顶级元素
cache
该命名空间的缓存配置。
cache-ref
引用其他命名空间的缓存配置。
resultMap
描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap
老式风格的参数映射。停用
sql
可被其他语句引用的可重用语句块。
insert
映射插入语句
update
映射更新语句
delete
映射删除语句
select
映射查询语句
select
1 2 3 4 5 6 7 8 9 10 11 12 13 select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
属性说明
insert, update 和 delete
数据变更语句 insert,update 和 delete 的实现非常接近:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20"> <update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> <delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
** insert、update、delete属性介绍**
插入主键
数据库支持自动生成主键,使用useGeneratedKeys=true,keyProperty=“id”
1 2 3 4 5 6 7 8 9 10 11 12 13 <insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert> //----多行插入 <insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username, password, email, bio) values <foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio}) </foreach> </insert>
selectKey生成主键
1 2 3 4 5 6 7 8 9 10 //生成一个随机主键,仅示例,不建议使用。 <insert id="insertAuthor"> <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>
selectKey
1 2 3 4 5 <selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
** selectKey属性**
Sql
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:
1 2 3 4 5 6 7 8 9 <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql> //----使用 <select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2 </select>
也可以在 include 元素的 refid 属性或内部语句中使用属性值,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <sql id="sometable"> ${prefix}Table </sql> <sql id="someinclude"> from <include refid="${include_target}"/> </sql> <select id="select" resultType="map"> select field1, field2, field3 <include refid="someinclude"> <property name="prefix" value="Some"/> <property name="include_target" value="sometable"/> </include> </select>
参数
基本使用
1 2 3 //直接被替换为占位符 "?",与${}不同。 #{property} #{property,javaType=int,jdbcType=NUMERIC}
增加类型处理器
1 #{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
小数点后位数
1 #{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
mode 属性,存储过程相关属性。
1 2 mode=IN|OUT|INOUT #{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
** 字符串替换**
1 2 3 4 ${name} ,直接替换为 对应的字符串,而不是占位符 "?" //示例 @Select("select * from user where ${column} = #{value}") User findByColumn(@Param("column") String column, @Param("value") String value);
提示:直接将用户输入内容进行${input}替换存在sql注入的安全问题。
结果映射
简单的映射基本不需要resultMap,直接使用map、pojo就行。
复杂的sql 使用resultMap也会变得非常简单。
resultMap概念图
constructor -用于在实例化类时,注入结果到构造方法中
idArg -ID参数
* arg - 将被注入到构造方法中的一个普通结果
id - 一个ID结果;
result - 注入到字段或javabean属性的普通结果
association - 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 - 关联可以使resultMap元素,或其他结果映射的引用
collection - 一个复杂类型集合
嵌套结果映射 - 可以使resultMap或其它引用
discriminator - 使用结果值来决定使用那个resultMap
case - 基于某些值的结果映射
* 嵌套结果映射 -case 也是一个结果映射;或其它resultmap及引用
resultMap属性
id
当前命名空间中的一个唯一标识,用于标识一个结果映射。
type
类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。
autoMapping
如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。
id&result
1 2 <id property="id" column="post_id"/> <result property="subject" column="post_subject"/>
两个元素共有的属性:
构造方法
构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。 constructor 元素就是为此而生的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class User { public User(Integer id, String username, int age) { } } ----- //必须按照参数循序写 <constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> <arg column="age" javaType="_int"/> </constructor> //3.4.3+ //可以在指定参数名称的前提下,以任意顺序编写 arg 元素 //构造器需要对参数添加 @param注解 <constructor> <idArg column="id" javaType="int" name="id" /> <arg column="age" javaType="_int" name="age" /> <arg column="username" javaType="String" name="username" /> </constructor>
构造方法的属性
关联
关联(association)元素处理“有一个”类型的关系。
如何加载关联:
嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
关联方法的属性
1 2 3 4 <association property="author" column="blog_author_id" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </association>
关联的多结果集(ResultSet)
多结果集,主查询返回多条数据。
集合
解决1对多的问题 ,与 关联(association)相似。
1 2 3 4 //其中ofType是集合泛型类型 <collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/> //-----或者---- <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
鉴别器(discriminator)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultMap="carResult"/> <case value="2" resultMap="truckResult"/> <case value="3" resultMap="vanResult"/> <case value="4" resultMap="suvResult"/> </discriminator> </resultMap>
完整示例
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 28 29 30 31 32 33 34 35 <!-- 非常复杂的语句 --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select>
上面sql对应resultMap
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 28 29 <!-- 非常复杂的结果映射 --> <resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap>
自动映射
忽略大小写
驼峰支持,设置mapUnderscoreToCamelCase=true
三种自动映射等级:
NONE
禁止自动映射。仅对手动映射的属性进行映射。
PARTIAL
默认状态,对除在内部定义了嵌套结果映射(连接)以外的属性进行映射
FULL
自动映射所有属性
缓存
默认只启动本地会话缓存,只对一个会话中的数据进行缓存。
二级缓存
二级缓存功能
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
启用方式
全局二级缓存需要在SQL映射文件中添加一行
配置参数
1 2 3 4 5 6 7 8 9 //创建了一个 FIFO 缓存 //每隔 60 秒刷新 //最多可以存储结果对象或列表的 512 个引用 //而且返回的对象被认为是只读的 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
清楚策略(eviction)
LRU - 最近最少使用,移除最长时间不被使用的对象;
FIFO - 先进先出,按对象进入缓存的顺序移除他们
SOFT - 软应用,基于垃圾回收器状态和软引用规则移除对象
WEAK - 弱引用,更积极的基于垃圾收集器状态和弱引用规则移除对象。
默认策略为:LRU
**刷新间隔(flushInterval):**正整数,单位:毫秒,默认:unset 不刷新
**引用数目(size):**缓存对象个数,正整数,默认值1024
**只读(readOnly):**true时返回对象是共享的同一个实例,不能被修改。false时通过序列化返回对象的拷贝实例。速度慢、但安全。默认是false
**提示:**二级缓存是事务性的,意思是:当sqlsession完成并提交时或回滚时,但并没有执行flushCache=true的insert/delete/update 语句时,缓存会获得更新。
自定义缓存
首先自定义缓存不支持上面的配置属性
实现 org.apache.ibatis.cache.Cache 接口
提供一个接受 String 参数作为 id 的构造器。
cache 配置type属性指向自定义缓存类
支持property进行配置,需要实现name对应的public属性
设置完所有属性后悔指向org.apache.ibatis.builder.InitializingObject 接口下面的initialize方法。需要的话可以实现该方法;
1 2 3 <cache type="com.domain.something.MyCustomCache"> <property name="file" value="/a/b/c.tmp"> </cache>
默认缓存策略会绑定命名空间,如果需要可以独立配置缓存;
各种语句默认缓存策略:
1 2 3 4 <select ... flushCache="false" useCache="true"/> <insert ... flushCache="true"/> <update ... flushCache="true"/> <delete ... flushCache="true"/>
cache-ref
可以通过引用来共享同一个缓存配置
1 <cache-ref namespace="com.someone.application.data.SomeMapper"/>