Mybatis(二)mapper映射文件之标签

一、基本介绍

mapper.xml文件是mybatis中的主要模块,它可以灵活的配置你需要执行的sql

我们先来看一下一些常见的配置规则

映射文件是以<mapper>作为根节点:<mapper namespace=" ">:一个namespace作用于一个dao文件

在根节点中支持9个元素,分别为:insert、update、delete、select(增删改查);cache、cache-ref、resultMap、parameterMap、sql

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!-- 头文件 -->
 
<mapper namespace="com.baihe.dao.UserDao">
<!-- 核心文件  namespace  命名空间 dao文件的路径 -->
 
    <cache eviction="" type=""  blocking="" flushInterval=" " readOnly="" size="" ></cache>
    <!-- 给定命名空间的缓存配置 -->
        <!--eviction- (回收策略)-->
            <!--LRU – 最近最少使用的Least Recently Used:移除最长时间不被使用的对象。(默认的属性)-->
            <!--FIFO – 先进先出First in First out:按对象进入缓存的顺序来移除它们。-->
            <!--SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。-->
            <!--WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。-->
        <!--flushInterval-(刷新间隔)-->
            <!--可以被设置为任意的正整数,而且它们代表一个合理的 毫 秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。-->
        <!--size- (引用数目)-->
            <!--可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。-->
        <!--readOnly(只读)-->
            <!--可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会
                返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。-->
        <!--type-  cache实现类-->
            <!--默认为PERPETUAL,可以使用自定义的cache实现类(别名或完整类名皆可)-->
 
    <cache-ref namespace="" ></cache-ref>
    <!-- 从其他命名空间引用缓存配置。-->
        <!-- 如果你不想定义自己的cache,可以使用cache-ref引用别的cache。-->
        <!-- 因为每个cache都以namespace为id-->
        <!-- 所以cache-ref只需要配置一个namespace属性就可以了。-->
        <!-- 需要注意的是,如果cache-ref和cache都配置了,以cache为准。-->
 
    <sql id="result" databaseId="" lang="">
        id,
        name
    </sql>
    <!-- 可重复使用的查询集-->
    <!-- 配合  <include refid="result"/> 使用 -->
 
    <resultMap id="" type="" autoMapping=" " extends="">
        <!-- 主键字段 -->
        <id property="" column="" javaType="" jdbcType="" typeHandler=""></id>
        <!-- 普通字段 -->
        <result property="" column=""></result>
        <!-- 构造函数-->
        <constructor></constructor>
        <!-- 配置一对一的关联关系 -->
        <association property="" >
            <id></id>
            <result></result>
        </association>
        <!--配置一对多的关联关系-->
        <collection property="" >
            <id></id>
            <result></result>
 
        </collection>
        <!-- -->
        <discriminator javaType="">
            <case value=""></case>
        </discriminator>
    </resultMap>
    <!-- 复杂的结果处理 -->
 
    <parameterMap id="" type="">
        <parameter property="" javaType=""></parameter>
    </parameterMap>
    <!-- 复杂的参数 -->
 
    <select id="" parameterType="" parameterMap="" resultType="" resultMap="" databaseId="" useCache="true" lang="" fetchSize="" flushCache="true" resultOrdered="true" resultSets=" " resultSetType="  " statementType=" " timeout="">
        SELECT
          <include refid="selectResult"/>
        FROM
          user
        LIMIT #{pageStart},#{rows}
    </select>
    <!-- 查询语句 -->
    <!--id- dao文件的方法名 -->
    <!--parameterType或parameterMap- 参数类型 -->
    <!--resultType或resultMap 返回值类型 -->
    <!-- -->
    <!--useCache- 是否使用缓存 -->
 
    <insert id="" parameterMap="" useGeneratedKeys="true" timeout="" statementType=" " flushCache=" " lang="" databaseId="" parameterType="" keyProperty="" keyColumn=" " >
        <selectKey keyProperty="" order="BEFORE" resultType="">
        </selectKey>
        INSERT INTO user
        (<include refid="selectResult"/>)
        VALUES
        ( #{id},
        #{name},
        #{password},
        #{age},
        #{sex})
    </insert>
    <!--插入语句 -->
    <!-- keyColumn="id" keyProperty="id" useGeneratedKeys="true"  插入后可返回自增id-->
 
    <delete id="" parameterType="" databaseId="" lang="" flushCache="true" statementType=" " timeout="" parameterMap="">
        DELETE FROM
          user
        WHERE
          id=#{id}
    </delete>
    <!-- 删除语句-->
 
    <update id="" parameterMap="" timeout="" statementType=" " flushCache=" " lang="" databaseId="" parameterType="" keyColumn="" keyProperty="" useGeneratedKeys="true" >
        UPDATE
            app
        SET
            name=#{name},
            password=#{password},
            age=#{age},
            sex=#{sex}
        WHERE
            id=#{id}
    </update>
    <!--修改语句 -->
 
    <delete id="delete">
        DELETE FROM
          user
        where
          id IN
        <foreach item="ids" index="index" collection="array" open="(" separator="," close=")">
          #{ids}
        </foreach>
    </delete>
</mapper>

常用标签一览

数据操作定义标签
insert映射添加语句
update映射修改语句
delete映射删除语句
select映射查询语句
selectKey

为了解决Insert数据时不支持主键自动生成的问题,他可以很随意的设置生成主键的方式(不建议使用)

数据类型标签
parameterMap设定入参的数据格式
resultMap设定出参的数据格式(最复杂,最有力量的元素,用来描述如何从数据库结果集中加载你的对象)

association

适用于表的连接查询一对一关系

collection

适用于表的一对多连接查询

动态sql标签
sql可以重用的sql代码块
include插入预先定义的<sql>标签语句
if通过判断参数值来决定是否使用某个数据进行拼接
foreach经常用于遍历集合构建in条件语句或者批量操作语句
choose、when、otherwise有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis提供了choose 元素,按顺序判断when中的条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行 otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default
格式化输出
where只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除
set类似的用于动态更新语句的解决方案叫做 setset 元素可以用于动态包含需要更新的列,而舍去其它的
trim一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作
  
  
case 
bind 

 

二、#{}和${}的使用

#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符?:
对于 : INSERT INTO user (name) VALUES (#{name}); ==> INSERT INTO user (name) VALUES (?);

${} 符号的作用是直接进行字符串替换:
对于 : INSERT INTO user (name) VALUES ('${name}'); ==> INSERT INTO user (name) VALUES ('tianshuzhi');

 

三、数据操作定义标签

1、Insert、update、delete

我们看一下.dtd文件显示的所有属性和可包含的标签
element : 表示里面可以包含的标签
attlist : 表示标签所包含的属性

<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

<!ELEMENT update (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

<!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

案例 :

<?xml version="1.0" encoding="UTF-8" ?>   
<!DOCTYPE mapper   
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd"> 
 
<!-- mapper 为根元素节点, 一个namespace对应一个dao -->
<mapper namespace="com.dy.dao.UserDao">
    <insert
      id="addUser"
      parameterType="user"
      flushCache="true"
      statementType="PREPARED"
      keyProperty=""
      keyColumn=""
      useGeneratedKeys="false"
      timeout="20"></insert>
    <update
      id="updateUser"
      parameterType="user"
      flushCache="true"
      statementType="PREPARED"
      timeout="20"></upsate>
    <delete
      id="deleteUser"
      parameterType="user"
      flushCache="true"
      statementType="PREPARED"
      timeout="20"></delete>
</mapper>
属性是否必须 
idyesid是命名空间中的唯一标识符,可被用来代表这条语句。 
一个命名空间(namespace) 对应一个dao接口, 
这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致
parameterType(推荐使用)no

(parameterType / ParameterMap 二选一配置)

默认为mybatis自动选择处理

将要传入语句的参数的完全限定类名或别名, 如果不配置,mybatis会通过ParameterHandler 根据参数类型默认选择合适的typeHandler进行处理
parameterType 主要指定参数类型,可以是int, short, long, string等类型,也可以是复杂类型(如对象)

ParameterMapno

(parameterType / ParameterMap 二选一配置)

 

flushCacheno

默认配置为true

将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)

statementTypeno

默认值:PREPARED

STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,

keyPropertyno

默认为unset

(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值

如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表

keyColumnno

(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。

如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表

useGeneratedKeysno

默认为false

(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段)

timeoutno

默认为unset, (依赖驱动)

这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数

上面给出了一个比较全面的配置说明,但是在实际使用过程中并不需要都进行配置,可根据自己的需要删除部分配置项。

<?xml version="1.0" encoding="UTF-8" ?>   
<!DOCTYPE mapper   
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.majing.learning.mybatis.dao.UserDao">

	<insert id="addUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
		insert into user(name,password,age) values(#{name},#{password},#{age})
	</insert>
	
	<delete id="deleteUser" parameterType="int">
		delete from user where id = #{id}
	</delete>
	
	<update id="updateUser" parameterType="user" >
		update user set name = #{name}, password = #{password}, age = #{age} where id = #{id}
	</update>

</mapper>

这里的parameterType设置成user是因为如果不设置的情况下,会自动将类名首字母小写后的名称,原来的类名为User。不过,建议大家还是使用typeAlias进行配置吧。唯一需要说明的就是<insert>元素里面的useGeneratedKeys和keyProperties属性,这两个属性是用来获取数据库中的主键的。

在数据库里面经常性的会给数据库表设置一个自增长的列作为主键,如果我们操作数据库后希望能够获取这个主键该怎么弄呢?正如上面所述,如果是支持自增长的数据库,如mysql数据库,那么只需要设置useGeneratedKeys和keyProperties属性便可以了,但是对于不支持自增长的数据库(如oracle)该怎么办呢?

mybatis里面在<insert>元素下面提供了<selectKey>子元素用于帮助解决主键自增长的问题。

来看下配置:

<selectKey
        <!-- selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 -->
        keyProperty="id"
        <!-- 结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。 -->
        resultType="int"
        <!-- 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。 -->
        order="BEFORE"
        <!-- 与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。 -->
        statementType="PREPARED">
</selectKey>

针对不能使用自增长特性的数据库,可以使用下面的配置来实现相同的功能:

<insert id="insertUser" parameterType="com.dy.entity.User">
        <!-- oracle等不支持id自增长的,可根据其id生成策略,先获取id -->
        <selectKey resultType="int" order="BEFORE" keyProperty="id">
              select seq_user_id.nextval as id from dual
        </selectKey>
  
           insert into user(id, name, password, age, deleteFlag) 
               values(#{id}, #{name}, #{password}, #{age}, #{deleteFlag})
</insert>

2、Select

.dtd文件显示的所有属性和可包含的标签
element : 表示里面可以包含的标签
attlist : 表示标签所包含的属性

<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED 
>

案例 :

<select
     id="findUserById"
     parameterType="int"
     resultType="User"
     resultMap="userResultMap"
     flushCache="false"
     useCache="true"
     timeout="10000"
     fetchSize="256"
     statementType="PREPARED"
     resultSetType="FORWARD_ONLY"></select>
属性是否必须  
idyesid是命名空间中的唯一标识符,可被用来代表这条语句。 
一个命名空间(namespace) 对应一个dao接口, 
这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致
 
parameterType(推荐使用)no

(parameterType / ParameterMap 二选一配置)

默认为mybatis自动选择处理

将要传入语句的参数的完全限定类名或别名, 如果不配置,mybatis会通过ParameterHandler 根据参数类型默认选择合适的typeHandler进行处理
parameterType 主要指定参数类型,可以是int, short, long, string等类型,也可以是复杂类型(如对象)

 
ParameterMapno

(parameterType / ParameterMap 二选一配置)

 

 
resultTypeno

(resultType / resultMap 二选一配置)

resultType用以指定返回类型,指定的类型可以是基本类型,可以是java容器,也可以是javabean

 
resultMap(推荐使用)no

(resultType / resultMap 二选一配置)

resultMap用于引用我们通过 resultMap标签定义的映射类型,这也是mybatis组件高级复杂映射的关键

 
flushCacheno

默认值:false

将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空

 
useCacheno

默认值:对 select 元素为 true

将其设置为 true,将会导致本条语句的结果被二级缓存

 
timeoutno

默认值为 unset(依赖驱动)

这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。

 
fetchSizeno

默认值为 unset(依赖驱动)

这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。

 
statementTypeno

默认值:PREPARED

STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement

 
resultSetTypeno

默认值为 unset (依赖驱动)

FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个

 

 

四、入参与出参(重点)

resultType、resultMap、parameterType、parameterMap

  • parameterType、parameterMap:是入参形式
  • resultType、resultMap:是出参形式
  • Map:映射
  • Type:Java类型

当实体类中的属性和数据库中的字段对应时,我们使用resultTypeparameterType就可以完成CRUD

当实体类中的属性和数据库中的字段不对应时,就要用resultMapparameterMap

1、resultType、resultMap

resultType:

是一种“查询结果集---Bean对象”数据类型映射关系,使用resultType关系,即可使Bean对象接收查询结果集;见名知意,该方法是通过查询结果集中每条记录(属性)的数据类型和Bean对象的数据类型作映射,若两者都相同,则表示匹配成功,Bean可以接收到查询结果。但是方法有局限性,要求Bean对象字段名和查询结果集的属性名相同(可以大小写不同,大小写不敏感)。因为这个局限性,可以省略调resultMap进行属性名映射。一般适用于pojo(简单对象)类型数据,简单的单表查询。

以下是resultType的写法,将其值设置成对应的java类上即可。不需要resultMap的映射关系。

 <select id="queryMessageList" parameterType="hdu.terence.bean.Message"   resultType=" hdu.terence.bean.Message ">
    SELECTID,COMMAND,DESCRIPTION,CONTENT FROM message WHERE 1=1     
    <if test="command!=null and!&quot;&quot;.equals(command.trim())">
    and COMMAND=#{command}
    </if>
    <if test="description!=null and!&quot;&quot;.equals(description.trim())">
    and DESCRIPTION like '%' #{description} '%'
    </if> 
  </select>

 

resultMap(非常重要):

是一种“查询结果集---Bean对象”属性名称映射关系,使用resultMap关系可将将查询结果集中的列一一映射到bean对象的各个属性(两者属性名可以不同,配置好映射关系即可),适用与复杂一点的查询。并且,在映射关系中,还可以通过typeHandler设置实现查询结果值的类型转换,比如布尔型与0/1的类型转换

<!ELEMENT resultMap (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST resultMap
id CDATA #REQUIRED
type CDATA #REQUIRED
extends CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
>

案例: 

<!-- 将JAVA实体类中的属性和表中的字段进行对应 
  column:数据库中的列
  jdbcType:数据库中的类型
  property:对应的实体类中的属性
 -->
 <resultMap type="Book.dao.Book" id="BookResultMap">
  <!--id是主键标签-->
  <id column="id" jdbcType="VARCHAR" property="id"/>
  <!--result代表其它列标签-->
  <result column="name" jdbcType="VARCHAR" property="bookName"/>
  <result column="price" jdbcType="VARCHAR" property="bookPrice"/>
 </resultMap>

(1)<association>适用于表的连接查询一对一关系

<association property=" " javaType=" ">

  • property = " " 被维护实体在宿主实体中的属性名 
  • javaType=" "是被维护方在宿主类中的限定类型
public class Orders{
	private Integer id;
	private Integer number;
	private Data createtime;
	private String note;
	private Integer user_id
	private User user;
	...此处省略GET SET
}
public class User{
	private Integer user_id;
	private String username;
	private String sex;
	private String address;
	...此处省略GET SET
}
<!-- 订单查询关联用户的resultMap将整个查询的结果映射到cn.itcast.mybatis.po.Orders中   -->  
    <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserResultMap">  
        <!-- 配置映射的订单信息 -->  
        <!-- id:指定查询列中的唯 一标识,订单信息的中的唯 一标识,如果有多个列组成唯一标识,配置多个id ,column:订单信息的唯 一标识列 ,property:订单信息的唯 一标识 列所映射到Orders中哪个属性  -->  
 
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="number" jdbcType="INTEGER" property="number"/>
        <result column="createtime" jdbcType="DATE" property="createtime"/>
        <result column="note" jdbcType="VARCHAR" property=note/>
        <result column="user_id" jdbcType="INTEGER" property="userId"/>
 
        <!-- 配置映射的关联的用户信息 --> 
        <!-- association:维护 一对一关系;用于映射关联查询单个对象的信息property:要将关联查询的用户信息映射到Orders中哪个属性 -->  
 
        <association property="user"  javaType="cn.itcast.mybatis.po.User">
            <!-- id:关联查询用户的唯 一标识
            column:指定唯 一标识用户信息的列
            javaType:映射到user的哪个属性-->
            <id column="user_id" jdbcType="INTEGER" property="id"/>
            <result column="username" jdbcType="VARCHAR" property="username"/>
            <result column="sex" jdbcType="VARCHAR" property="sex"/>
            <result column="address" jdbcType="VARCHAR" property="address"/>
        </association> 
    </resultMap>

(2)<collection>适用于表的一对多连接查询

<collection property=" " ofType=" "> 

  • property = " " 被维护实体在宿主实体中的属性名 
  • ofType=" "是被维护方在宿主类中集合泛型限定类型

例子1:

订单对应多个订单明细时,需要根据连接条件订单id匹配订单明细,并且消除重复的订单信息(订单明细中的),如下程序)

<resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">
        <!-- 订单信息 --> 
        <!-- 用户信息 -->
        <!-- 使用extends继承,不用在中配置订单信息和用户信息的映射 -->
        <!-- 订单明细信息一个订单关联查询出了多条明细,要使用collection进行映射
        collection:对关联查询到多条记录映射到集合对象中
        property:将关联查询到多条记录映射到cn.itcast.mybatis.po.Orders哪个属性 
        ofType:指定映射到list集合属性中pojo的类型 -->  
 
         <collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
            <!-- id:订单明细唯 一标识 
            property:要将订单明细的唯 一标识 映射到cn.itcast.mybatis.po.Orderdetail的哪个属性-->  
            <id column="orderdetail_id" property="id"/>
            <result column="items_id" property="itemsId"/>
            <result column="items_num" property="itemsNum"/>
            <result column="orders_id" property="ordersId"/>
         </collection>
    </resultMap> 

 例子2:

由于在一对多关系中,多的一放是以List形式存在,因此ofType的值取用Lsit<?> 的泛型对象类型

public class OrderTable {
    private String orderId;
    private String cid;
    private String address;
    private Date createDate;
    private String orderitemId;
    private List<Orderitem> orderitemList;
	...此处省略GET SET
}
<resultMap id=“BaseResultMap” type=“pojo.OrderTable” >  
   <id column=“order_id” property=“orderId” jdbcType=“VARCHAR” />  
   <result column=“cid” property=“cid” jdbcType=“VARCHAR” />  
   <result column=“address” property=“address” jdbcType=“VARCHAR” />  
   <result column=“create_date” property=“createDate” jdbcType=“TIMESTAMP” />  
   <result column=“orderitem_id” property=“orderitemId” jdbcType=“VARCHAR” />  
     <!–维护一对多的关系  –>  
    <collection property=“orderitemList” ofType=“pojo.Orderitem”>  
        <id column=“orderitem_id” property=“orderitemId”/>  
        <result column=“product_id” property=“productId”/>  
        <result column=“count” property=“count”/>  
    </collection>   
</resultMap>  

 

(3)映射的查询结果集中的列标签可以根据需要灵活变化,并且,在映射关系中,还可以通过typeHandler设置实现查询结果值的类型转换,比如布尔型与0/1的类型转换。

<resultMaptype="hdu.terence.bean.Message" id="MessageResult"> 
    <!--存放Dao值--><!--type是和数据库对应的bean类名Message-->
    <id column="id" jdbcType="INTEGER"property=" id"/><!--主键标签-->
    <result column="COMMAND" jdbcType="VARCHAR" property="command"/>
    <result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
    <result column="CONTENT" jdbcType="VARCHAR" property="content"/>
  </resultMap> 
 
  <select id="queryMessageList" parameterType="hdu.terence.bean.Message" resultMap="MessageResult">
    SELECTID,COMMAND,DESCRIPTION,CONTENT FROM message WHERE 1=1    
    <if test="command!=null and!&quot;&quot;.equals(command.trim())">
    and COMMAND=#{command}
    </if>
    <if test="description!=null and!&quot;&quot;.equals(description.trim())">
    and DESCRIPTION like '%' #{description} '%'
    </if> 
  </select>

注意:

  1. 关于出现重复列名的处理:在实际操作过程中,查询到的结果可能会出现相同的列名,这样会对映射到实体属性带来影响甚至出现报错,那么对待这个问题可以通过对列取别名的方式处理
  2. 关联关系的维护可以根据实体类之间的实际情况进行嵌套维护
<resultMap id=“BaseResultMap” type=“pojo.OrderTable” >  
    <id column=“order_id” property=“orderId” jdbcType=“VARCHAR” />  
    <result column=“cid” property=“cid” jdbcType=“VARCHAR” />  
    <result column=“address” property=“address” jdbcType=“VARCHAR” />  
    <result column=“create_date” property=“createDate” jdbcType=“TIMESTAMP” />  
    <result column=“orderitem_id” property=“orderitemId” jdbcType=“VARCHAR” />  
         <!–维护一对多的关系  –>  
        <collection property=“orderitemList” ofType=“pojo.Orderitem”>  
            <id column=“orderitem_id” property=“orderitemId”/>  
            <result column=“product_id” property=“productId”/>  
            <result column=“count” property=“count”/>  
<span style=“white-space:pre”>        </span><!–嵌套一对一关系–>  
            <association property=“customer” javaType=“pojo.Customer”>  
                <id column=“cid” property=“cid”/>  
                <result column=“cname” property=“cname”/>  
            </association>  
        </collection>   
</resultMap>

 

2、parameterType、parameterMap

parameterType:

直接将查询结果列值类型自动对应到java对象属性类型上,不再配置映射关系一一对应,例如上述代码中下划线部分表示将查询结果类型自动对应到hdu.terence.bean.Message的Bean对象属性类型。

(1)MyBatis的传入参数parameterType类型分两种

  • 基本数据类型:int,string,long,Date;
  • 复杂数据类型:类和Map

(2)如何获取参数中的值

  • 基本数据类型:#{参数} 获取参数中的值
  • 复杂数据类型:#{属性名}  ,map中则是#{key}

(3)案例

    1)基本数据类型案例

<sql id="Base_Column_List" > 
 id, car_dept_name, car_maker_name, icon,car_maker_py,hot_type 
</sql> 
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" > 
 select 
 <include refid="Base_Column_List" /> 
 from common_car_make 
 where id = #{id,jdbcType=BIGINT} 
</select>

    2)复杂类型--类类型

<update id="updateByPrimaryKeySelective" parameterType="com.epeit.api.model.CommonCarMake" > 
 update common_car_make 
 <set > 
  <if test="carDeptName != null" > 
  car_dept_name = #{carDeptName,jdbcType=VARCHAR}, 
  </if> 
  <if test="carMakerName != null" > 
  car_maker_name = #{carMakerName,jdbcType=VARCHAR}, 
  </if> 
  <if test="icon != null" > 
  icon = #{icon,jdbcType=VARCHAR}, 
  </if> 
  <if test="carMakerPy != null" > 
   car_maker_py = #{carMakerPy,jdbcType=VARCHAR}, 
  </if> 
  <if test="hotType != null" > 
   hot_type = #{hotType,jdbcType=BIGINT}, 
  </if> 
 </set> 
 where id = #{id,jdbcType=BIGINT} 
</update>

    3)复杂类型--map类型 

<select id="queryCarMakerList" resultMap="BaseResultMap" parameterType="java.util.Map"> 
  select 
  <include refid="Base_Column_List" /> 
  from common_car_make cm 
  where 1=1 
  <if test="id != null"> 
   and cm.id = #{id,jdbcType=DECIMAL} 
  </if> 
  <if test="carDeptName != null"> 
   and cm.car_dept_name = #{carDeptName,jdbcType=VARCHAR} 
  </if> 
  <if test="carMakerName != null"> 
   and cm.car_maker_name = #{carMakerName,jdbcType=VARCHAR} 
  </if> 
  <if test="hotType != null" > 
   and cm.hot_type = #{hotType,jdbcType=BIGINT} 
  </if> 
  ORDER BY cm.id 
 </select>

    4)复杂类型--map中包含数组的情况

<select id="selectProOrderByOrderId" resultType="com.epeit.api.model.ProOrder" parameterType="java.util.HashMap" > 
  select sum(pro_order_num) proOrderNum,product_id productId,promotion_id promotionId 
  from pro_order 
  where 1=1 
  <if test="orderIds != null"> 
   and 
   <foreach collection="orderIds" item="item" open="order_id IN(" separator="," close=")"> 
    #{item,jdbcType=BIGINT} 
   </foreach> 
  </if> 
  GROUP BY product_id,promotion_id 
 </select>

ParameterMap:

和resultMap类似,表示将查询结果集中列值的类型一一映射到java对象属性的类型上,在开发过程中不推荐这种方式

 

3、案例

下面的代码是一个Book实体类 和 BOOK_MANAGE数据库,他们的字段是不对应的,以下是用resultMap和parameterMap进行的CRUD操作。

查询时,我们需要返回类型,即用resultMap;

增改删时,我们需要参数类型,即用parameterMap;(这里的删除是根据id删除,实体类和数据库对应的,所以用parameterType效果也一样。

实体类:

public class Book {
 private int id;
 private String bookName;
 private double bookPrice;

(.....省略get set 方法)
}

BookSql.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- Sql映射  | namespce:唯一标识当前此映射文件 -->
<mapper namespace="my.BookManage">
 
 <!-- 将JAVA实体类中的属性和表中的字段进行对应 
  column:数据库中的列
  property:对应的实体类中的属性
 -->
 <resultMap type="Book.dao.Book" id="BookResultMap">
  <id column="id" property="id"/>
  <result column="name" property="bookName"/>
  <result column="price" property="bookPrice"/>
 </resultMap>
 
 <!-- resultMap:resultMap的id ,bookName:resultMap的property,即实体类中的属性 -->
 <parameterMap type="Book.dao.Book" id="BookParameterMap">
  <parameter property="bookName" resultMap="BookResultMap" />  
  <parameter property="bookPrice" resultMap="BookResultMap" />  
 </parameterMap>

 <select id="selectAll" resultMap="BookResultMap">
  select * from BOOK_MANAGE
 </select>
 
 <!-- 根据ID查询Book -->
 <select id="selectBookById" parameterType="int" resultMap="BookResultMap">
  select * from BOOK_MANAGE 
   where 
  id=#{id}
 </select>
 
 <!-- 根据ID删除Book -->
 <delete id="deleteBookById" parameterType="Book.dao.Book"> 
  delete from BOOK_MANAGE 
   where 
  id=#{id}
 </delete>
 
 <!-- 保存一个Book -->
 <insert id="saveBook" parameterMap="BookParameterMap">
  insert into BOOK_MANAGE
   (ID,NAME,PRICE)
    values
   (Bookmanage_Seq.Nextval,#{bookName},#{bookPrice})
 </insert>
 
 <!-- 根据ID修改Book -->
 <update id="updatePersnById" parameterMap="BookParameterMap">
  update BOOK_MANAGE 
   set 
    NAME=#{bookName},
    PRICE=#{bookPrice}
   WHERE id=#{id}
 </update>
</mapper>

 

五、动态SQL标签(重点)

1、sql、include 

 sql 标签的 di 表示 sql 片段的唯一标识符

include 使用 refid 来引入 sql 的 id

<!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST sql
id CDATA #REQUIRED
lang CDATA #IMPLIED
databaseId CDATA #IMPLIED
>

<!ELEMENT include (property+)?>
<!ATTLIST include
refid CDATA #REQUIRED
>

案例: 

<!--定义sql片段-->
<sql id="user_table_all_columns">
    id,username,gender,birthday,address
</sql>
<!--查询用户-->
<select id="findUserById" parameterType="int" resultType="com.liuyanzhao.mybatis.po.User">
    SELECT
    <!--引入sql片段-->
    <include refid="user_table_all_columns"/>
    FROM user WHERE id=#{value}
</select>

2、if

if 标签通常用于 WHERE 语句中,通过判断参数值来决定是否使用某个查询条件, 他也经常用于UPDATE语句中判断是否更新某一个字段,还可以在INSERT语句中用来判断是否插入某个字段的值

<!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST if
test CDATA #REQUIRED
>

 案例

<select id=" getStudentListLikeName " parameterType="StudentEntity" resultMap="studentResultMap">     
    SELECT * from STUDENT_TBL ST      
    <if test="studentName!=null and studentName!='' ">     
        WHERE ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName}),'%')      
    </if>     
</select> 

3、foreach 

遍历传入的List、collection或map参数,依次使用集合中的元素执行SQL语句

<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST foreach
collection CDATA #REQUIRED
item CDATA #IMPLIED
index CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
separator CDATA #IMPLIED
>
collection

1、入参为list、array、map

collection属性的值有三个分别是list、array、map三种

(collection=“array” / collection = “list” / collection = “map”)

要做foreach的对象,作为入参时,List<?>对象默认用list代替作为键,数组对象有array代替作为键,Map对象没有默认的键。当然在作为入参时可以使用@Param("keyName")来设置键,设置keyName后,list,array将会失效。

 

2、入参为POJO

除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:
如果User有属性 List ids; 入参是User对象,那么这个collection = "ids"
如果User有属性 Ids ids; 其中Ids是个对象,Ids有个属性List id; 入参是User对象,那么collection = "ids.id
"

上面只是举例,具体collection等于什么,就看你想对那个元素做循环。
该参数为必选

item

表示集合中每一个元素进行迭代时的别名

参数名,这个参数名是循环中的对象名,

例如:一个集合LIst<User> users

open开始符号,循环开始前添加
separator分隔符号,用来在每一次循环之间添加特定的参数(例如:separator="," 结果就为1,2,3,4)
close结束符号,循环结束后添加
index

用于表示在迭代过程中,每次迭代到的位置(下标位置)

在 list 和 数组中,index 是元素的序号;

在 map 中,index 是元素的 key。

当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

例子1:将名字是jerry的员工从A部门中删除

实体类(表结构是:d_id , d_name , u_id)

public class Department {
    private Integer d_id;
    private String d_name;
    private Integer u_id
    private List<User> userList;
	...
}

方法接口

public interface UserTypeDOMapper {
	int delete(Department department);
}

mapper

这里传入的是POJO对象

  • collection:是对象里面集合的名字
  • item:是定义集合中类型的一个别名
<delete id="deleteByPriKeys" parameterType="java.lang.String">  
     delete from product where product_Id in  
     <foreach collection="userList" item="user" open="(" separator="," close=")">  
         #{productId,jdbcType = VARCHAR}  
     </foreach>  
</delete>

<delete id="deleteByPriKeys" parameterType="com.test.Department">  
     delete from department where user_id in  
     <foreach collection="userList" item="user" open="(" separator="," close=")">  
         #{user.id,jdbcType=INTEGER}  
     </foreach>  
</delete>

下面使用的是直接传入list对象

int batchDeleteByPrimaryKey(List<Integer> uidList);

  • collection:我这里设定为list(collection属性的值有三个分别是list、array、map三种
  • item:是定义集合中类型的一个别名
  <delete id="batchDeleteByPrimaryKey"  parameterType="java.util.ArrayList">
    delete from tobacco_users
    <where>
      uid in
      <foreach collection="list" item="item" open="(" separator="," close=")">
        #{item,jdbcType=INTEGER}
      </foreach>
    </where>
  </delete>

 

(1)传入的参数为list的时候

 对应的Dao中的Mapper文件是:

public List<User> selectByIds(List<Integer> ids);

xml文件代码片段:

<select id="selectByIds" resultType="com.txw.pojo.User">
        select * from user where id in
        <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
            #{item}
        </foreach>
</select>

(2)传入的参数为Array的时候

对应的Dao中的Mapper文件是:

public List<User> selectByIds(int[] ids);

xml文件代码片段: 

<select id="selectByIds" resultType="com.txw.pojo.User">
        select * from user where id in
        <foreach collection="array" index="index" item="item" open="(" separator="," close=")">
            #{item}
        </foreach>
</select>

 

(3) 传入的参数为Map的时候

对应的Dao中的Mapper文件是:

public List<User> selectByIds(Map<String, Object> params);

注意:Map<String, Object> 中的元素有 int[] ids = {2, 4};  map.put("ids", ids);

xml文件代码片段: 

<select id="selectByIds" resultType="com.txw.pojo.User">
        select * from user where  id in
        <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
            #{item}
        </foreach>
</select>

map的时候需要注意的是:collection的值“ids”是存储在map中的key(比如:map.put("ids",ids));尤其需要注意;

 

 

3、choose 

有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis提供了choose 元素,按顺序判断when中的条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行 otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default。

if是与(and)的关系,而choose是或(or)的关系。

<!ELEMENT choose (when* , otherwise?)>
<!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST when
test CDATA #REQUIRED
>
<!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>

案例: 

<select id="getStudentListChoose" parameterType="Student" resultMap="BaseResultMap">     
    SELECT * from STUDENT WHERE 1=1    
    <where>     
        <choose>     
            <when test="Name!=null and student!='' ">     
                   AND name LIKE CONCAT(CONCAT('%', #{student}),'%')      
            </when>     
            <when test="hobby!= null and hobby!= '' ">     
                    AND hobby = #{hobby}      
            </when>                   
            <otherwise>     
                    AND AGE = 15  
            </otherwise>     
        </choose>     
    </where>     
</select>

六、格式化输出

1、where、set

where只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除
set类似的用于动态更新语句的解决方案叫做 setset 元素可以用于动态包含需要更新的列,而舍去其它的
<!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>

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>

set 案例 :

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

2、trim

 trim 标记是一个格式化的标记,可以完成set或者是where标记的功能

属性描述
prefix给sql语句拼接的前缀
suffix给sql语句拼接的后缀
prefixOverrides去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定
<!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST trim
prefix CDATA #IMPLIED
prefixOverrides CDATA #IMPLIED
suffix CDATA #IMPLIED
suffixOverrides CDATA #IMPLIED
>

案例:

<select id="selectUser" resultMap="BaseResultMap">
    select * from user 
    <trim prefix="WHERE" prefixoverride="AND | OR">
      <if test="name != null and name.length()>0"> AND name=#{name}</if>
      <if test="gender != null and gender.length()>0"> AND gender=#{gender}</if>
    </trim>
</select>

<update id="updateUser" parameterType="com.marvin.demo.entity.UsersBean">
  update user
  <trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">
    <if test="name != null and name.length()>0"> name=#{name} , </if>
    <if test="gender != null and gender.length()>0"> gender=#{gender} ,  </if>
  </trim>
</update>

<insert id="insertSelective" parameterType="com.marvin.demo.entity.UsersBean">
    insert into tobacco_users
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="uid != null"> uId, </if>
      <if test="uname != null"> uName, </if>
      <if test="upwd != null"> uPwd, </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="uid != null"> #{uid,jdbcType=INTEGER}, </if>
      <if test="uname != null"> #{uname,jdbcType=VARCHAR}, </if>
      <if test="upwd != null"> #{upwd,jdbcType=VARCHAR}, </if>
    </trim>
</insert>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值