热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析MyBatis的高级映射技术

在前一章节中,我们探讨了MyBatis的基本对象映射方法,其中对象属性与数据库表字段之间实现了直接的一对一映射。然而,在实际开发中,这种简单的映射方式往往难以满足复杂业务需求。本文将深入分析MyBatis的高级映射技术,介绍如何通过配置和注解实现更为灵活的对象与数据库表之间的映射关系,包括嵌套结果、联合查询和动态SQL等高级功能,以提高开发效率和代码可维护性。

上一章中实现的MyBatis对象映射较为简单,对象中的属性和数据库中的表字段是一一对应的(无论数量和名称都完全一样),如果对象中的属性名和表中的字段名不一致怎么办?又或者Java对象中存在复杂类型属性(即类似Hibernate中多对一、一对多关系对象时)怎么完成数据库表和对象的映射?本章来解决这样的问题。

1 MyBatis的数据映射规则

        MyBatis可以自动把查询到的表数据填充到对象中,这一过程是通过Java反射技术实现的,默认情况下,MyBatis会照查询出来的结果集字段名去填充对象的属性,因此数据库表中的字段名应该与对象的属性名相符合。但这个要求并不总是能保证。

1.1 使用查询别名映射对象属性

1)对象属性名和表字段名不一致时

        如果仅仅是数据库表中的字段名和Java对象的属性名不一致时,可以在select语句中指定查询字段的别名,别名与对象的属性名相同,MyBatis就可以对属性正确赋值了。

        例如有以下Java对象和数据库表Street:

  

 

        对象中的外键属性名为“districtId”而数据表中的外键字段名为“district_id”这时,可以编写以下SQL完成映射:



2)需要跨表查询并填充对象时

        比如我们希望在查询Movie的对象的同时,获取到Movie对应的外键表Category的Name值,因此我们在Movie对象中添加CategoryName属性。为了获取该值,我们可以使用表连接Join语句,并把Category的Name字段在查询中起别名为CategoryName以符合对象填充要求。

 

Movie表中只有CategoryId外键

 

 

Category表中有Name字段

 

对象中需要跨表获取数据

        针对上述需求,我们可以把MyBatis中的Movie查询按如下方式实现。



1.2 使用hashmap作为查询结果的返回类型

        如果从多个数据表中查询一些字段,无法填充到某一个实体中,我们还可以把resultType(返回结果类型)声明为hashmap,这时,查询到的每一行数据都会封装到一个HashMap集合中,键就是字段名,值就是字段值。


上述查询的执行代码如下:

public static void main(String[] args) throws IOException {SqlSession sess = MyBatisUtil.openSession();MovieMapper dao = sess.getMapper(MovieMapper.class);for(Map map : dao.getMoviesMap()){for(String key : map.keySet()){System.out.print(key+":"+map.get(key)+"\t");}System.out.println();}sess.close();}

执行结果如下所示:

2 SQL的重用

        映射配置文件中还有一个元素,用于声明可以被重用的sql语句块。例如上述Movie信息的连接查询语句,可能需要在多个 where m.id=#{id}

 

元素中用id声明该SQL语句块的名称,然后在元素中通过refid属性来应用它,这样就可以大大提高SQL语句的可维护性。

3SQL语句中传入多个参数

        实际应用中,SQL语句所需的参数往往不止一个。这时,我们可以把 where m.CategoryId=#{categoryId}limit #{skips},#{takes}

(1)使用命名查询方式时的参数传递

在执行的时侯,通过定义一个Map集合作为SQL参数,即可完成参数传递。

public List getMoviesPaging(int cid, int pageNum, int pageSize) {SqlSession session = MyBatisUtil.openSessionn();Map parameters = new HashMap();parameters.put("categoryId", cid);parameters.put("skips", (pageNum-1)*pageSize);parameters.put("takes", pageSize);try {return session.selectList(
"mycinema.dao.MovieDao.getMoviesPaging", parameters);} finally {session.close();} }

(2)使用Mapper方式时的参数传递

        使用Mapper方式时,只需声明接口就可以去调用select * from Movie where id=#{id}

使用resultType填充实体对象

Category属性为null

4.1 resultMap的使用

        如果希望一口气填充对象及其子对象,可以使用中resultMap属性指定的是一个名为的元素定义,是MyBatis中非常重要的元素,它完成了类似JDBC中从ResultSet往Java对象填充数据的过程。通过配置resultMap,可以实现任意复杂的Java对象的数据映射问题。

4.1.1 外键对象映射

(1)通过join关联

        下面的示例中:select语句使用了join把外键表相关数据一并查询了出来;通过resultMap元素,定义了查询结果字段与Java对象之间的映射填充关系。注意的是,resultMap中的子元素,声明了外键对象Category的填充细节。






以下是resultMap配置的重要子元素的解析:


子元素

作用

id

一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能。

result

注入到字段或 JavaBean 普通属性的普通结果

association

一个复杂的类型关联;许多结果将包成这种类型嵌入结果映射

collection

复杂类型的集嵌入结果映射

        上述示例中,association元素实现了Movie和Category对象的多对一关系。

4.1.2 外键集合(一对多)映射

(1)通过join关联

        与上述类似,如果现在的情况是Category对象中包含一个Movie对象的集合(如下代码所示),就需要使用中的子元素来描述集合属性映射。

Category对象中包含Movie对象的集合

public class Category {……public List movies;public List getMovies() { return movies; }public void setMovies(List movies) { this.movies = movies; }
}

元素中,property是对象中集合属性的属性名,ofType是集合元素类型(也就是一对多中多一方对象的类型)。



……

 

4.2 另一种外键映射方式:通过二次查询实现外键加载

1)外键对象的二次查询映射

        除了通过join的方式关联外键对象,还可通过二次查询的方式关联。也就是说,把主对象和外键对象的查询,分成两个独立查询来执行,通过主对象,找到外键ID,再根据外键ID查询外键对象,其具体配置如下所示。





 注意上述的配置,对于Movie的查询,只是单表查询,而resultMap的association元素,多提供了column(外键字段名)和select(使用外键值做关联二次查询)两个属性。其中,select的值“mycinema.dao.CategoryDao.fetchById”指的是CategoryMapper.xml配置文件中,根据id查询Category对象的select元素,如下所示:

……

上述这种方式,看起来配置方便一些,但是会造成1+N次查询的问题,实际使用应慎重考虑。下图是该查询的执行日志(log4j)输出的执行过程,从中可以看出,一个Movie对象的查询,使用了两条SQL语句。

2)外键集合的二次查询映射

        外键集合映射同样可以使用二次加载的方式。


 在上述的元素中,column属性是一对多关系中一方被外键引用的字段名(通常是主键字段名),select属性则是根据外键获取多方集合的查询名称,在上述列子中,这个查询应预先配置在MovieMapper.xml中,例如:


使用上述这种方式获取外键集合,同样会造成N+1次查询的问题,该查询的执行日志(log4j)如下:

        这种做法查询语句简单了,但可能换来了性能损耗。实践中往往难以两全其美,需要根据情况选择不同的方案。

 


推荐阅读
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 数据管理权威指南:《DAMA-DMBOK2 数据管理知识体系》
    本书提供了全面的数据管理职能、术语和最佳实践方法的标准行业解释,构建了数据管理的总体框架,为数据管理的发展奠定了坚实的理论基础。适合各类数据管理专业人士和相关领域的从业人员。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 利用存储过程构建年度日历表的详细指南
    本文将介绍如何使用SQL存储过程创建一个完整的年度日历表。通过实例演示,帮助读者掌握存储过程的应用技巧,并提供详细的代码解析和执行步骤。 ... [详细]
  • MySQL缓存机制深度解析
    本文详细探讨了MySQL的缓存机制,包括主从复制、读写分离以及缓存同步策略等内容。通过理解这些概念和技术,读者可以更好地优化数据库性能。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 在使用 DataGridView 时,如果在当前单元格中输入内容但光标未移开,点击保存按钮后,输入的内容可能无法保存。只有当光标离开单元格后,才能成功保存数据。本文将探讨如何通过调用 DataGridView 的内置方法解决此问题。 ... [详细]
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
author-avatar
不曾孤独_815
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有