Mybatis 中对于多表查询提供了非常强大的实现方式, 主要是通过 resultMap 的结果映射对于多表查询后的返回值进行封装, 让我们来看一下官网上对于 resultMap 的解释: resultMap 元素是 MyBatis 中最重要最强大的元素. 它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你进行一些 JDBC 不支持的操作. 实际上, 在为一些比如连接的复杂语句编写映射代码的时候, 一份 resultMap 能够代替实现同等功能的长达数千行的代码. ResultMap 的设计思想是, 对于简单的语句根本不需要配置显式的结果映射, 而对于复杂一点的语句只需要描述它们的关系就行了. 通过描述对象之间的关系将查询后的结果映射到我们定义的实体类中.
首先介绍一下本例中的实体类以及其映射关系, Demo 中存在 User 类以及 Account 类, 其关系为一个用户对应零个, 一个或者多个账户, 账户中为了简单单单保存用户的账户余额以及所属用户的 ID. 我们实现的查询的目标为: 每次查询一个账户的时候同时将其所属的用户信息也展示出来. 为了更好的帮助理解, 我们将展示一种非 mybatis 方式以及两种 mybatis 方式的实现来实现. User 类以及 Accoun 类 t 的内容如下:
- import java.io.Serializable;
- public class Account implements Serializable{
- private Integer id;
- private Integer uid;
- private Double money;
- private User user;
- public User getUser() {
- return user;
- }
- public void setUser(User user) {
- this.user = user;
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public Integer getUid() {
- return uid;
- }
- public void setUid(Integer uid) {
- this.uid = uid;
- }
- public Double getMoney() {
- return money;
- }
- public void setMoney(Double money) {
- this.money = money;
- }
- @Override
- public String toString() {
- return "Account{" +
- "id=" + id +
- ", uid=" + uid +
- ", money=" + money +
- '}';
- }
- }
- View Code
- import java.io.Serializable;
- import java.util.Date;
- public class User implements Serializable{
- private Integer id;
- private String username;
- private Date birthday;
- private String sex;
- private String address;
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public Date getBirthday() {
- return birthday;
- }
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
- public String getSex() {
- return sex;
- }
- public void setSex(String sex) {
- this.sex = sex;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- @Override
- public String toString() {
- return "User{" +
- "id=" + id +
- ", username='" + username + '\'' +
- ", birthday=" + birthday +
- ", sex='" + sex + '\'' +
- ", address='" + address + '\'' +
- '}';
- }
- }
- View Code
数据库的建表语句如下:
- DROP TABLE IF EXISTS user;
- CREATE TABLE user (
- id INT(11) NOT NULL auto_increment,
- username VARCHAR(32) NOT NULL COMMENT '用户名称',
- birthday datetime default NULL COMMENT '生日',
- sex char(1) default NULL COMMENT '性别',
- address varchar(256) default NULL COMMENT '地址',
- PRIMARY KEY (id)
- )ENGINE=InnoDB default CHARSET=utf8
- INSERT INTO `user` VALUES ('41', '老王', '2018-02-27 17:47:08', '男', '石家庄');
- INSERT INTO `user` VALUES ('45', '老李', '2018-02-27 17:47:08', '男', '石家庄');
- INSERT INTO `user` VALUES ('46', '老郭', '2018-02-27 17:47:08', '男', '石家庄');
- DROP TABLE IF EXISTS account;
- CREATE TABLE account(
- ID int(11) NOT NULL COMMENT '编号',
- UID INT(11) DEFAULT NULL COMMENT '用户编号',
- MONEY DOUBLE DEFAULT NULL COMMENT '金额',
- PRIMARY KEY (ID),
- KEY FK_Reference_8 (UID),
- CONSTRAINT FK_Reference_8 FOREIGN KEY (UID) REFERENCES user (id)
- )ENGINE=INNODB DEFAULT CHARSET=utf8
- INSERT INTO accountc (ID,UID,MONEY) VALUES (1,46,1000),(2,45,1000),(3,46,2000);
搭建项目的过程就不展示了, 主要的核心实体类和对应的数据库表如上, 接下来我们展示我们所要展示的三种方式实现一对一的联表查询.
1. 非 mybatis 的高级结果映射方式实现联表查询.
这种方式的原理为通过创建一个新的类 AccountUser 类继承 Account 类并在 AccountUser 类中添加我们想要查询的 User 的信息, 并且在账户查询的 Dao.xml 文件中配置相应的 sql 语句即可实现. 假如我们查询 Account 的信息的时候同时想要查询用户的名称以及地址, 那就在 AccountUser 的类中声明用户的名称以及地址. 这种实现方式只是作为一种拓展的实现方式, 在实际使用过程中并不推荐使用.
(1)声明 AccountUser 类
- public class AccountUser extends Account {
- private String username;
- private String address;
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- @Override
- public String toString() {
- return super.toString() + ""+"AccountUser{" +
- "username='" + username + '\'' +
- ", address='" + address + '\'' +
- '}';
- }
- }
- View Code
需要注意的是该类继承了 Account 类, 声明了我们需要的 User 类中的用户名称以及地址, 对 AccountUser 类的 toString()方法进行了改造, 添加了 super.toString(), 方便我们打印的时候可以打印出从父类中继承的属性的属性值.
(2)在 AccountDao 类中声明查询账户信息的方法
- /**
- * 查找所有账户同时包含用户的姓名和地址
- * @return
- */
- List<AccountUser> findAllAccountUser();
(3)在 AccountDao.xml 中配置 findAllAccountUser 方法的实现
- <select id="findAllAccountUser" resultType="com.example.domain.AccountUser">
- SELECT a.*,u.username,u.address FROM USER u,account a WHERE a.UID= u.id;
- </select>
(4)测试该方法
- @Test
- public void findAllAccounUsertTest(){
- List<AccountUser> accountList = accountDao.findAllAccountUser();
- for (AccountUser account:accountList){
- System.out.println(account);
- }
- }
测试结果:
2. 通过 Mybatis 中的高级结果映射的 resultMap 的关联属性 (association) 来实现多表的一对一查询.
关联属性主要用来处理 "有一个" 类型的关系, 关联的关键之处是我们需要告诉 MyBatis 如何加载关联. 在 MyBatis 中有两种不同的方式加载关联: 一是嵌套 Select 查询: 通过执行另外一个 SQL 映射语句来加载期望的复杂类型. 二是嵌套结果映射: 使用嵌套的结果映射来处理连接结果的重复子集. 通过这两种不同的方式衍生出两种不同的方式去实现多表的一对一查询.
1. 关联的嵌套 SELECT 查询
(1)因为我们要实现的是在查询账户的时候期望可以得到账户所属用户的某些信息, 所以我们需要在 Account 类中声明 User 对象, 用来将查询到的结果进行封装. 如下
- private User user;
- public User getUser() {
- return user;
- }
- public void setUser(User user) {
- this.user = user;
- }
(2)AccountDao 类中添加查询的方法的声明.
- /**
- * 查找所有账户同时包含用户的所有信息
- * @return
- */
- List<Account> findAll();
(3)在 AccountDao.xml 中配置 findAll 方法的的结果映射. 首先声明结果映射关系 resultMap,resultMap 的 id 为该结果映射的唯一标识, type 为结果类的完全限定名, resultMap 中的属性说明: id 和 result 元素都将一个列的值映射到一个简单数据类型 (String, int, double, Date 等) 的属性或字段. 这两者之间的唯一不同是, id 元素表示的结果将是对象的标识属性, 这会在比较对象实例时用到. 这样可以提高整体的性能, 尤其是进行缓存和嵌套结果映射 (也就是连接映射) 的时候. id 和 result 中的属性说明: property
映射到列结果的字段或属性, 其实就是实体类中属性的名称. column 是指数据库中的列名, 对应实体类的属性. 在下面的 < id property="id" column="aid"/>中的 column 属性的值 aid 没有完全匹配上数据中的 id, 是因为在查询语句中对 account 中的 id 字段设置了别名 aid.association 的属性的 property 为 user 对应实体类中声明的 user 对象, 其类型使用 javaType 属性指定为 User 类, column 为数据表的列名, 并作为参数传递给此 select 语句. select 属性用于加载复杂类型属性的映射语句的 ID, 它会从 column 属性中指定的列检索数据.
- <resultMap id="accountUserMap" type="com.example.domain.Account">
- <id property="id" column="aid"/>
- <result property="uid" column="uid"/>
- <result property="money" column="money"/>
- <!-- 关联的嵌套的 select 查询 -->
- <association property="user" javaType="com.example.domain.User" column="uid" select="selectUser"/>
- </resultMap>
(4)在 AccountDao.xml 中配置结果映射中的 < association property="user" javaType="com.example.domain.User" column="uid" select="selectUser"/>的 select="selectUser" 的实现以及 findAll 方法的实现,
- <select id="selectUser" resultType="user">
- SELECT * FROM USER WHERE ID = #{id};
- </select>
- <select id="findAll" resultMap="accountUserMap">
- SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
- </select>
这里我们有两个 select 查询语句: 一个用来加载账户信息(Account), 另外一个用来加载用户信息(User), 而且 accountUserMap 的结果映射描述了应该使用 selectUser 语句加载它的 user 属性, 其它的列名和属性名相匹配的属性将会被自动加载.
(5)查询测试
- @Test
- public void findAllTest(){
- List<User> userList = userDao.findAll();
- for (User user: userList){
- System.out.println(user);
- }
- }
测试结果:
2. 关联的嵌套结果映射实现 1.
(1)(2)步骤是上一方法是相同的.
(3)主要是修改了上一种方式中第三步中的 resultMap 中的 association 关联属性, 将其替换为:<association property="user" javaType="com.example.domain.User" column="uid" resultMap="userMap"/>, 在 association 中添加了 resultMap="userMap" 属性, userMap 为结果映射的 ID, 可以将此关联的嵌套结果集映射到一个合适的对象中, 也就是将关联属性 user 的结果映射到映射 ID 为 userMap 的 resultMap 中. userMap 的声明如下:
- <resultMap id="userMap" type="com.example.domain.User">
- <id property="id" column="id"/>
- <result property="username" column="username"/>
- <result property="birthday" column="birthday" jdbcType="DATE"/>
- <result property="sex" column="sex"/>
- <result property="address" column="address"/>
- </resultMap>
(4)AccountDao.xml 的 findAll 方法的映射则只需要 findAll 方法, 不再需要上一个方式中的 selectUser 映射的方法
- <select id="findAll" resultMap="accountUserMap">
- SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
- </select>
(5)(6)查询代码以及测试结果不再贴出
3. 关联的嵌套结果映射实现 2.
第二种实现方式中使用了外部的结果映射元素来映射关联. 这使得 User 的结果映射可以被重用. 然而, 如果我们不需要重用它(在上个例子中他是 userMap), 或者你更喜欢将你所有的结果映射放在一个具有描述性的结果映射元素中. 你可以直接将结果映射作为子元素嵌套在内.
(1)(2)步骤是上一方法是相同的.
(3)仍然是修改了上一种方式中第三步中的 resultMap 结果映射中的 association 关联属性, 将其替换如下:
- <!-- 关联的嵌套的结果映射 2-->
- <association property="user" javaType="com.example.domain.User">
- <id property="id" column="id"/>
- <result property="username" column="username"/>
- <result property="birthday" column="birthday" jdbcType="DATE"/>
- <result property="sex" column="sex"/>
- <result property="address" column="address"/>
- </association>
这样实现与第二种实现大同小异, 只是将关联对象的属性配置直接在 association 中进行了配置.
(4)AccountDao.xml 的 findAll 方法的映射的 findAll 方法
- <select id="findAll" resultMap="accountUserMap">
- SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
- </select>
(5)(6)查询代码以及测试结果不再贴出
总结: 通过上述例子可以初步窥探了 Mybatis 中多表联查 (一对一) 的使用方式, 主要是通过 resultMap 的高级结果映射来实现的, 在本例中最关键的属性是 resultMap 的关联属性 association,association 也是我们告诉 Mybatis 对象之间的关系的桥梁, 同时也介绍了 resultMap 的属性的说明, 通过解释其属性再加上 Demo 可以更好的理解结果映射的含义以及使用, 这只是最简单的一种使用方式, 以后会详细介绍一对多, 多对多, 多对一等复杂情况在 Mybatis 中的如何查询映射.
参考网址: mybatis 中文官网 http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
来源: https://www.cnblogs.com/hopeofthevillage/p/11406649.html