一、基本介绍
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 | 类似的用于动态更新语句的解决方案叫做 set。set 元素可以用于动态包含需要更新的列,而舍去其它的 |
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>
属性 | 是否必须 | |
---|---|---|
id | yes | id是命名空间中的唯一标识符,可被用来代表这条语句。 一个命名空间(namespace) 对应一个dao接口, 这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致 |
parameterType(推荐使用) | no | (parameterType / ParameterMap 二选一配置) 默认为mybatis自动选择处理 将要传入语句的参数的完全限定类名或别名, 如果不配置,mybatis会通过ParameterHandler 根据参数类型默认选择合适的typeHandler进行处理 |
ParameterMap | no | (parameterType / ParameterMap 二选一配置)
|
flushCache | no | 默认配置为true 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句) |
statementType | no | 默认值:PREPARED STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement, |
keyProperty | no | 默认为unset (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值 如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表 |
keyColumn | no | (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。 如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表 |
useGeneratedKeys | no | 默认为false (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段) |
timeout | no | 默认为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>
属性 | 是否必须 | ||
---|---|---|---|
id | yes | id是命名空间中的唯一标识符,可被用来代表这条语句。 一个命名空间(namespace) 对应一个dao接口, 这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致 | |
parameterType(推荐使用) | no | (parameterType / ParameterMap 二选一配置) 默认为mybatis自动选择处理 将要传入语句的参数的完全限定类名或别名, 如果不配置,mybatis会通过ParameterHandler 根据参数类型默认选择合适的typeHandler进行处理 | |
ParameterMap | no | (parameterType / ParameterMap 二选一配置)
| |
resultType | no | (resultType / resultMap 二选一配置) resultType用以指定返回类型,指定的类型可以是基本类型,可以是java容器,也可以是javabean | |
resultMap(推荐使用) | no | (resultType / resultMap 二选一配置) resultMap用于引用我们通过 resultMap标签定义的映射类型,这也是mybatis组件高级复杂映射的关键 | |
flushCache | no | 默认值:false 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空 | |
useCache | no | 默认值:对 select 元素为 true 将其设置为 true,将会导致本条语句的结果被二级缓存 | |
timeout | no | 默认值为 unset(依赖驱动) 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。 | |
fetchSize | no | 默认值为 unset(依赖驱动) 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。 | |
statementType | no | 默认值:PREPARED STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement | |
resultSetType | no | 默认值为 unset (依赖驱动) FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个 |
四、入参与出参(重点)
resultType、resultMap、parameterType、parameterMap
- parameterType、parameterMap:是入参形式
- resultType、resultMap:是出参形式
- Map:映射
- Type:Java类型
当实体类中的属性和数据库中的字段对应时,我们使用resultType和parameterType就可以完成CRUD
当实体类中的属性和数据库中的字段不对应时,就要用resultMap和parameterMap了
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!"".equals(command.trim())">
and COMMAND=#{command}
</if>
<if test="description!=null and!"".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!"".equals(command.trim())">
and COMMAND=#{command}
</if>
<if test="description!=null and!"".equals(description.trim())">
and DESCRIPTION like '%' #{description} '%'
</if>
</select>
注意:
- 关于出现重复列名的处理:在实际操作过程中,查询到的结果可能会出现相同的列名,这样会对映射到实体属性带来影响甚至出现报错,那么对待这个问题可以通过对列取别名的方式处理
- 关联关系的维护可以根据实体类之间的实际情况进行嵌套维护
<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 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子: |
---|---|
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 | 类似的用于动态更新语句的解决方案叫做 set。set 元素可以用于动态包含需要更新的列,而舍去其它的 |
<!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>