MyBatis——使用 resultMap 配置一对一、一对多关系详解

MyBatis——使用resultMap 映射一对一、一对多问题详解


1. MyBaits简介

MyBatis 是一个优秀的持久层框架,它对 jdbc 的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需 要花费精力去处理例如注册驱动、创建 connection、创建 statement、手动设 置参数、结果集检索等 jdbc 繁杂的过程代码。MyBatis 通过 xml 或注解的方 式将要执行的各种 statement(statement、preparedStatemnt、 CallableStatement)配置起来,并通过 java 对象和 statement 中的 sql 进行 映射生成最终执行的 sql 语句,最后由 MyBatis 框架执行 sql 并将结果映射成 java 对象并返回。

与其他的对象关系映射框架不同,MyBatis 并没有将 Java 对象与数据库表关 联起来,而是将 Java 方法与 SQL 语句关联。MyBatis 允许用户充分利用数据 库的各种功能,例如存储过程、视图、各种复杂的查询以及某数据库的专有特 性。如果要对遗留数据库、不规范的数据库进行操作,或者要完全控制 SQL 的执行,MyBatis 是一个不错的选择。

2. 创建Mapper.xml文件需要注意的问题总结
  1. 创建Mapper.xml文件头部模板
<?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="path"> </mapper>

path填入Mapper文件的路径

创建一个新的 mapper ,需要首先给它取一个 namespace,这相当于是一个 分隔符,因为我们在项目中,会存在很多个 Mapper,每一个 Mapper 中都会 定义相应的增删改查方法,为了避免方法冲突,也为了便于管理,每一个 Mapper 都有自己的 namespace,而且这个 namespace 不可以重复。

  1. xxxMapper.xml和xxxMapper.java 的位置问题

注意,在 Maven 中,默认情况下,Maven 要求我们将 XML 配置、properties 配置等,都放在 resources 目录下,如果我们强行放在 java 目录下,默认情 况下,打包的时候这个配置文件会被自动忽略掉。对于这两个问题,我们有两种解决办法:

  • 不要忽略 XML 配置:
    我们可以在 pom.xml 中,添加如下配置,让 Maven 不要忽略我在 java 目录
    下的 XML 配置:
<build>
	<resources>
	 <resource>
			<directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
    </resource>
        <resource>
            <directory>src/main/resources</directory>
		</resource>
	</resources>
</build>
  • 按照 Maven 的要求来
    按照 Maven 的要求来,将 xml 文件放到 resources 目录下,但是,MyBatis 中默认情况下要求,xxxMapper.xml 和 xxxMapper 接口,必须放在一起, 所以,我们需要手动在 resources 目录下,创建一个和 xxxMapper 接口相同 的目录,这样,我们就不需要在 pom.xml 文件中添加配置了,因为这种写法同时满足 了 Maven 和 MyBatis 的要求。

  • **注意:在resources中创建相同目录时候,不能一次创建多级,只能一级一级的创建,虽然看上去是相同的,但是实际上是不一样的,如果一次性创建多级,编译之后的文件中xxxMapper.xml 和 xxxMapper 接口不会放在一起…(之前吃过大亏,每一次都手动拖拽编译文件…)

  1. application.properties 文件配置
//引入阿里巴巴的数据库连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
//设置驱动、端口号、数据库名、时区
spring.datasource.url=jdbc:mysql://localhost:3306/dataBaseName?serverTimezone=UTC

spring.datasource.username = root
spring.datasource.password = rootroot

server.port=8080
//日志级别 从小到大分别为 trace -> debug -> info -> warn -> error
logging.level.cn.stians.vblog.mapper=debug

3. MyBatis——resultMap 映射写法

  1. 一对一关系: 比如一本书,对应一个作者,对应的pojo如下:
public class Book {
    
    
 private Integer id; 
 private String name; 
 private Author author;//Book中装了一个作者
 	...
 	//省略get,set方法
 	...
 }
  • 根据id查询book
	Book getBookById(int id);
<?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="org.javaboy.mybatis.mapper.BookMapper">
<resultMap id="BookWithAuthor" type="org.javaboy.mybatis.model.Book">
	<id column="id" property="id"/>
	<result column="name" property="name"/>
	<!-- association 节点用来描述一对一关系-->
<association property="author" javaType="org.javaboy.mybatis.model.Author">
	<id column="aid" property="id"/>
	<result column="aname" property="name"/>
	<result column="aage" property="age"/> 
</association>
</resultMap>
 <select id="getBookById" resultMap="BookWithAuthor">
	SELECT b.*,a.`age` AS aage,a.`id` AS aid,a.`name` AS aname FRO
	M book b,author a WHERE b.`aid`=a.`id` AND b.`id`=#{id} 
</select>
</mapper>
  • 将公共的属性抽出来,可以减少重复的代码
<?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="org.javaboy.mybatis.mapper.BookMapper">
	<resultMap id="BaseResultMap" type="org.javaboy.mybatis.model.Book">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
</resultMap>

<!-- extends 节点用来继承上面的BaseResultMap这个公共的模板,这样就可以不需要写公共的属性了-->
<resultMap id="BookWithAuthor" type="org.javaboy.mybatis.model.Bo ok" extends="BaseResultMap">
<association property="author" javaType="org.javaboy.mybatis.model.Author">
	<id column="aid" property="id"/>
	<result column="aname" property="name"/>
	<result column="aage" property="age"/> </association>
</resultMap>
<select id="getBookById" resultMap="BookWithAuthor">
	SELECT b.*,a.`age` AS aage,a.`id` AS aid,a.`name` AS aname FRO
	M book b,author a WHERE b.`aid`=a.`id` AND b.`id`=#{id}
</select>
</mapper>
  • 使用懒加载

上面这种加载方式,是一次性的读取到所有数据。然后在 resultMap 中做映 射。如果一对一的属性使用不是很频繁,可能偶尔用一下,这种情况下,我们 也可以启用懒加载。懒加载,就是先查询 book,查询 book 的过程中,不去查询 author,当用户 第一次调用了 book 中的 author 属性后,再去查询 author。

  • 再提供一个方法
Book getBookById(Integer id); 
Author getAuthorById(Integer id);
<resultMap id="BaseResultMap" type="org.javaboy.mybatis.model.Book"> 
	<id column="id" property="id"/>
	<result column="name" property="name"/>
</resultMap>
<resultMap id="BookWithAuthor2" type="org.javaboy.mybatis.model.Book" extends="BaseResultMap">
	<association property="author" javaType="org.javaboy.mybatis.model.Author" select="org.javaboy.mybatis.mapper.BookMapper.getAuthorById" column="aid" fetchType="lazy"/>
</resultMap>
<select id="getBookById2" resultMap="BookWithAuthor2">
	 select * from book where id = #{id}; 
</select>
<select id="getAuthorById" resultType="org.javaboy.mybatis.model.Author">
	select * from author where id = #{aid}; 
</select>

这里,定义 association 的时候,不直接指定映射的字段,而是指定要执行的 方法,通过 select 字段来指定,column 表示执行方法时传递的参数字段,最 后的 fetchType 表示开启懒加载。
当然,要使用懒加载,还需在全局配置中开启:

<settings>
	<setting name="lazyLoadingEnabled" value="true"/> 
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>
  1. 一对多查询
    比如一个用户对应多个角色,对应的pojo类如下:
public class User {
    
    
	private Integer id; 
	private String username; 
	private String password; 
	private List<Role> roles;
	
	//省略get、set方法
	...
	}
  • 提供方法,根据id查询用户
	User getUserById(Integer id);
<resultMap id="UserWithRole" type="org.javaboy.mybatis.model.User"> <id column="id" property="id"/>
	<result column="username" property="username"/>
	<result column="password" property="password"/>
	<collection property="roles" ofType="org.javaboy.mybatis.model.Role">
		<id property="id" column="rid"/>
		<result property="name" column="rname"/> 
		<result property="nameZh" column="rnameZH"/>
	</collection>
</resultMap>
    
 <select id="getUserById" resultMap="UserWithRole">
	SELECT u.*,r.`id` AS rid,r.`name` AS rname,r.`nameZh` AS rnameZh
	FROM USER u,role r,user_role ur WHERE u.`id`=ur.`uid` AND ur.`rid`=r.`id` AND u.`id`=#{id}
</select>

在 resultMap 中,通过 collection 节点来描述集合的映射关系。在映射时,会自动将一的一方数据集合并,然后将多的一方放到集合中,能实现这一点,靠的就是 id 属性。

  • 这个一对多,也可以做成懒加载的形式,那我们首先提供一个角色查询 的方法:
	User getUserById(Integer id);	
	List<Role> getRolesByUid(Integer id);

然后,在 XML 文件中,处理懒加载

<resultMap id="UserWithRole" type="org.javaboy.mybatis.model.User"> <id column="id" property="id"/>
	<result column="username" property="username"/>
	<result column="password" property="password"/>
	<collection property="roles" select="org.javaboy.mybatis.mapper.UserMapper.getRolesByUid" column="id" fetchType="lazy">
	</collection>
</resultMap>

<select id="getUserById" resultMap="UserWithRole"> 
	select * from user where id=#{id};
</select>

<!--这里也可以写到Role类对象的mapper.xml中去-->
<select id="getRolesByUid" resultType="org.javaboy.mybatis.model.Role">
	SELECT r.* FROM role r,user_role ur WHERE r.`id`=ur.`rid` AND ur.`uid`=#{id}
</select>

猜你喜欢

转载自blog.csdn.net/weixin_43941676/article/details/108563953
今日推荐