MyBaits 中的 Mapper.xml 文件详解(下)

简介: MyBatis 的真真强大在于他的映射语句,这是它的魔力所在。由于它的异常强大,映射器的XML文件就显得相对简单。如果拿它跟具有相同功能的JDBC

关联 (One-To-One)


<!-- ResultMap -->
<resultMap id="blogAuthorMap" type="blog">
  <id property="id" column="id"/>
  <result column="name" property="name"/>
  <result column="title" property="title"/>
  <result column="content" property="content"/>
  <result column="author_id" property="authorId"/>
  <!-- 一对一关系映射,配置封装author的内容 -->
  <association property="author" javaType="author">
      <id property="id" column="id"/>
      <result property="username" column="username"/>
      <result property="password" column="password"/>
      <result property="email" column="email"/>
      <result property="blo" column="blo"/>
  </association>
</resultMap>
<!--  -->
<select id="findAll" resultMap="blogAuthorMap">
    select b.*, a.username, a.email, a.blo from blog b, author a where b.author_id = a.id
</select>


  • 关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。


  • 关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:


  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。


关联的嵌套 Select 查询


<resultMap id="blogAuthorMap" type="blog">
  <id property="id" column="id"/>
  <result column="name" property="name"/>
  <result column="title" property="title"/>
  <result column="content" property="content"/>
  <result column="author_id" property="authorId"/>
  <!-- 一对一关系映射,配置封装author的内容 -->
  <association property="author" column="author_id" javaType="author" select="findAuthorById"/> 
</resultMap>
<select id="findAuthorById" resultType="author">
    select * author where where id = #{id}
</select>
<select id="findBlogAll" resultMap="blogAuthorMap">
    select * from blog
</select>


  • 就是这么简单。我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性。


  • 其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。


  • 这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:


  • 你执行了一个单独的SQL语句来获取一个列表(就是 "+1")
  • 对列表返回的每一条记录,你执行了一个select 语句来为每条记录加载详细信息(这就是 "N")


  • 这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。


  • 好消息是,MyBatis 能够对这样的查询进行延迟加载,因此可以将大量语句同时运行的开销分散开来。 然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。


  • 所以我们建议使用上面中方式“关联的嵌套结果映射”


关联 (One-To-Many)


<!-- ResultMap -->
<resultMap id="authorBlogMap" type="author">
  <id column="id" property="id"/>
  <result column="username" property="username"/>
  <result column="password" property="password"/>
  <result column="email" property="email"/>
  <result column="blo" property="blo"/>
  <!-- 配置author用户中blog集合的映射 -->
  <collection property="blogList" ofType="blog">
      <!-- aid 是blog.id 的一个别名 -->
      <id column="aid" property="id"/>
      <result column="name" property="name"/>
      <result column="title" property="title"/>
      <result column="content" property="content"/>
      <result column="author_id" property="authorId"/>
  </collection>
</resultMap>
<!-- select -->
<select id="findAll" resultMap="authorBlogMap">
    select * from author a left outer join blog b on a.id = b.author_id
</select>


  • 上述我们代码示例中, 一个作者(Author), 有多篇博客。在作者类中可以通过如下方式来表示


/**
  * 用户博客列表
  */
private List<Blog> blogList;


关联 (Many-To-Many)


  • 由于MyBatis 并没有提供 多对多的实现方式,那么我们在设计中可以通过一对多来间接实现。如下例:


<!-- 博客关联博客分类 -->
<resultMap type="blog" id="resultMap">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="title" property="title"/>
    <result column="content" property="content"/>
    <!-- 配置分类集合 -->
    <collection property="classificationList" ofType="classification">
        <id column="c_id" property="id"></id>
        <result column="c_name" property="name"></result>
        <result column="remarks" property="remarks"></result>
    </collection>
</resultMap>
<!-- 查询博客列表中带有分类 -->
<select id="findAll" resultMap="resultMap">
select b.*,c.id as c_id,c.name as c_name,c.remarks from blog b
    left outer join blog_classification bc on b.id = bc.blog_id
    left outer join classification c on bc.classification_id = c.id
</select>


  • 上面的例子中, 一个博客(blog)可能会有多个分类,并且一个分类(blog_classification)下面会有多个博客信息,用博客分类映射表(blog_classification)来表示他们之间的关系,本处只是为了体现多对多的实现,在实际业务中对于这种查询方式并不值得借鉴


  • MyBatis 对关联映射,并没有深度,广度或组合上的要求。但是在映射时需要留意性能问题,在探索最佳实践的过程中,应用的单元测试和心梗测试会是你的好帮手,而MyBatis 的好处在于,可以在不对你的代码引入重大变更(如果有)的情况下,允许你之后改变你的想法。


  • 高级关联和集合映射是一个深度话题。文档的介绍只能到此为止。配合少许的实践,你会很快了解全部的用法。


自动映射


  • 正如你在前面一节看到的,在简单的场景下,MyBatis 可以为你自动映射查询结果。但如果遇到复杂的场景,你需要构建一个结果映射。 但是在本节中,你将看到,你可以混合使用这两种策略。让我们深入了解一下自动映射是怎样工作的。


  • 当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性。


  • 通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔;而 Java 属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。


  • 甚至在提供了结果映射后,自动映射也能工作。在这种情况下,对于每一个结果映射,在 ResultSet 出现的列,如果没有设置手动映射,将被自动映射。在自动映射处理完毕后,再处理手动映射。 在下面的例子中,id 和 userName 列将被自动映射,hashed_password 列将根据配置进行映射。


<select id="selectUsers" resultMap="userResultMap">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password
  from some_table
  where id = #{id}
</select>


<resultMap id="userResultMap" type="User">
  <result property="password" column="hashed_password"/>
</resultMap>


  • 三种自动映射等级:


  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射
  • FULL - 自动映射所有属性。


  • 默认值是 PARTIAL,这是有原因的。当对连接查询的结果使用 FULL 时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。 下面的例子将展示这种风险:


<select id="selectBlog" resultMap="blogResult">
  select
    B.id,
    B.title,
    A.username,
  from Blog B left outer join Author A on B.author_id = A.id
  where B.id = #{id}
</select>


<resultMap id="blogResult" type="Blog">
  <association property="author" resultMap="authorResult"/>
</resultMap>
<resultMap id="authorResult" type="Author">
  <result property="username" column="author_username"/>
</resultMap>


  • 在该结果映射中,Blog 和 Author 均将被自动映射。但是注意 Author 有一个 id 属性,在 ResultSet 中也有一个名为 id 的列,所以 Author 的 id 将填入 Blog 的 id,这可不是你期望的行为。 所以,要谨慎使用 FULL。


  • 无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。


<resultMap id="userResultMap" type="User" autoMapping="false">
  <result property="password" column="hashed_password"/>
</resultMap>


缓存


  • MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便的配置和定制。为了使它更加强大而且易于配置,MyBatis 3 中的缓存做了许多改进。


  • 默认情况下,值启用了本地会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存,只需要在SQL的配置文件中添加。


<cache />


  • 基本上是这样。这个简单的语句有如下的效果:


  • 映射语句中所的有 select 语句的结果集将被缓存。
  • 映射语句汇文件中的所有 insert、update、和 delete 语句会被刷新缓存。
  • 缓存会使用最近最少使用算法(LRU,Least Recently Used)算法来清除不需要的缓存
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)
  • 缓存会保存列表或对象(无论查询方法返回那种)的1024个引用
  • 缓存会被视为 读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不敢要其他调用者或者线程的潜在修改。


  • **缓存之作用与 cache 标签所在的映射文件的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句不会被默认缓存,你需要使用

@CacheNamespaceRef 注解指定缓存作用域


  • 这些属性可以通过 cache 元素的属性来修改。比如:


<cache
    eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"/>
>


  • 这个更干呕记得配置创建了一个FIFO缓存,每间隔60秒刷新,最多可以存储结果对象或者列表512个引用,并且返回单额对象被认为是只读的,因此对它们进行修改可能会在不同线程 中的调用者产生冲突 可用的清除策略有:


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


  • 默认的策略 LRU


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


  • size*(引用数目)属性可以被设置为任意正整数,要注意被缓存的对象大小和运行环境中可用的内存资源。默认值是 1024.


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


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


参考资料



相关文章
|
3月前
|
XML 前端开发 Java
讲解SSM的xml文件
本文详细介绍了SSM框架中的xml配置文件,包括springMVC.xml和applicationContext.xml,涉及组件扫描、数据源配置、事务管理、MyBatis集成以及Spring MVC的视图解析器配置。
92 1
|
5月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
2月前
|
XML Android开发 数据格式
Eclipse 创建 XML 文件
Eclipse 创建 XML 文件
33 2
|
2月前
|
Java Maven
maven项目的pom.xml文件常用标签使用介绍
第四届人文,智慧教育与服务管理国际学术会议(HWESM 2025) 2025 4th International Conference on Humanities, Wisdom Education and Service Management
214 8
|
3月前
|
XML JavaScript Java
java与XML文件的读写
java与XML文件的读写
35 3
|
3月前
|
XML 存储 缓存
C#使用XML文件的详解及示例
C#使用XML文件的详解及示例
142 0
|
3月前
|
XML 存储 Web App开发
查看 XML 文件
查看 XML 文件
|
4月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
78 1
|
6月前
|
XML Java 数据库
配置applicationContext.xml文件
配置applicationContext.xml文件
|
6月前
|
XML Java 数据格式
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法