myBatis-04 Mapper文件

1、标签和属性

1.1、Mapper内部的顶级标签

  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • sql – 可被其它语句引用的可重用语句块。
  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。

1.2、数据变更

insert,update,delete三个标签用法基本差不错,比较简单。

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

相关属性:

属性描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

批量新增的用法

如果你的数据库还支持多行插入, 你也可以传入一个 Author 数组或集合,并返回自动生成的主键。

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

1.3、数据查询

select标签,用于完成数据的查询,普通的查询比较简单。

复杂的用法:动态Sql,与resultMap结合

<selectKey
  keyProperty="id"
  resultType="int" / resultMap="" 
  order="BEFORE"
  statementType="PREPARED">

相关属性:

属性描述
keyProperty selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。
resultType 结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。
resultMap 用来引用外部结果集配置,完成查询字段与Java对象属性的映射,与resultType不能同时使用。
order 可以设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType 和前面一样,MyBatis 支持 STATEMENTPREPARED 和 CALLABLE 类型的映射语句,分别代表 StatementPreparedStatement 和 CallableStatement 类型。

1.4 sql代码片段

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:

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

1.5 cache缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

。。。

1.6 cache-ref

。。。

2、输入参数

Mapper操作内会包含参数,参数定义方式:

1)#{参数名},主要用于字段值位置。

2)${参数名},主要用于字符串替换,当替换参数值时有注入的风险。

参数值传入支持简单类型也支持复杂类型,如果只有一个参数,可以不需要通过parameterType指定参数类型。

参数名后边可以指定其它的数据类型声明,例如:

常用的类型:javaType,jdbcType,typeHandler,numericScale,mode,resultMap

jdbcType的取值必须是org.apache.ibatis.type.JdbcType枚举类型中的某一个类型。

typeHandler是数据值处理器,用来完成数据的二次处理。

numericScale如果是数值类型,可以通过该属性指定保留小数的位数。

mode 属性允许你指定 INOUT 或 INOUT 参数,如果是存储过程的参数才需要。

resultMap 用来将结果集 ResultMap 映射到参数的类型上。

#{isDelete,javaType=java.lang.String,jdbcType=BIT,typeHandler=db.TypeHandles.isDeleteTypeHandle}
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}

简单类型方法:

<select id="selectUsers" resultType="User">
  select id, username, password
  from users
  where id = #{id}
</select>

负责类型用法:如果 User 类型的参数对象传递到了语句中,会查找 id、username 和 password 属性,然后将它们的值传入预处理语句的参数中。

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

参数替换的应用举例:

按照任意的列进行查询

@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

按照任意列进行排序

ORDER BY ${columnName} 

3、输出映射

resultType,用来指定输出的具体类型,查询列名需要和类型的字段名保持统一,字段名和属性不对应是要使用别名方式。

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

myBatis会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上

resultMap,显示的指定字段和属性的映射关系

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

4、动态Sql

 动态 SQL 是 MyBatis 的强大特性之一。

4.1、if

判断表达式是否成立,成立则包含代码块内的语句

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

 这里的test属性值应该是合法的Java代码,参数内部包含的属性可以直接使用。

4.2、choose、when、otherwise

多重判断,类似于switch语句。

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

4.3、where

当通过if拼接where查询条件的时候,需要在前边增加一个1=1的语句前缀,如果通过where标签,则可以省略掉。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
        AND 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>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

4.4、set

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

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

4.5、foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  <where>
    <foreach item="item" index="index" collection="list"
        open="ID in (" separator="," close=")" nullable="true">
          #{item}
    </foreach>
  </where>
</select>

foreach元素的属性主要有item,index,collection,open,separator,close。

  • item:集合中元素迭代时的别名,该参数为必选。
  • index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选
  • open:foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选
  • separator:元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
  • close: foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
  • collection: 要做foreach的对象,作为入参时,List对象默认用"list"代替作为键,数组对象有"array"代替作为键,Map对象没有默认的键。当然在作为入参时可以使用@Param("keyName")来设置键,设置keyName后,list,array将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:如果User有属性List ids。入参是User对象,那么这个collection = "ids".如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = "ids.id"

在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况: 

  • 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list .
  • 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array .
  • 如果传入的参数时多个的时候,可以封装在一个类内部,通过类内部属性来包含集合对象。
  • 如果传入的参数是多个的时候,也可以将参数封装成一个Map了,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key.

使用例子1 - 单参数List:

<select id="countByUserList" resultType="_int" parameterType="list">
select count(*) from users
  <where>
    id in
    <foreach item="item" collection="list" separator="," open="(" close=")" index="">
      #{item.id, jdbcType=NUMERIC}
    </foreach>
  </where>
</select>c

测试代码:

@Test
  public void shouldHandleComplexNullItem() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
      Mapper mapper = sqlSession.getMapper(Mapper.class);
      User user1 = new User();
      user1.setId(2);
      user1.setName("User2");
      List<User> users = new ArrayList<User>();
      users.add(user1);
      users.add(null);
      int count = mapper.countByUserList(users);
      Assert.assertEquals(1, count);
    } finally {
      sqlSession.close();
    }
  }

使用例子2 - 单参数Array数组:

<select id="dynamicForeach2Test" resultType="Blog">
     select * from t_blog where id in
     <foreach collection="array" index="index" item="item" open="(" separator="," close=")">
          #{item}
     </foreach>
</select>c

测试代码:

@Test
 public void dynamicForeach2Test() {
         SqlSession session = Util.getSqlSessionFactory().openSession();
         BlogMapper blogMapper = session.getMapper(BlogMapper.class);
         int[] ids = new int[] {1,3,6,9};
         List blogs = blogMapper.dynamicForeach2Test(ids);
         for (Blog blog : blogs)
         System.out.println(blog);
         session.close();
 }

使用例子3 - 自己把参数封装成Map的类型:

<select id="dynamicForeach3Test" resultType="Blog">
         select * from t_blog where title like "%"#{title}"%" and id in
          <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
               #{item}
          </foreach>
 </select>

测试代码:

@Test
    public void dynamicForeach3Test() {
        SqlSession session = Util.getSqlSessionFactory().openSession();
         BlogMapper blogMapper = session.getMapper(BlogMapper.class);
          final List ids = new ArrayList();
          ids.add(1);
          ids.add(2);
          ids.add(3);
          ids.add(6);
         ids.add(7);
         ids.add(9);
        Map params = new HashMap();
         params.put("ids", ids);
         params.put("title", "中国");
        List blogs = blogMapper.dynamicForeach3Test(params);
         for (Blog blog : blogs)
             System.out.println(blog);
         session.close();
     }

4.6、bind

完成模糊查询。

bind元素,可以通过OGNL表达式创建一个上下文变量,在模块查询中可以使用。

    <select id="getEmpsTestInnerParameter" resultType="com.hand.mybatis.bean.Employee">
        <!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
        SELECT * FROM emp
        <where>
            <!--eName是emp表中一个属性值-->
            <bind name="bindName" value="'%'+eName+'%'"/>
            <if test="_parameter!=null">
                ename like #{bindName}
            </if>
        </where>
    </select>

4.7、多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。比如下面的例子:

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>

 

posted @ 2022-02-09 23:15  草莓爸  阅读(77)  评论(0编辑  收藏  举报