当前位置:   article > 正文

4.Mybatis映射文件(类名+Mapper.xml)

mybatis映射文件

目录


Mybatis专栏目录(点击进入…)



持久化类(POJO,JavaBean)

持久化类指:其实例状态需要被MyBatis持久化到数据库中的类。在应用的设计中,持久化类通常对应需求中的业务实体。MyBatis一般采用POJO编程模型来实现持久化类,与POJO类配合完成持久化工作是MyBatis最常见的工作模式

POJO(Plain Ordinary Java Object,普通Java对象)。POJO类可以简单地理解为符合JavaBean规范的实体类,它不需要继承和实现任何特殊的Java基类或者接口。JavaBean对象的状态保存在属性中,访问属性必须通过对应的getter和setter方法
在MyBatis中,不需要POJO类名与数据库表名一致,因为MyBatis是POJO与SQL语句之间的映射机制,一般情况下,保证POJO对象的属性与数据库表的字段名一致即可


映射文件(JavaBean名+Mapper.xml)

MyBatis真正强大之处就在于SQL映射语句,也就是它的魅力所在。相当于它强大的功能,SQL映射文件的配置却非常简单。

SQL映射文件的创建,完成与POJO(实体类)的映射,该文件也是一个XML文件,命名为UserMapper.xml(一般都是采用POJO的名称 + Mapper的规则来进行命名);当然该mapper文件属于DAO层的操作,应该放置在dao包下,并根据业务功能进行分包放置

SQL映射文件顶级元素配置

顶级元素描述
mapper映射文件的根元素节点,只有一个属性namespace(命名空间)。用于区分不同的mapper,全局唯一;绑定DAO接口,即面向对象编程。当namespace绑定某一接口之后,可以不用写该接口的实现类。MyBatis通过接口的完整限定名查找到对应的mapper配置来执行SQL语句。因此namespace的命名必须要与接口同名
cache配置给定命名空间的缓冲
cache-ref从其他命名空间引用缓冲配置
parameterMap表示将查询结果集中列值的类型一一映射到java对象属性的类型上,在开发过程中不推荐这种方式
resultMap用来描述数据库结果集和对象的对应关系
sql可以重用的SQL块,也可以被其他语句引用
insert映射插入语句
update映射更新语句
delete映射删除语句
select映射查询语句

Select

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。一个命名空间(namespace)对应一个dao接口,这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id应该与方法名一致
parameterType将要传入语句的参数的完全限定类名或别名,如果不配置,mybatis会通过ParameterHandler根据参数类型默认选择合适的typeHandler进行处理。parameterType主要指定参数类型,可以是int, short, long, string等类型,也可以是复杂类型(如对象)。默认值为unset(依赖驱动)
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(PreparedStatement)
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE或SCROLL_INSENSITIVE中的一个。默认值:unset(依赖驱动)
databaseId如果配置了databaseIdProvider,MyBatis会加载所有的不带databaseId或匹配当前databaseId语句,如果带或者不带的语句都有,则不带的会被忽略
resultOrdered这个设置仅针对嵌套结果select语句适用:如果为true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
resultSets仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的

parameterType(输入类型)

通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的包装类型

占位符
1.#{}(无法拼接SQL)
实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?

<select id="findUserById" parameterType="int" resultType="user">
	select * from user where id = #{id}
</select>
  • 1
  • 2
  • 3

(1)占位符#{}可以有效防止sql注入,在使用时不需要关心参数值的类型,mybatis会自动进行Java类型和jdbc类型的转换(预编译)
(2)#{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称

2.${}(可以拼接SQL)
可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转换,${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value
使用${}不能防止sql注入,但是有时用${}会非常方便

<select id="selectUserByName" parameterType="string" resultType="user">
	select * from user where username like '%${value}%'
</select>
  • 1
  • 2
  • 3

使用#{}则传入的字符串中必须有%号,而%是人为拼接在参数中,显然有点麻烦,如果采用${}在sql中拼接为%的方式则在调用mapper接口

传递参数就方便很多。如果使用${}原始符号则必须人为在传参数中加%

List<User> list = userMapper.selectUserByName("%管理员%");
  • 1

如果使用%${value}%则不用人为在参数中加%

List<User>list = userMapper.selectUserByName("管理员");
  • 1

(1)传递pojo对象

parameterType也可以传递pojo对象。Mybatis使用ognl表达式解析对象字段的值

<!-- 传递pojo对象综合查询用户信息 -->
<select id="findUserByUser" parameterType="user" resultType="user">
	select * from user where id=#{id} and username like '%${username}%'
</select>
  • 1
  • 2
  • 3
  • 4

上边%${username}%中的username就是user对象中对应的属性名称

(2)传递pojo包装对象

parameterType还可以传递pojo包装对象(也就是将多个对象包装为一个对象)
开发中通过pojo传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数
包装对象

public class QueryVo {
    private User user;
	private UserCustom userCustom;
	// 省略getter/setter
}
  • 1
  • 2
  • 3
  • 4
  • 5

映射文件中的使用

<!—传递pojo包装对象综合查询用户信息 -->
<select id="findUserByUser" parameterType="queryVo" resultType="user">
	select * from user where id=#{user.id} and username like '%${user.username}%'
</select>
  • 1
  • 2
  • 3
  • 4

可以看出通过使用类似java中对象访问属性的形式来进行参数传递

(3)传递Hashmap

parameterType也可以传递hashmap类型的参数
xml映射文件

<select id="findUserByHashmap" parameterType="hashmap" resultType="user">
	select * from user where id=#{id} and username like '%${username}%'
</select>
  • 1
  • 2
  • 3

在代码中的调用形式如下:

public void testFindUserByHashmap() throws Exception{
	SqlSession session = sqlSessionFactory.openSession();
	//获取session
	UserMapper userMapper = session.getMapper(UserMapper.class);
	//获限mapper接口实例
	HashMap<String, Object> map = new HashMap<String, Object>();
	//构造查询条件Hashmap对象
	map.put("id", 1);
	map.put("username", "管理员");
	List<User> list = userMapper.findUserByHashmap(map);
	//传递Hashmap对象查询用户列表
	//关闭session
	session.close();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

resultType(返回值类型)

使用resultType可以进行输出映射,只有查询出来的列名和pojo中的属性名一致,才可以映射成功。如果查询出来的列名和pojo中的属性名全部不一致,就不会创建pojo对象。但是只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。

(1)返回简单类型
<!-- 获取用户列表总数 -->
<select id="findUserCount" parameterType="user" resultType="int">
	select count(*) from user
</select>
  • 1
  • 2
  • 3
  • 4
(2)返回pojo对象和pojo列表

当使用动态代理时,输出pojo对象和输出pojo列表在xml映射文件中定义的resultType是一样的,而生成的动态代理对象中是根据mapper方法的返回值类型确定是调用selectOne(返回单个对象调用)还是selectList(返回集合对象调用)

<!-- 获取用户列表总数 -->
<select id="findByUser" parameterType="user" resultType="user">
	select * from user where id=#{user.id}
</select>
  • 1
  • 2
  • 3
  • 4

参数(Parameters)
前面的所有语句中所见到的都是简单参数的例子,实际上参数是MyBatis非常强大的元素,对于简单的做法,大概90%的情况参数都很少
mode属性允许指定IN,OUT或INOUT参数。如果参数为OUT或INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样。如果mode为OUT(或INOUT),而且jdbcType 为CURSOR(也就是Oracle的REFCURSOR),必须指定一个resultMap来映射结果集到参数类型。要注意这里的javaType属性是可选的,如果左边的空白是jdbcType的CURSOR类型,它会自动地被设置为结果集

字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并安全地设置值(比如?)。这样做更安全,更迅速,通常也是首选做法

${}不会修改或转义字符串。不过有时只是想直接在SQL语句中插入一个不改变的字符串。

比如:像ORDER BY,可以这样来使用:
以这种方式接受从用户输出的内容并提供给语句中不变的字符串是不安全的,会导致潜在的SQL注入攻击,因此要么不允许用户输入这些字段,要么自行转义并检验


Insert、Update 和 Delete

insert,update和delete方法返回的值指示该语句影响的行数

属性描述
id命名空间中的唯一标识符,可被用来代表这条语句。一个命名空间(namespace)对应一个dao接口,这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致
parameterType将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数。默认值:unset(无设置,依赖驱动)
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的语句;如果带或者不带的语句都有,则不带的会被忽略

selectKey(子元素)

用来给不支持自动生成主键的数据库用,如oracel,或者该表主键没有设置为主键增长策略

SelectKey在Mybatis中是为了解决Insert数据时不支持主键自动生成的问题,可以很随意的设置生成主键的方式

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

sql

用来定义可重用的SQL代码段,可以包含在其他语句中

使用:利用它的id,在include的属性refid调用

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

<select id="selectUsers" resultType="map">
	select
	<include refid="sqlUser"> 
		<property name="alias" value="t1" />
	</include>
	from some_table t1
</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

cache(缓存)

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

<cache/>效果如下
(1)映射语句文件中的所有select语句的结果将会被缓存
(2)映射语句文件中的所有insert、update和delete语句会刷新缓存
(3)缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存
(4)缓存不会定时进行刷新(也就是说,没有刷新间隔)
(5)缓存会保存列表或对象(无论查询方法返回哪种)的1024个引用
(6)缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
提示:缓存只作用于cache标签所在的映射文件中的语句。如果混合使用Java API和XML映射文件,在共用接口中的语句将不会被默认缓存。需要使用@CacheNamespaceRef注解指定缓存作用域

使用缓存

<cache eviction="FIFO"
	flushInterval="60000"
	size="512"
	readOnly="true"/>
  • 1
  • 2
  • 3
  • 4

这个更高级的配置创建了一个FIFO缓存,每隔60秒刷新,最多可以存储结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突

1.eviction(清除策略)

(1)LRU:最近最少使用:移除最长时间不被使用的对象(默认)
(2)FIFO:先进先出:按对象进入缓存的顺序来移除它们
(3)SOFT:软引用:基于垃圾回收器状态和软引用规则移除对象
(4)WEAK:弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象

2.flushInterval(刷新间隔)

可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新

3.size(引用数目)

可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024

4.readOnly(只读)

可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false

注意:二级缓存是事务性的。这意味着,当SqlSession完成并提交时,或是完成并回滚,但没有执行flushCache=true的insert/delete/update语句时,缓存会获得更新

自定义缓存

除了上述自定义缓存的方式,也可以通过实现自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为

<cache type="com.domain.something.MyCustomCache"/>
  • 1

例子:
type属性指定的类必须实现org.mybatis.cache.Cache接口,且提供一个接受String参数作为id的构造器。这个接口是MyBatis框架中许多复杂的接口之一,但是行为却非常简单。

public interface Cache {
   String getId();
   int getSize();
   void putObject(Object key, Object value);
   Object getObject(Object key);
   boolean hasKey(Object key);
   Object removeObject(Object key);
   void clear();
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

ResultMap

resultMap是Mybatis最强大的元素,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中
关联嵌套查询,有一个非常好的作用:重用select语句,通过简单的select语句之间的组合来构造复杂的对象

在这里插入图片描述

属性描述
id名称空间中的唯一标识符,可以用来引用这个结果映射
type一个完全合格的Java类名,或者一个类型别名(参见上面的表,用于内置类型别名列表)
autoMapping如果设置这个属性,MyBatis将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性autoMappingBehavior。默认值:未设置(unset)
extends继承。也就是本ResultMap包含另一个ResultMap的所有属性

id和result(简单数据类型)

id和result都映射一个单独列的值到简单数据类型(字符串、整型、双精度浮点数、日期等)的单独属性或字段

这两者之间的唯一不同是id表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)

<id column="" property="" javaType="" jdbcType="" typeHandler=""/>
<result column="" property="" javaType="" jdbcType="" typeHandler="" />
  • 1
  • 2
<resultMap id="user" type="com.my.User">
	<id column="id" property="id"  jdbcType="BIGINT"/>
	<result column="name" property="name"  jdbcType="VARCHAR"/>
	<result column="address" property="address"  jdbcType="BIGINT"/>
</resultMap>

<select id="FindUser" parameterType="int" resultMap="user">
	select * from user where id = #{value}
</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

id和Result的属性

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

支持的JDBC类型(jdbcType)
MyBatis通过包含的jdbcType枚举型,支持下面的JDBC类型

BITFLOATCHARTIMESTAMPOTHERUNDEFINED
TINYINTREALVARCHARBINARYBLOGNVARCHAR
SMALLINTDOUBLELONGVARCHARVARBINARYCLOBNCHAR
INTEGERNUMERICDATELONGVARBINARYBOOLEANNCLOB
BIGINTDECIMALTIMENULLCURSORARRAY

非常重要: 在嵌套数据结果映射中id元素扮演了非常重要的角色。应该通常指定一个或多个属性,它们可以用来唯一标识结果。实际上就是如果你离开她了,但是有一个严重的性能问题时MyBatis仍然可以工作。选择的属性越少越好,它们可以唯一地标识结果。主键就是一个显而易见的选择(尽管是联合主键)


constructor(构造函数)

用于配置构造方法(当POJO未定义无参数的构造方法时使用)
用于在实例化类时,注入结果到构造方法中

参数描述
idArgID参数;标记出作为ID的结果可以帮助提高整体性能
arg将被注入到构造方法的一个普通结果
<constructor>
	<idArg column="" columnPrefix="" javaType="" jdbcType="" name="" resultMap="" select="" typeHandler=""/>
	<arg  column="" columnPrefix="" javaType="" jdbcType="" name="" resultMap="" select="" typeHandler=""/>
</constructor>
  • 1
  • 2
  • 3
  • 4

剩余的属性和规则和固定的id和result元素是相同的

属性描述
type一个完全合格的Java类名,或者一个类型别名(参见上面的表,用于内置类型别名列表)
property映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBean的属性,那么就会使用。否则MyBatis将会寻找给定名称property的字段。这两种情形可以使用通常点式的复杂属性导航。比如:可以这样映射一些东西:“username”,或者映射到一些复杂的东西: “address.street.number”
column来自数据库的类名,或重命名的列标签。这和通常传递给 resultSet.getString(columnName)方法的字符串是相同的
javaType一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名的列表)。 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。然而,如 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为
jdbcType在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 需要对插入, 更新和删除操作可能为空的列进行处理。这是 JDBC 的需要, jdbcType 而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定这个类型-但 仅仅对可能为空的值
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的 类型处理器。 这个属性值是类的完全限定名或者是一个类型处理器的实现, 或者是类型别名
select另一个映射语句的ID,它将加载该属性映射所需的复杂类型。从列属性中指定的列中检索到的值将传递给目标选择语句作为参数。更多的看到关联元素
resultMap这是一个ResultMap的ID,它可以将这个参数的嵌套结果映射到一个适当的对象图中。这是使用调用另一个select语句的另一种选择。它允许您将多个表加入到一个结果集中。这样的结果集将包含重复的、重复的数据组,这些数据需要被分解并正确映射到嵌套的对象图。为了方便这一点,MyBatis可以让您“链”结果映射到一起,以处理嵌套的结果

association(单个对象)

一个复杂类型的关联;许多结果将包装成这种类型。主要用来封装返回结果中的单个对象(字段)

嵌套结果映射 – 关联本身可以是一个resultMap元素,或者从别处引用一个。

<association property="">
	<id/>
	<result/>
</association>
  • 1
  • 2
  • 3
  • 4
<!--使用association定义关联的单个对象的封装规则 -->
<!--对应assoSelect -->
<association property="student" javaType="domain.Student">
	<result column="student_num" property="studentNum" />
	<result column="name" property="name" />
	<result column="phone" property="phone" />
	<result column="email" property="email" />
	<result column="sex" property="sex" />
</association>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
属性描述
property映射数据库列的字段或属性
colum数据库的列名或者列标签别名
javaType完整Java类名或别名
jdbcType支持的JDBC类型列表列出的JDBC类型。这个属性只在insert、update或delete的时候针对允许空的列有用
resultMap一个可以映射联合嵌套结果集到一个适合的对象视图上的ResultMap。这是一个替代的方式去调用另一个select语句
columnPrefix连接多个表时,必须使用列别名以避免结果集中出现重复的列名。通过指定columnPrefix,列映射到外部可以将此类resultMap
notNullColumn默认情况下,仅当映射到子对象属性的列中至少有一列为非空时,才会创建子对象。使用此属性,您可以通过指定哪些列必须具有值来更改此行为,以便仅当其中任何列不为空时,MyBatis才会创建子对象。可以使用逗号作为分隔符指定多个列名。默认值:未设置
fetchType可选。有效的价值观是懒惰和渴望的。如果存在,它将取代全局配置参数lazyLoadingEnabledfor这个映射
resultMap结果映射的ID,可以映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另外一个查询语句。这允许联合多个表来合成到resultMap一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis让"链接"结果映射,来处理嵌套结果

collection(集合)

一个复杂类型的集合
嵌套结果映射 – 集合本身可以是一个resultMap元素,或者从别处引用一个

<collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" > 
</collection>
  • 1
  • 2

注意:<collection>标签中的column:要传递给select查询语句的参数,如果传递多个参数,格式为column=”{参数名1=表字段1,参数名2=表字段2}” 。

<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>
  • 1
  • 2
  • 3
  • 4
  • 5

集合元素的作用几乎和关联是相同的。实际上,它们也很相似,文档的异同是多余的类中表现

private List posts;
  • 1

discriminator(鉴别器)

使用结果值来决定使用哪个resultMap
case:基于某些值的结果映射

嵌套结果映射 - case本身可以是一个resultMap元素,因此可以具有相同的结构和元素,或者从别处引用一个

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

有时一个单独的数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集。 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结构。 鉴别器非常容易理解,因为它的表现很像Java语言中的switch语句

定义鉴别器指定了column和javaType属性。列是MyBatis查找比较值的地方。javaType是需要被用来保证等价测试的合适类型(尽管字符串在很多情形下都会有用)

<resultMap id="vehicleResult" type="Vehicle">
	<id property="id" column="id" />
	<result property="vin" column="vin"/>
	<result property="year" column="year"/>
	<result property="make" column="make"/>
	<result property="model" column="model"/>
	<result property="color" column="color"/>
	
	<discriminator javaType="int" column="vehicle_type">
		<case value="1" resultMap="carResult"/>
		<case value="2" resultMap="truckResult"/>
		<case value="3" resultMap="vanResult"/>
		<case value="4" resultMap="suvResult"/>
	</discriminator>
</resultMap>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

MyBatis会从结果集中得到每条记录,然后比较它的vehicle类型的值。如果它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。换句话说。这样做完全是剩余的结果映射被忽略(除非它被扩展)。如果没有任何 一个实例相匹配,那么MyBatis仅仅使用鉴别器块外定义的结果映射

嵌套查询的N+1问题

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

<select id="selectBlog" resultMap="blogResult"> 加载博客
	SELECT * FROM BLOG WHERE ID = #{id}
</select>

<!-- 博客的结果映射描述了“selectAuthor”语句应该被用来加载它的author属性 -->
<select id="selectAuthor" resultType="Author">  加载作者
	SELECT * FROM AUTHOR WHERE ID = #{id}
</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

执行selectBlog方法
(1)先查询出博客(blog表)
(2)然后在根据博客表的作者id查询作者表

(1)对返回的每条记录,执行了一个查询语句来为每个加载细节(就是“N”)
(2)执行了一个单独的SQL语句来获取结果列表(就是“+1”)
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。这种方式很简单, 但是对于大型数据集合和列表将不会表现很好。问题就是熟知的“N+1查询问题”。会导致成百上千的SQL语句被执行。这通常不是期望的

MyBatis 能延迟加载这样的查询就是一个好处,因此可以分散这些语句同时运行的消耗。然而,如果加载一个列表,之后迅速迭代来访问嵌套的数据,会调用所有的延迟加载,这样的行为可能是很糟糕的

尽管嵌套查询大量的简化了存在关联关系的查询,但它的弊端也比较明显:即所谓的N+1问题。关联的嵌套查询显示得到一个结果集,然后根据这个结果集的每一条记录进行关联查询。

现在假设嵌套查询就一个(即resultMap内部就一个association标签),现查询的结果集返回条数为N,那么关联查询语句将会被执行N次,加上自身返回结果集查询1次,共需要访问数据库N+1次。如果N比较大的话,这样的数据库访问消耗是非常大的!所以使用这种嵌套语句查询的使用者一定要考虑慎重考虑,确保N值不会很大

以上面一对多(根据blog的id查询author)的例子为例,select 语句本身会返回user条数为1 的结果集,由于它存在有1条关联的语句查询,它需要共访问数据库 1*(1+1)=2次数据库

关联嵌套查询结果

嵌套语句的查询会导致数据库访问次数不定,进而有可能影响到性能。Mybatis还支持一种嵌套结果的查询:即对于一对多,多对多,多对一的情况的查询,Mybatis通过联合查询,将结果从数据库内一次性查出来,然后根据其一对多,多对一,多对多的关系和ResultMap中的配置,进行结果的转换,构建需要的对象

<resultMap type="domain.User" id="user_auto">
	<id column="id" property="id" />
	<result column="age" property="age" />
	
	<collection column="id" property="orders" ofType="domain.User_orders">
		<id column="order_id" property="id" />
		<result column="name" property="name" />
	</collection>
</resultMap>

<select id="findAuth" resultMap="user_auto">
	select u.id,u.age,o.id as order_id ,o.name,o.user_id as user_id from user u
	left outer join user_orders o on o.user_id = u.id
</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

嵌套结果查询的执行步骤:
1.根据表的对应关系,进行join操作,获取到结果集;
①根据结果集的信息和user 的resultMap定义信息,对返回的结果集在内存中进行组装、赋值,构造User;
②返回构造出来的结果List 结果

对于关联的结果查询,如果是多对一的关系,则通过形如 <association property=“user” column=“user_id” javaType=“domain.User” > 进行配置,Mybatis会通过column属性对应的user_id 值去从内存中取数据,并且封装成User_order对象;

如果是一对多的关系,就如User和User_order之间的关系,通过形如 <collection column=“id” property=“orders” ofType=“domain.User_orders”>进行配置,MyBatis通过 id去内存中取User_orders对象,封装成List;

对于关联结果的查询,只需要查询数据库一次,然后对结果的整合和组装全部放在了内存中


resultMap(输出结果映射)/paramterMap(输入参数)具体使用

当实体类中的属性和数据库中的字段不对应时,就要用resultMap和parameterMap
mybatis中可以使用resultMap完成高级输出结果映射。如果查询出来的列名和定义的pojo属性名不一致,就可以通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。然后使用resultMap作为statement的输出映射类型。resultMap可以实现将查询结果映射为复杂类型的pojo

比如:在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询

<!-- 将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 
			property: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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
<resultMap type="" id="">
	<!--
		id:唯一性,用于标示这JavaBean对象的唯一性,不一定会是数据库的主键(不要把它理解为数据库对应表的主键)
		property:对应JavaBean的属性名
		column:对应数据库表的列名,当JavaBean的属性与数据库对应表的列名不一致的时候,就能通过指定这个保持正常映射了
	-->
	<id property="" column="" />


	<!--  
		result与id相比, 对应普通属性
	-->
	<result property="" column="" />
	   
	<!--
		constructor对应javabean中的构造方法
		idArg 对应构造方法中的id参数
		arg 对应构造方法中的普通参数
	-->
	<constructor>
		<idArg column="" />
		<arg column="" />
	</constructor>
        
	<!--
          collection: 对应javabean中容器类型, 是实现一对多的关键
          property: 为javabean中容器对应字段名
          column: 为体现在数据库中列名
          ofType: 就是指定javabean中容器指定的类型
	-->
	<collection property="" column="" ofType=""></collection>

	<!--
         association: 为关联关系,是实现N对一的关键
         property: 为javabean中容器对应字段名
         column:为体现在数据库中列名
         javaType:指定关联的类型
	-->
	<association property="" column="" javaType=""></association>
  
</resultMap>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

自动映射

MyBatis可以自动映射查询结果。如果遇到复杂的场景,就需要构建一个resultmap

<setting name="autoMappingBehavior" value="PARTIAL"/>
  • 1

当自动映射查询结果时,MyBatis会获取sql返回的列名并在java类中查找相同名字的属性(忽略大小写)。意味着如果Mybatis发现了_ID_列和_id_属性,Mybatis会将_ID_的值赋给id。
通常数据库列使用大写单词命名,单词间用下划线分隔;而java属性一般遵循驼峰命名法。为了在这两种命名方式之间启用自动映射,需要将mapUnderscoreToCamelCase设置为true

自动映射级别

(1)NONE

禁用自动映射,只有手动映射属性才会被设置

(2)PARTIAL

将自动映射结果,除了那些嵌套结果映射(连接)内的结果
默认值是PARTIAL,这是有原因的。使用FULL时,将在处理连接结果时执行自动映射,并且
连接会检索同一行中的多个不同实体的数据,因此可能会导致不需要的映射

(3)FULL

自动映射一切
关于FULL模式,应该谨慎使用,该模式不管resultMap是嵌套的还是非嵌套的,都会进行自动映射,可能会造成某些嵌套属性与查询结果的字段名一致而误被自动映射


resultType和resultMap区别

都是用来表示查询结果集与java对象之间的一种关系,将查询结果集,按照某种关系映射到java对象

(1)resultType:将查询结果集中的各个列,一一映射到java对象中与列名一致的属性中。此处查询结果集到java对象的映射关系是固定的,只有列名和属性名相同,该列才能映射成功

(2)resultMap:将查询结果集中的列一一映射到java对象的各个属性上去。此处的这个映射关系,是根据用户在“resultMap”的子标签中的配置来决定的,灵活多变,常用于多表查询以及查询时使用别名的情况


parameterMap和parameterType区别

parameterMap其实和resultMap类似,映射关系灵活多变,但parameterMap并不被各个开发者推荐。相对的,parameterType则是比较常用,该项与resultType类似。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/625621
推荐阅读
相关标签
  

闽ICP备14008679号