MyBatis映射
映射方式
- 接口+XML
- 先定义接口映射器,然后再定义xml映射器,其中xml映射器的namespace应该对应接口映射器的类名。
- 此种⽅法 要求Mapper接⼝名称 和 Mapper映射⽂件名称相同 ,且放在同⼀个⽬录中
- 如果放在Resources文件夹需要创建与java路径下相同的包路径
- 接口+注解
- 此类形式,将原先xml里面的sql配置信息,变成Java注解的形式写到接口映射器
Select
执行查询操作
例子:查找用户表,返回用户人数
1 | <select id="countUser" resultType="int"> |
属性
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
---|---|
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以根据语句中实际传入的参数计算出应该使用的类型处理器(TypeHandler),默认值为未设置(unset)。 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache | 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
fetchSize | 这是一个给驱动的建议值,尝试让 驱动程序 每次批量返回的结果行数 等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句:如果为 true,则假设结果集以正确顺序(排序后)执行映射,当返回新的主结果行时,将不再发生对以前结果行的引用。 这样可以减少内存消耗。默认值:false 。 |
resultSets | 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
affectData | Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see Transaction Control Method. Default: false (since 3.5.12) |
Insert
执行插入操作
例子:插入一条用户数据,参数为用户实体类型
1 | <insert id="insertUser" parameterType="User"> |
Update
执行更新操作
例子:更新id为1的用户的数据,参数为用户实体类型
1 | <update id="updateUser" parameterType="User"> |
Delete
执行删除操作
例子:删除id为1的用户
1 | <delete id="deleteUserById"> |
属性(增删改)
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
---|---|
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以根据语句中实际传入的参数计算出应该使用的类型处理器(TypeHandler),默认值为未设置(unset)。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | 这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。(仅适用于 insert 和 update) |
keyProperty | 指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。(仅适用于 insert 和 update) |
keyColumn | 设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。(仅适用于 insert 和 update) |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
sql
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。
使用sql标签声明,id用来唯一确定一个sql,内容可以使用可变量 ${value}
1 | <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql> |
使用sql时,通过inclue标签引入一个sql,refid 指定要引用的sql id,内容可以设置sql中定义的可变量
1 | <select id="selectUsers" resultType="map"> |
参数
参数类型(parameterType)会被自动设置为 int
参数 即在sql语句中插入可变量#{value} , 方法中的参数会自动替换可变量
当在sql中使用可变量时,预处理状态语句就会生成一个参数占位符?,然后用方法对应的的形参替换
安全的
字符串替换
字符串替换就是使用${value} 替换sql语句中的字符串,是直接替换,不会在预处理语句生成占位符
不安全,可能会导致sql注入
resultMap
基础
结果映射,用于将数据库中的结果映射到返回值
resultMap的属性
属性 | 描述 |
---|---|
id |
当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
type |
类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。 |
autoMapping |
如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 |
隐式resultMap
- 当结果集自动映射JavaBean或者集合,会自动创建隐式的resultMap
- 如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名
显式resultMap
- 声明了一个结果集,id 为 userResultMap 用于唯一确定一个结果集,返回类型为User
- id 标签表示主键映射,property是指Java中的成员变量名,column 是指数据库的列名
- result 标签表示普通映射,property是指Java中的成员变量名,column 是指数据库的列名
1 | <resultMap id="userResultMap" type="User"> |
使用显示resultMap
使用显示resultMap 需要 指定resultMap属性的值为对应的自定义结果映射的id
1 | <select id="selectUsers" resultMap="userResultMap"> |
resultMap 内部标签
- constructor 用于在实例化类时,注入结果到构造方法中
- idArg ID 参数,标记出作为 ID 的结果可以帮助提高整体性能
- arg 将被注入到构造方法的一个普通结果
- id ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result 注入到字段或 JavaBean 属性的普通结果
- association 映射到一个类对象
- collection 映射到一个集合
- discriminator 使用结果值来决定使用哪个 resultMap/resultType
- 在特定的情况下使用不同的pojo进行关联, 鉴别器元素就是被设计来处理这个情况的。
- 鉴别器非常容易理解,因为它的表现很像 Java 语言中的 switch 语句
- discriminator 标签常用的两个属性如下:
- column:该属性用于设置要进行鉴别比较值的列 。
- javaType:该属性用于指定列的类型,保证使用相同的 Java 类型来比较值。
- discriminator 标签可以有1个或多个 case 标签
- case 标签包含以下三个属性 :
- value : 该值为 discriminator 指定 column 用来匹配的值 。
- resultMap : 当column的值和 value 的值匹配时,可以配置使用resultMap指定的映射,resultMap优先级高于 resultType
- resultType : 当 column 的值和 value 的值匹配时,用于配置使用 resultType指定的映射。
例子:一个博客结果映射
1 | <!-- 非常复杂的结果映射 --> |
id(id结果)
1 | <id property="id" column="post_id"/> |
- 将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
- id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。
- 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候
result(结果)
1 | <id property="id" column="post_id"/> |
- 将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
id 和 result 的属性
属性 | 描述 |
---|---|
property |
映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 |
column |
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType |
一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
typeHandler |
这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。使用这个属性,你可以覆盖默认的类型处理器。 |
constructor(构造注入)
1 | <constructor> |
- 构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。
- 需按照顺序注入
属性 | 描述 |
---|---|
column |
数据库中的列名,或者是列的别名。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型, 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。 |
typeHandler |
这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。使用这个属性,你可以覆盖默认的类型处理器。 |
select |
用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。具体请参考关联元素。 |
resultMap |
结果映射的 ID,可以将嵌套的结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet 。这样的 ResultSet 将会将包含重复或部分数据重复的结果集。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许你 “串联”结果映射,以便解决嵌套结果集的问题。想了解更多内容,请参考下面的关联元素。 |
name |
构造方法形参的名字。从 3.4.3 版本开始,通过指定具体的参数名,你可以以任意顺序写入 arg 元素。参看上面的解释。 |
association(关联映射)
关联(association)元素处理“有一个”类型的关系。
比如一个博客有一个用户。
属性 | 描述 |
---|---|
property |
映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名。 |
jdbcType |
JDBC 类型,只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。 |
typeHandler |
这个属性值是一个 类型处理器实现类的完全限定名,或者是类型别名。使用这个属性,你可以覆盖默认的类型处理器。 |
column |
数据库中的列名,或者是列的别名。嵌套查询时作为参数 |
select |
用于加载复杂类型属性 的 映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 |
fetchType |
可选的。有效值为 lazy 和 eager 。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled ,使用属性的值。 |
resultMap |
结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet 。 |
columnPrefix |
当连接多个表时,你可能会不得不使用列别名来避免在 ResultSet 中产生重复的列名。指定 columnPrefix 列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。 |
notNullColumn |
默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列中任意一列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。 |
autoMapping |
如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。 |
关联加载方式(重点)
嵌套 Select
通过 执行另外一个 SQL 映射语句 来加载期望的复杂类型。
例子:
1 | <!--主查询--> |
嵌套 Select 在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。
- 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
- 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。
嵌套resultMap
使用 嵌套的结果映射 来处理连接结果的重复子集。
外部resultMap(可复用)
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/> </resultMap> <!--外部resultMap--> <resultMap id="authorResult" type="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 内联resultMap
- ```xml
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<!--内联resultMap-->
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</association>
</resultMap>
collection(集合映射)
类似于association,只不过collection返回的是多条数据,映射到集合中
不同之处在于
javaType=”ArrayList” 变成了集合实现类,一般可以省略
ofType=”Post” 新增了一个属性,用于指定集合内元素类型
嵌套select
1 | <select id="selectBlog" resultMap="blogResult"> |
嵌套resultMap
外部resultMap
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="id"/> <result property="subject" column="subject"/> <result property="body" column="body"/> </resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 内联resultMap
- ```xml
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/>
</resultMap>
<resultMap id="blogPostResult" type="Post">
<id property="id" column="id"/>
<result property="subject" column="subject"/>
<result property="body" column="body"/>
</resultMap>
discriminator(鉴别器)
有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。
discriminator 元素就是被设计来应对一个数据库查询可能会返回多个不同的结果集这种情况,它很像 Java 语言中的 switch 语句
重要属性:
- column 指定了 MyBatis 查询被比较值的地方,即使用那一列来比较
- javaType 用来确保使用正确的相等测试
例子:在这个示例中,MyBatis 会从结果集中得到每条记录,然后比较它的 vehicle type 值。
如果它匹配任意一个鉴别器的 case,就会使用这个 case 指定的结果映射。
1 | <resultMap id="vehicleResult" type="Vehicle"> |
如果不能匹配任何一个 case,MyBatis 就只会使用鉴别器块外定义的结果映射。
1 | <resultMap id="carResult" type="Car"> |
只有 doorCount 属性会被加载,在上面的例子中,我们当然知道 cars 和 vehicles 之间有关系,也就是 Car 是一个 Vehicle。因此,我们希望剩余的属性也能被加载,使用 extends 继承一个resultMap
1 | <resultMap id="carResult" type="Car" extends="vehicleResult"> |
风格2
1 | <discriminator javaType="int" column="vehicle_type"> |
cache
缓存分类
- 一级缓存
- 默认开启,它仅仅对一个会话中的数据进行缓存。
- 本地的会话SqlSession缓存
- 二级缓存
- 在需要开启的的 SQL 映射文件中添加
<cache/>
- 映射文件级别的缓存,命名空间级别的缓存
- 在需要开启的的 SQL 映射文件中添加
二级缓存的作用
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
cache标签的属性
属性 | 作用 |
---|---|
eviction | 清除策略 |
flushInterval | 刷新间隔,属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。 |
size | 引用数目,属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。 |
readonly | 只读,属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。 |
二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
使用自定义缓存
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
1 | <cache type="com.domain.something.MyCustomCache"/> |
缓存共享
对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 但你可能会想要在多个命名空间中共享相同的缓存配置和实例。
可以使用 cache-ref 元素来引用另一个命名空间(mapper)的缓存。
1 | <cache-ref namespace="com.someone.application.data.SomeMapper"/> |