Mybatis3 (2)xml映射文件

SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

  • cache – 给定命名空间的缓存配置。
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

select

查询

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
select>

下面就是 insert,update 和 delete 语句的示例

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
delete>

在插入语句里面有一些额外的属性和子元素用来处理主键的生成,有多种生成方式。

如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
insert>

对不支持自动生成主键的JDBC驱动,Mybatis有另外一种生成主键。

<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 元素将会首先运行,Author 的 id 会被设置,然后插入语句会被调用。

sql

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password sql>

这个元素可以被用来定义重用的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 属性里或include内部语句中${prefix}Table

参数(Parameters)

<insert id="insertUser" parameterType="User">
  insert into users (id, username, password)
  values (#{id}, #{username}, #{password})
insert>

如果User类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找。

大多时候你只须简单地指定属性名,其他的事情 MyBatis 会自己去推断,顶多要为可能为空的列指定 jdbcType。

#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}

更多选项设置:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Parameters

字符串替换

默认情况,使用 #{} 会导致PreparedStatement参数创建,并安全设置参数。首选!
有时想插入一个不转义的字符串,如:

ORDER BY ${columnName}

注意:用这种方式不安全,可能导致sql注入,要么不允许用户输入这些字段,要么自行转义验证。

Result Maps

resultMap 元素是 MyBatis 中最重要最强大的元素。用来支持程序使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)作为领域模型。

例如下面配置映射到User实体中

<select id="selectUsers" resultType="com.someapp.model.User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
select>

类型别名可以帮助你不用输入类的完全限定名


<typeAlias type="com.someapp.model.User" alias="User"/>


<select id="selectUsers" resultType="User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
select>

Mybatis会创建一个ResultMap,映射到javaBean中。如果没有匹配上,可以再select语句中使用别名

<select id="selectUsers" resultType="User">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password     as "hashedPassword"
  from some_table
  where id = #{id}
select>

下面是另一种方式来实现映射。

<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" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
select>

总结:resultMap可以用来解决数据库列名与属性没有匹配的问题。如果足够了解它,就不需要显示配置它。

高级结果映射


<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>

解析:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps

构造方法

public class User {
   //...
   public User(Integer id, String username, int age) {
     //...
  }
//...
}

将结果注入构造方法,Mybatis需要通过某种方式定位相应构造方法。constructor 元素就是为此而生的。

<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>

如果类中存在名称和类型相同的属性,那么可以省略 javaType 。

关联

<association property="author" column="blog_author_id" javaType="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
association>

关联的嵌套查询

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
select>

我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。

这种方式很简单,但是对于大型数据集合和列表表现不好,原因是我们熟悉的“N+1 查询问题”

  • 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
  • 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

Mybatis能延时加载是好事,但是加载完后立即调用所有延迟加载,就很糟糕。

有另外一种方法。

关联的嵌套结果

<select id="selectBlog" resultMap="blogResult">
  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
  from Blog B left outer join Author A on B.author_id = A.id
  where B.id = #{id}
select>

注意这个联合查询, 使用唯一而且清晰的名字来重命名。 使映射非常简单。

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/>
resultMap>

<resultMap id="authorResult" type="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"/>
resultMap>

在上面,authorResult 用来加载作者实例。现在,上面的示例用了外部的结果映射元素来映射关联。这使得 Author 结果映射可以 重用

非常重要: id元素在嵌套结果映射中扮演着非 常重要的角色。 不指定唯一标志属性,会有严重的性能问题。

如果不需要映射到单独的结果映射中,也可以嵌套结果映射。

<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <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"/>
  association>
resultMap>

集合

<collection property="posts" ofType="domain.blog.Post">
  <id property="id" column="post_id"/>
  <result property="subject" column="post_subject"/>
  <result property="body" column="post_body"/>
collection>

和关联相同,不同处:

private List<Post> posts;

集合的嵌套查询

首先,让我们看看使用嵌套查询来为博客加载文章。

<resultMap id="blogResult" type="Blog">
  <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
select>

<select id="selectPostsForBlog" resultType="Post">
  SELECT * FROM POST WHERE BLOG_ID = #{id}
select>

关注这段

<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>

读作:“在Post类型的ArrayList中的posts的集合”。

集合的嵌套结果

至此,你可以猜测集合的嵌套结果是如何来工作的,因为它和关联完全相同,除了它应 用了一个“ofType”属性
详细介绍:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps
你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。

鉴别器

  <discriminator javaType="int" column="draft">
     <case value="1" resultType="DraftPost"/>
   discriminator>

有时一个单独的数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集。 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结构。和switch语句类似。

自动映射

如果数据库字段名和javaBean属性名不一致,要将 mapUnderscoreToCamelCase设置为true

例如:

<select id="selectUsers" resultMap="userResultMap">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password
  from some_table
  where id = #{id}
select>
<resultMap id="userResultMap" type="User">
  <result property="password" column="hashed_password"/>
resultMap>

上面的例子中,id和userName和javaBean中属性一致,被自动映射了。而hashed_password在resultMap中映射了字段和属性,所以在结果中也能取到值。

有三种自动映射等级:

  • NONE - 禁用自动映射。仅设置手动映射属性。
  • PARTIAL(默认) - 将自动映射结果除了那些有内部定义内嵌结果映射的(joins).
  • FULL - 自动映射所有。

缓存

一级缓存是SQLSession级别的缓存,换出的数据只在SQLSession内有效。
二级缓存是mapper级别的缓存,同一个namespace公用这个缓存,所以对SQLSession是共享的

一级缓存

1.第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来

2.第二次执行select会从缓存中查数据,如果select相同切传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率

注意事项:

  • 如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前SqlSession缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读
  • 当一个SqlSession结束后那么他里面的一级缓存也就不存在了,mybatis默认是开启一级缓存,不需要配置
  • mybatis的缓存是基于[namespace:sql语句:参数]来进行缓存的,意思就是,SqlSession的HashMap存储缓存数据时,是使用[namespace:sql:参数]作为key,查询返回的语句作为value保存的。例如:-1242243203:1146242777:winclpt.bean.userMapper.getUser:0:2147483647:select * from user where id=?:19

二级缓存

二级缓存是mapper级别的缓存。也就是同一个namespace的mapper.xml,当多个SQLSession使用同一个mapper操作数据库,得到的数据会缓存在同一个二级缓存中。

具体流程:
  1.当一个sqlseesion执行了一次select后,在关闭此session的时候,会将查询结果缓存到二级缓存
  2.当另一个sqlsession执行select时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能

注意事项:
   如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前mapper缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读。

开启二级缓存:
confg.xml

<settings>
        <setting name="cacheEnabled" value="true"/>默认是false:关闭二级缓存
<settings>

userMapper.xml

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> 当前mapper下所有语句开启二级缓存

配置了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,返回的对象是只读。
若想禁用某select语句的二级缓存,添加userCache="false"

<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">

可用的收回策略有:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

使用自定义缓存

你可能感兴趣的:(mybatis,Mybatis)