前面章节介绍了一对一映射,本章节将介绍怎样实现一对多映射。
在 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}]}
            