前面章节介绍了一对一映射,本章节将介绍怎样实现一对多映射。
在 MyBatis 中,使用 <resultMap> 配合 <collection> 标签实现一对多映射。<collection> 标签 DTD 定义如下:
<!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)> <!ATTLIST collection property CDATA #REQUIRED column CDATA #IMPLIED javaType CDATA #IMPLIED ofType CDATA #IMPLIED jdbcType CDATA #IMPLIED select CDATA #IMPLIED resultMap CDATA #IMPLIED typeHandler CDATA #IMPLIED notNullColumn CDATA #IMPLIED columnPrefix CDATA #IMPLIED resultSet CDATA #IMPLIED foreignColumn CDATA #IMPLIED autoMapping (true|false) #IMPLIED >
根据上面 DTD 得知,<collection> 标签下允许使用 <constructor>、<id>、<result>、<association>、<collection> 和 <discriminator> 标签。该标签也定义了很多属性,每个属性含义如下:
property:映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column:数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType:一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
ofType:完整java类名或别名(集合所包括的类型)
jdbcType:JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
select:用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。具体请参考关联元素。
resultMap:结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的 ResultSet 有部分数据是重复的。 为了将结果集正确地映射到嵌套的对象树中, MyBatis 允许你“串联”结果映射,以便解决嵌套结果集的问题。使用嵌套结果映射的一个例子在表格以后。
typeHandler:指定自定义类型处理器
notNullColumn:默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。
columnPrefix:当连接多个表时,你可能会不得不使用列别名来避免在 ResultSet 中产生重复的列名。指定 columnPrefix 列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。 详细说明请参考后面的例子。
resultSet:这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。
foreignColumn:指定外键对应的列名,指定的列将与父类型中 column 的给出的列进行匹配。
autoMapping:如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。
示例代码如下:
<resultMap id="ormUser" type="com.hxstrive.mybatis.orm.demo1.ORMUser"> <id column="user_id" jdbcType="INTEGER" property="userId" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="sex" jdbcType="VARCHAR" property="sex" /> <result column="age" jdbcType="INTEGER" property="age" /> <result column="salary" jdbcType="DOUBLE" property="salary" /> <result column="borthday" jdbcType="DATE" property="borthday" /> <!-- 映射关联的List --> <collection column="user_id" property="contactList" ofType="com.hxstrive.mybatis.orm.demo1.ORMContact"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="user_id" jdbcType="INTEGER" property="userId" /> <result column="usage" jdbcType="VARCHAR" property="usage" /> <result column="number" jdbcType="VARCHAR" property="number" /> </collection> </resultMap>
上面示例中,<collection> 标签将根据“user_id”列到 user_concat 表中查找指定用户的所有联系方式,然后将查询结果映射到 contactList 属性。
(1)MyBatis 配置文件 mybatis-cfg.xml 内容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="database.properties"/> <environments default="MySqlDatabase" > <environment id="MySqlDatabase" > <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/hxstrive/mybatis/orm/demo1/ORMMapper.xml"/> </mappers> </configuration>
(2)定义 JavaBean 实体
a、ORMUser.java
package com.hxstrive.mybatis.orm.demo1; import java.util.Date; import java.util.List; public class ORMUser { private Integer userId; private String name; private String sex; private Integer age; private Double salary; private Date borthday; private byte[] face; private List<ORMContact> contactList; // 忽略 getter 和 setter @Override public String toString() { return "ORMUser{" + "userId=" + userId + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + ", salary=" + salary + ", borthday=" + borthday + ", contactList=" + contactList + '}'; } }
b、ORMContact.java
package com.hxstrive.mybatis.orm.demo1; public class ORMContact { private Integer id; private Integer userId; private String usage; private String number; private ORMUser user; // 忽略 getter 和 setter @Override public String toString() { return "ORMContact{" + "id=" + id + ", userId=" + userId + ", usage='" + usage + '\'' + ", number='" + number + '\'' + ", user=" + user + '}'; } }
(3)定义 Mapper 接口和配置文件
a、ORMMapper.java
package com.hxstrive.mybatis.orm.demo1; import java.util.List; public interface ORMMapper { List<ORMUser> joinSelect01(); }
b、ORMMapper.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="com.hxstrive.mybatis.orm.demo1.ORMMapper"> <!-- 一对多映射 --> <resultMap id="ormUser" type="com.hxstrive.mybatis.orm.demo1.ORMUser"> <id column="user_id" jdbcType="INTEGER" property="userId" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="sex" jdbcType="VARCHAR" property="sex" /> <result column="age" jdbcType="INTEGER" property="age" /> <result column="salary" jdbcType="DOUBLE" property="salary" /> <result column="borthday" jdbcType="DATE" property="borthday" /> <!-- 映射关联的List --> <collection column="user_id" property="contactList" ofType="com.hxstrive.mybatis.orm.demo1.ORMContact"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="user_id" jdbcType="INTEGER" property="userId" /> <result column="usage" jdbcType="VARCHAR" property="usage" /> <result column="number" jdbcType="VARCHAR" property="number" /> </collection> </resultMap> <select id="joinSelect01" resultMap="ormUser"> select a.`user_id`, a.`name`, a.`sex`, a.`age`, a.`salary`, a.`borthday`, b.`id`, b.`user_id`, b.`usage`, b.`number` from `user` a join `user_contact` b on a.`user_id`=b.`user_id` </select> </mapper>
(4)客户端代码如下:
package com.hxstrive.mybatis.orm.demo1; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; import java.util.List; public class ORMDemo { public static void main(String[] args) throws Exception { String cfgName = "com/hxstrive/mybatis/orm/demo1/mybatis-cfg.xml"; InputStream input = Resources.getResourceAsStream(cfgName); SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlFactory = factoryBuilder.build(input); SqlSession sqlSession = sqlFactory.openSession(true); ORMMapper ormMapper = sqlSession.getMapper(ORMMapper.class); List<ORMUser> userList = ormMapper.joinSelect01(); for ( ORMUser user : userList ) { System.out.println(user); } } }
运行客户端代码,输出结果如下:
2020-09-15 21:59:09,215 DEBUG [org.apache.ibatis.logging.LogFactory] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 2020-09-15 21:59:09,244 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-15 21:59:09,244 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-15 21:59:09,244 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-15 21:59:09,244 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-15 21:59:09,401 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Opening JDBC Connection 2020-09-15 21:59:09,877 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 250370634. 2020-09-15 21:59:09,879 DEBUG [com.hxstrive.mybatis.orm.demo1.ORMMapper.joinSelect01] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@eec5a4a] 2020-09-15 21:59:09,880 DEBUG [com.hxstrive.mybatis.orm.demo1.ORMMapper.joinSelect01] - ==> Preparing: select a.`user_id`, a.`name`, a.`sex`, a.`age`, a.`salary`, a.`borthday`, b.`id`, b.`user_id`, b.`usage`, b.`number` from `user` a join `user_contact` b on a.`user_id`=b.`user_id` 2020-09-15 21:59:10,048 DEBUG [com.hxstrive.mybatis.orm.demo1.ORMMapper.joinSelect01] - ==> Parameters: 2020-09-15 21:59:10,143 DEBUG [com.hxstrive.mybatis.orm.demo1.ORMMapper.joinSelect01] - <== Total: 5 ORMUser{userId=1, name='赫仑', sex='男', age=27, salary=7800.0, borthday=Wed Jun 04 00:00:00 CST 1902, contactList=[ORMContact{id=1, userId=1, usage='手机号码', number='15787782291', user=null}]} ORMUser{userId=2, name='张小凡', sex='男', age=30, salary=8670.0, borthday=Wed Aug 22 00:00:00 CDT 1990, contactList=[ORMContact{id=2, userId=2, usage='家庭号码', number='028-82234543', user=null}]} ORMUser{userId=3, name='叶星云', sex='女', age=31, salary=6890.0, borthday=Mon Feb 27 00:00:00 CST 1989, contactList=[ORMContact{id=3, userId=3, usage='工作号码', number='028-78675599', user=null}, ORMContact{id=4, userId=3, usage='生活号码', number='15198892234', user=null}, ORMContact{id=5, userId=3, usage='私密电话', number='15882334337', user=null}]}