MyBatis-SQL映射文件

一.概述

映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义;SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

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

二.属性

2.1 insert,update和delete

属性描述
id命名空间中的唯一标识符,可被用来代表这条语句。
parameterType将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
statementTypeSTATEMENT,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 的语句;如果带或者不带的语句都有,则不带的会被忽略。

2.1.2 实验:获取自增主键的值

<!-- public int insertEmployee(Employee employee); -->
	<!-- 让MyBatis自动将自增id赋值传入的employee对象的id属性
	      useGeneratedKeys:使用自动生成的主键,原生JDBC获取自增主键的方法
	      keyProperty:将刚才自增的id封装给哪个属性
	  -->
	<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
	    insert into t_employee(empname,gender,email) values(#{empname},#{gender},#{email})
	</insert>

2.1.3 实验:获取非自增主键的值

<insert id="insertEmployee2">
	  <!-- 查询主键 
	   order="BEFORE":在核心sql语句之前先运行一个查询sql查到的id,将查到的id值赋给JavaBean的哪个属性
	-->
	<selectKey order="BEFORE" keyProperty="id" resultType="integer">
	   select max(id)+1 from t_employye
	</selectKey>
	     insert into t_employee(id,empname,gender,email) values(#{id},#{empname},#{gender},#{email})
	</insert>

selectKey元素属性描述如下:

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

2.2 select

2.2.1 概述

select元素来定义查询操作,属性描述如下:

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
resultType从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
flushCache将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
fetchSize这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
statementTypeSTATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
databaseId如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSets这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。

2.2.2 实验:查询返回List

  <!-- public List<Employee> getAllEmps(); -->
  <!-- resultType:如果返回的是集合,写的是集合元素里面的类型 -->
  <select id="getAllEmps" resultType="com.test.bean.Employee">
    select * from t_employee
  </select>
  

2.2.3 实验:查询单条或多条封装Map

1)查询单条记录封装Map

  <!-- public Map<String,Object> getEmpByIdReturnMap(Integer id); -->
  <!-- 写成map是因为支持别名 -->
  <!--//默认列名作为key,值作为value-->
<select id="getEmpByIdReturnMap" resultType="map">
select * from t_employee where id=#{id}
     
</select>

测试:

2)查询多条封装map:

EmployeeDao.xml:使用注解@MepKey

//多条
	//key就是这个记录的主键,value就是这个记录封装好的对象
	//把查询的记录的id的值作为key封装这个map
	@MapKey(value = "id")
	public Map<Integer,Object> getAllEmpReturnMap();

映射文件:

<!-- 	//多条
	//key就是这个记录的主键,value就是这个记录封装好的对象
	public Map<Integer,Object> getAllEmpReturnMap(Integer id); -->
	<!-- 查询多个返回一个:集合里面写元素类型 -->
	<select id="getAllEmpReturnMap" resultType="com.test.bean.Employee">
	select * from t_employee 
	</select>
  

要注意的是:查询返回List和查询多条封装Map的时候,返回类型要写成集合里面的元素类型

2.3 参数

2.3.1 参数取值

实际上在MyBatis中,两种取值方式:

  • #{属性名}:是参数预编译的方式,参数的位置都是?代替,参数后来都是预编译设置进去的。安全,不会有SQL注入问题。
  • ${属性名}:不是参数预编译,直接和SQL语句进行拼串。不安全

要注意,SQL语句只有参数位置是支持编译的

下面以一个sql语句:

   select * from t_employee where id=${id} and empname=#{empname}
  

这个执行的语句:

 select * from t_employee where id=1 and empname=? 

如果都用#取值:

select * from t_employee where id=? and empname=?

一般都是使用#{}安全,在不支持参数预编译位置要进行取值就使用${}。

2.3.2 参数传递

1)单个参数:可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理。取值:    基本类型:#{随便写}

2)多个参数:只要传入多个参数,MyBatis自动将这些参数封装在一个map中,封装时使用的key就是参数的索引和参数的第几个标识。使用0,1(参数的索引)或者parm1,parm2(第几个param)。相当于下面的存入过程:

Map<String,Object> map=new HashMap<>();
                       map.put("1",传入的值); 
                       map.put("2",传入的值);
                       #{key}就是从这个map中的值

3)命名参数:为参数使用@Param注解起一个名字,MyBatis就会将这些参数封装进Map,key就是我们指定的名字

实验代码:

 <!-- 	public Employee getEmpByIdAndName(Integer id,String empName); -->
  <!-- 传递多个参数的时候,不能像传递单个参数一样,那样取值,会出现BindingException -->
  <select id="getEmpByIdAndName" resultType="com.test.bean.Employee">
   <!-- select * from t_employee where id=#{0} and empname=#{1} -->  
   select * from t_employee where id=#{id} and empname=#{empname}
  </select>

2.3.2 参数处理

参数位置支持的属性: javaType、 jdbcType mode numericScale 、resultMap、 typeHandler jdbcTypeName。

参数也可以指定一个特殊的数据类型:

  • javaType 通常可以从参数对象中来去确定

  • 如果 null 被当作值来传递,对于所有可能为空的列,jdbcType 需要被设置

  • 对于数值类型,还可以设置小数点后保留的位数:

  • mode 属性允许指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样

2.4 resultMap

2.4.1 场景

上面查询的Employee对象属性名与数据库中的属性名相对应,即使是loginAccount也满足驼峰命名规则。但是也有种情况,数据库表中列名和JavaBean对象的属性名不相符,就可以使用resutMap,自定义结果封装结果集。

2.4.2 属性简介

•constructor - 类在实例化时, 用来注入结果到构造方法中
–idArg - ID 参数; 标记结果作为 ID 可以帮助提高整体效能
–arg - 注入到构造方法的一个普通结果
•id – 一个 ID 结果; 标记结果作为 ID 可以帮助提高整体效能
•result – 注入到字段或 JavaBean 属性的普通结果
•association – 一个复杂的类型关联;许多结果将包成这种类型
–嵌入结果映射 – 结果映射自身的关联,或者参考一个
•collection – 复杂类型的集
–嵌入结果映射 – 结果映射自身的集,或者参考一个
•discriminator – 使用结果值来决定使用哪个结果映射
–case – 基于某些值的结果映射
•嵌入结果映射 – 这种情形结果也映射它本身,因此可以包含很多相同的元素,或者它可以参照一个外部的结果映射。

2.4.3 id 和result

这是结果映射最基本内容。id 和 result 都映射一个单独列的值到简单数据类型(字符 串,整型,双精度浮点数,日期等)的单独属性或字段。这两者之间的唯一不同是 id 表示的结果将是当比较对象实例时用到的标识属性。

属性描述
property映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同 的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称 property 的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你 可以这样映射一些东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。
column从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会 传递给 resultSet.getString(columnName)方法参数中相同的字符串。
javaType一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为。
jdbcType在这个表格之后的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅 仅需要对插入,更新和删除操作可能为空的列进行处理。这是 JDBC jdbcType 的需要,而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定 这个类型-但仅仅对可能为空的值。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理 器的实现,或者是类型别名。

实验代码:

<?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.test.dao.CatDao">
  <!-- public Cat getCatById(Integer id); -->
  <!-- resultType="com.test.bean.Cat":使用默认规则,属性列名一一对应
       resultMap="":查出数据使用这个指定的规则进行封装
   -->
  
  <select id="getCatById" resultMap="mycat">
      select id,cname name,cAge age,cgender gender from t_cat where id=#{id}
  </select>
  
  <!-- 自定义结果集:自己定义每一列数据和JavaBean的映射规则 -->
  <!-- type="":指定为哪个JavaBean自定义规则,全类名
       id="" :唯一标识,让别名在后面引用-->
  <resultMap type="com.test.bean.Cat" id="mycat">
  <!--id:指定主键的对应规则 
     column="id":指定哪一列是主键列
     property="":指定JavaBean的哪个属性封装id这一列数据
      -->
  <id column="id" property="id"/>
  <!-- 普通列 :result-->
  <result column="cName" property="name"/>
  <result column="cAge" property="age"/>
  <result column="cgender"  property="gender"/>
  </resultMap>
  </mapper>

2.4.4 联合查询实验一:有两个对象,Lock和Key,在Key中有Lock对象,也就是说一把钥匙对应一把锁

/*
 * 钥匙
 */
public class Key {
	private Integer id;
	private String keyName;
	private Lock lock;  //当前钥匙能开哪把锁
	...
}

数据库设计,数据库Key表中有外键:

(1)级联属性的方式封装查出的数据

KeyDao:

package com.test.dao;

import com.test.bean.Key;

public interface KeyDao {
	
	//将锁和钥匙一起查出
	public Key getKeyByID(Integer id);
	

}

KeyDao的实现:SQL映射文件,在这个文件中自定义结果集封装规则,用来封装JavaBean中另一个Bean的属性

<?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.test.dao.KeyDao">
    <!-- public Key getKeyByID(Integer id); -->
    <select id="getKeyByID" resultMap="mykey">
SELECT k.id ,k.keyname,k.lockid,l.lockname FROM t_key k LEFT JOIN t_lock l ON k.lockid=l.id WHERE k.id=#{id}
    </select>
    <!-- 自定义封装规则 ,使用级联属性封装联合查询出的结果-->
    <resultMap type="com.test.bean.Key" id="mykey">
      <id property="id" column="id"/>
      <result property="keyName" column="keyname"/>
      <result property="lock.id" column="lid"/>
      <result property="lock.lockName" column="lockname"/>
    </resultMap>
  </mapper>

(2)使用association定义联合查询的对象的封装规则

实验代码:

<?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.test.dao.KeyDao">
    <!-- public Key getKeyByID(Integer id); -->
    <select id="getKeyByID" resultMap="mykey">
SELECT k.id ,k.keyname,k.lockid,l.id,l.lockname FROM t_key k LEFT JOIN t_lock l ON k.lockid=l.id WHERE k.id=#{id}
    </select>
    
    <!-- MaBatis推荐的 -->
     <resultMap type="com.test.bean.Key" id="mykey">
      <id property="id" column="id"/>
      <result property="keyName" column="keyname"/>
      <!-- 接下来的属性是一个对象,自定义这个对象的封装规则,使用association表示联合了一个对象 -->
      <!-- javaType这个属性的全类名 -->
      <association property="lock" javaType="com.test.bean.Lock">
         <!-- 定义lock属性对应的lock对象如何封装 -->
         <id property="id" column="lid"/>
         <result property="lockName" column="lockname"/>
         
         
      </association>
    </resultMap>
  </mapper>

2.4.5 联合查询实验二背景:一把锁可以有多个钥匙

public class Lock {

	private Integer id;
	private String lockName;
	private List<Key> keys;
    ...
}

LockDao:

public interface LockDao {
	
	public Lock getLockById(Integer id);
	

}

(1)collection定义集合类型属性的封装规则

<?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.test.dao.LockDao">
 <!-- public Lock getLockById(Integer id); -->
  
  <select id="getLockById" resultMap="mylock">
  SELECT k.id kid,k.keyname,k.lockid,l.* FROM t_lock l LEFT JOIN t_key k ON l.id=k.lockid WHERE l.id=#{id}
  </select>
  
  <!-- 自定义封装规则 -->
  <resultMap type="com.test.bean.Lock" id="mylock">
     <id property="id" column="id"/>
     <result property="lockName" column="lockname"/>
     <!-- collection定义集合元素的封装
          property指定哪个属性是集合属性 
          ofType指定集合元素的类型 -->
     <collection property="keys" ofType="com.test.bean.Key">
     <!-- 标签体中指定这个集合的封装规则 -->
     <id property="id" column="kid"/>
     <result property="keyName" column="keyname"/>
     </collection>
  </resultMap>
  
  </mapper>

2.4.5 分步查询

(1)使用collection分步查询

实验背景:查锁对应的钥匙,一把锁对应多个钥匙

KeyDao实现:有一个简答的方法查询Key对象

<!-- 	public List<Key> getKeysByLockId(Integer id); -->
  <select id="getKeysByLockId" resultType="com.test.bean.Key">
     select * from t_key where lockid=#{lockid}
  </select>

LockDao:一个简单的方法查询Lock,Lock对应多个Key,所以使用的List类型,自定义结果封装集的时候,使用collection,同时使用select告诉MyBatis去调用一个查询去Key对象。

 <!-- public Lock getLockIdByStep(Integer id); -->
  <select id="getLockIdByStep" resultMap="mylockstep">
       select * from t_lock where id=#{id}
  </select>  
  
  
  <resultMap type="com.test.bean.Lock" id="mylockstep">
      <id property="id" column="id"/>
      <result property="lockName" column="lockname"/>
      <!-- collection指定集合类型的封装规则 -->
      <collection property="keys" select="com.test.dao.KeyDao.getKeysByLockId" column="id"></collection>
  
  </resultMap>

(2)使用select语句指定分步查询

实验背景:查钥匙对应的锁,一个钥匙对应一个锁

LockDao实现:有一个简单的方法查询Lock对象

  <!-- public Lock getLockByIdSimple(Integer id); -->
  <select id="getLockByIdSimple" resultType="com.test.bean.Lock">
     select * from t_lock where id=#{id}
  </select>
  

KeyDao实现:一个简单的方法查询Key,但是自定义封装结果集的时候,告诉MyBatis去调用一个查询去查询Lock对象

 <!-- public Key getKeyByIdSimple(Integer id); -->
  <!-- 查询key的时候也可以带上锁 -->
  <!-- id keyname lockid -->
  <select id="getKeyByIdSimple" resultMap="mykey01">
    select * from t_key where id=#{id}
  </select>
  
  <resultMap type="com.test.bean.Key" id="mykey01">
    <id property="id" column="id"/>
    <result property="keyName" column="keyname"/>
    <!-- 告诉MyBatis自己去调用一个查询查锁子
         select="":指定一个查询sql的唯一标识,MyBatis自动调用指定的SQL将查询的lock封装进来 
                            需要指定的id,告诉MyBatis把哪一列的值传递过去
          column="":指定把哪一列的数据传递过去-->
    <association property="lock" select="com.test.dao.LockDao.getLockByIdSimple" column="lockid"
    fetchType="eager">
    </association>
  </resultMap>

参考MyBatis的官方文档,需要详细了解SQL映射文件可以看官方文档。映射文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值