MyBatis 关联映射
MyBatis 中对一对一,一对多的关联映射关系的配置方式是比较简单的,只需要在 XML 实体映射文件中进行相应的简单配置即可;
以下为示例代码中数据库的结构:
# 用户表
table user(
user_id primary key,
user_name,
user_eamil,
role_id refrences role(role_id)
)
# 角色表
table role(
role_id primary key,
role_name,
role_group
)
# 用户评论表
table comments(
comment_id primary key,
comment_content,
user_id references user(user_id)
)
其中 user 和 role 为一对一的关联关系;user 和 comments 为一对多的关联关系;
一对一映射
user 和 role 为一对一的关联关系,在 POJO User 的数据域中含有一个 Role,如下:
site.assad.domain.User
public class User implements Serializable{
private int id;
private String name;
private String email;
private Role role; //一对一关系关联映射对象属性
// getter,setter
}
site.assad.domain.Role
public class Role implements Serializable {
private int id;
private String name;
private String group;
// getter,setter
}
可以使用
<association> 标签来配置一对一的关联映射,该标签含有的属性如下:
- property:必填,对应 POJO 中的属性名称;
- javaType:属性对应的 Java 类型;
- resultMap:可以通过该属性直接使用现有的 resultMap;
- columnPrefix:查询列的前缀,配置该属性后,在子标签配置 result 的 column 时可以设置该前缀值;
- fetchType:抓取策略,可选值由 lazy(延迟抓取),eager(立即抓取);
以下是映射文件的一个示例的配置,
mapper/UserMapper.xml
<mapper namespace="site.assad.dao.UserDao">
<!--User 的基本实体映射-->
<resultMap id="userMap" type="site.assad.domain.User">
<id property="id" column="user_id" />
<result property="name" column="user_name" />
<result property="email" column="user_email" />
</resultMap>
<!--User 一对一关联属性 role 的属性映射 -->
<resultMap id="userRoleMap" extends="userMap" type="site.assad.domain.User">
<!--Role 相关属性映射-->
<association property="role" javaType="site.assad.domain.Role" >
<id property="id" column="role_id" />
<result property="name" column="role_name" />
<result property="group" column="role_group" />
</association>
</resultMap>
<!--示例一对一关联映射查询接口方法-->
<select id="getUserByIdWithRole" resultMap="userRoleMap">
select u.user_id, u.user_name, u.user_email,
r.role_id, r.role_name, r.role_group
from user u
inner join role r on u.role_id = r.role_id
where u.user_id = #{id}
</select>
</mapper>
在Dao接口 site.assad.dao.UserDao 中定义接口方法:
public interface UserDao {
User getUserByIdWithRole(int id);
}
以下是dao接口方法的测试代码:
public class UnitTest {
private UserDao userDao;
public void init() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
reader.close();
}
public void testGetUserByIdWithRole(){
User user = userDao.getUserByIdWithRole(1);
System.out.println(user);
}
}
一对多映射
user 和 comment 为一对多的关联关系,在 POJO User 数据域含有一个 Comment 类型的列表属性,如下:
site.assad.domain.User
public class User implements Serializable{
private int id;
private String name;
private String email;
private List<Comment> comments; //一对多的关联映射对象属性
// getter,setter
}
site.assad.domain.Comment
public class Comment implements Serializable{
private int id;
private String content;
// getter,setter
}
可以使用
<collection>
标签来配置一对一的关联映射,该标签含有的属性类似于 <association>:
- property:必填,对应 POJO 中的属性名称;
- ofType:集合元素的类型;
- resultMap:可以通过该属性为集合元素直接使用现有的 resultMap;
- columnPrefix:集合元素查询列的前缀,配置该属性后,在子标签配置 result 的 column 时可以设置该前缀值;
- fetchType:抓取策略,可选值有 lazy(延迟抓取),eager(立即抓取);
以下是映射文件的一个示例的配置,
mapper/UserMapper.xml
<mapper namespace="site.assad.dao.UserDao">
<!--User 的基本实体映射-->
<resultMap id="userMap" type="site.assad.domain.User">
<id property="id" column="user_id" />
<result property="name" column="user_name" />
<result property="email" column="user_email" />
</resultMap>
<!--User 一对多关联属性 commentList 的属性映射 -->
<resultMap id="userCommentsMap" extends="userMap" type="site.assad.domain.User">
<!--Comment 相关属性映射-->
<collection property="comments" ofType="site.assad.domain.Comment" fetchType="lazy">
<id property="id" column="comment_id" />
<result property="content" column="comment_content"/>
</collection>
</resultMap>
<!--示例一对多关联映射查询接口方法-->
<select id="getUserByIdWithComments" resultMap="userCommentsMap">
select u.user_id, u.user_name, u.user_email,
c.comment_id, c.comment_content
from user u
inner join comments c on u.user_id = c.user_id
where u.user_id = #{id}
</select>
</mapper>
在Dao接口 site.assad.dao.UserDao 中定义接口方法:
public interface UserDao {
User getUserByIdWithComments(int id);
}
以下是dao接口方法的测试代码:
public class UnitTest {
private UserDao userDao;
.....
public void testGetUserByIdWithComments(){
User user = userDao.getUserByIdWithComments(1);
List<Comment> comments = user.getComments();
comments.forEach(System.out::println);
}
}
集成配置示例
以上的 POJO User 中同时含有一对一关联属性,一对多关联属性,如下:
site.assad.domain.User
public class User implements Serializable{
private int id;
private String name;
private String email;
private Role role; //一对一关系关联映射对象属性
private List<Comment> comments; //一对多的关联映射对象属性
// getter,setter
}
可以使用以下比较简洁的 XML 配置方式,
mapper/UserMapper.xml:
<mapper namespace="site.assad.dao.UserDao">
<!--User 的基本实体映射-->
<resultMap id="userMap" type="site.assad.domain.User">
<id property="id" column="user_id" />
<result property="name" column="user_name" />
<result property="email" column="user_email" />
</resultMap>
<!--Role 的实体映射-->
<resultMap id="roleMap" type="site.assad.domain.Role">
<id property="id" column="role_id" />
<result property="name" column="role_name" />
<result property="group" column="role_group" />
</resultMap>
<!--Comment 的实体映射-->
<resultMap id="commentMap" type="site.assad.domain.Comment">
<id property="id" column="comment_id" />
<result property="content" column="comment_content"/>
</resultMap>
<!--User 完整实体映射,包含两个关联属性的映射-->
<resultMap id="userFullMap" type="site.assad.domain.User" extends="userMap">
<association property="role" resultMap="roleMap" />
<collection property="comments" resultMap="commentMap" fetchType="lazy"/>
</resultMap>
<!--查询接口方法配置-->
<select id="getUserById" resultMap="userFullMap">
select u.user_id, u.user_name, u.user_email,
r.role_id, r.role_name, r.role_group,
c.comment_id, c.comment_content
from user u
inner join role r on u.user_id = r.role_id
inner join comments c on u.user_id = c.user_id
where u.user_id = #{id}
</select>
</mapper>
之后再在 site.assad.dao.UserDao 接口中添加接口方法:
User getUserById(int id) 即可;
可以看到,以上的配置方式将 User、Role、Comment 的实体映射配置进一步独立处理,之后通过一个 resultMap 集成 userMap,并引用 roleMap,commentMap 的 resultMap 配置,这样的配置方式进一步提供了代码复用性;
在实际开发过程中,关于 Role,Comment 实体的配置往往不会编写在 UserMapper.xml ,而是编写在各自的实体映射 XML 中,如下:
mapper/RoleMapper.xml
<mapper namespace="site.assad.dao.RoleDao">
...
<resultMap id="roleMap" type="site.assad.domain.Role">
<id property="id" column="role_id" />
<result property="name" column="role_name" />
<result property="group" column="role_group" />
</resultMap>
....
mapper/CommentMapper.xml
<mapper namespace="site.assad.dao.CommentDao">
....
<resultMap id="commentMap" type="site.assad.domain.Comment">
<id property="id" column="comment_id" />
<result property="content" column="comment_content"/>
</resultMap>
....
此时在
mapper/UserMapper.xml 可以跨文件通过相应的接口类引用这些 resultMap,而不用为一个 POJO 维护多个基本 resultMap,如下:
....
<resultMap id="userMap" type="site.assad.domain.User">
<id property="id" column="user_id" />
<result property="name" column="user_name" />
<result property="email" column="user_email" />
</resultMap>
<resultMap id="getUserFull" type="site.assad.domain.User" extends="userMap">
<association property="role" resultMap="site.assad.dao.RoleDao.roleMap" />
<collection property="comments" resultMap="site.assad.dao.CommentDao.commentMap"/>
</resultMap>
....