【MyBatis】Mybatis 多表查询(一)一对一,一对多

首先我将实体类放出来,然后再探讨多表查询:
用户User:

package com.siyi.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

账户Account:

package com.siyi.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    private 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;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

实体类对应数据库种的user,account表。
表中列名和成员变量相同。

1. 一对一(or 多对一)

  • 需求
    查询所有账户信息,关联查询下单用户信息。
  • 注意:
    因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。

1.1 第一种方式(通过创建新的实体类包装数据)

第一种方法就是通过继承账户类,形成一个新的类,在新的类里面添加我们所需要的参数。
这样我们就可以按照之前的那些操作查询出我们需要的信息。

那么我们先看看新创建的实体类AccountUser :

package com.siyi.domain;

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 + '\'' +
                '}';
    }
}

定义账户的持久层 Dao 接口:

package com.siyi.dao;

import com.siyi.domain.Account;
import com.siyi.domain.AccountUser;

import java.util.List;

public interface IAccountDao {
   /**
     * 查询所有账户,并且带有用户名称和地址信息
     * @return
     */
    public List<AccountUser> findAllAccount();
}

定义 IAccountDao.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.siyi.dao.IAccountDao">
    <!-- 查询所有账户同时包含用户名和地址信息 -->
    <select id="findAllAccount" resultType="AccountUser">
        select a.*,u.username,u.address from account a,user u where u.id=a.uid;
    </select>
</mapper>

测试方法:

package com.siyi.test;

import com.siyi.dao.IAccountDao;
import com.siyi.domain.Account;
import com.siyi.domain.AccountUser;
import com.siyi.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class AccountTest {

    private InputStream in;
    private SqlSession session;
    private IAccountDao iAccountDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.获取SqlSession对象
        session = factory.openSession(true);
        //4.获取DAO的代理对象
        iAccountDao = session.getMapper(IAccountDao.class);
    }

    @After//用于在测试方法之后执行
    public void destroy() throws IOException {
        //事务提交
        //session.commit();
        //释放资源
        session.close();
        in.close();
    }
    
    /**
     * 测试查询所有账户,同时包含用户名称和地址
     */
    @Test
    public void testFindAllAccountUser(){
        List<AccountUser> accountUsers = iAccountDao.findAllAccount();
        for (AccountUser accountUser:accountUsers){
            System.out.println(accountUser);
        }
    }
}

这样我们获得的数据都是通过AccountUser类封装的。

定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

第二种方式(使用resultMap)

使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户是哪个用户的

修改 Account 类:(加入 User 类的对象作为属性)

package com.siyi.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    private 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;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

修改 IAccountDao 接口中的方法:

package com.siyi.dao;

import com.siyi.domain.Account;
import com.siyi.domain.AccountUser;

import java.util.List;

public interface IAccountDao {

    /**
     * 查询所有账户
     * @return
     */
    public List<Account> findAll();
}

注意:第二种方式,将返回值改 为了 Account 类型。
因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息。

重新定义 IAccountDao.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.siyi.dao.IAccountDao">

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
        <!-- 一对一的关系映射:配置封装user的内容 -->
        <association property="user" column="uid" javaType="User">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <result property="sex" column="sex"/>
            <result property="birthday" column="birthday"/>
        </association>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="accountUserMap">
        <!-- select * from account -->
        select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id=a.uid;
    </select>
</mapper>

测试方法:

package com.siyi.test;

import com.siyi.dao.IAccountDao;
import com.siyi.domain.Account;
import com.siyi.domain.AccountUser;
import com.siyi.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class AccountTest {

    private InputStream in;
    private SqlSession session;
    private IAccountDao iAccountDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.获取SqlSession对象
        session = factory.openSession(true);
        //4.获取DAO的代理对象
        iAccountDao = session.getMapper(IAccountDao.class);
    }

    @After//用于在测试方法之后执行
    public void destroy() throws IOException {
        //事务提交
        //session.commit();
        //释放资源
        session.close();
        in.close();
    }

    /**
     * 测试查询所有
     * @throws IOException
     */
    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<Account> accounts = iAccountDao.findAll();
        for (Account account : accounts) {
            System.out.println("------------------------");
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }
}

2. 一对多

  • 需求:
    查询所有用户信息及用户关联的账户信息。
  • 分析:
    用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。

根据上面第二种方法,我们可以试想一下,一对多?那不就是一个对象对应多个对象吗?
那我们可以在成员里面添加一个List集合不就行了。
那么我们改写User类:

package com.siyi.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    //一对多关系映射:主表实体应该包含从表实体的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

用户持久层 Dao 接口中加入查询方法:

package com.siyi.dao;

import com.siyi.domain.User;

import java.util.List;

/**
 * 用户持久层接口
 */
public interface IUSerDao {

    /**
     * 查询所有用户,同时获取到用户下所有账户的信息
     * @return
     */
    public List<User> findAll();
}

用户持久层 Dao 映射文件配置:

<?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.siyi.dao.IUSerDao">

    <!-- 定义User的resultMap -->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"/>
            <result property="uid" column="uid"/>
            <result property="money" column="money"/>
        </collection>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userAccountMap">
        <!-- select * from user; -->
        select * from user u left join account a on u.id=a.uid
    </select>
</mapper>
  • collection
    部分定义了用户关联的账户信息。表示关联查询结果集
  • property=“accounts”:
    关联查询的结果集存储在 User 对象的上哪个属性。
  • ofType=“account”:
    指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

测试方法:

package com.siyi.test;

import com.siyi.dao.IUSerDao;
import com.siyi.domain.Account;
import com.siyi.domain.AccountUser;
import com.siyi.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserTest {

    private InputStream in;
    private SqlSession session;
    private IUSerDao iuSerDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.获取SqlSession对象
        session = factory.openSession(true);
        //4.获取DAO的代理对象
        iuSerDao = session.getMapper(IUSerDao.class);
    }

    @After//用于在测试方法之后执行
    public void destroy() throws IOException {
        //事务提交
        //session.commit();
        //释放资源
        session.close();
        in.close();
    }

    /**
     * 测试查询所有
     * @throws IOException
     */
    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<User> users = iuSerDao.findAll();
        for (User user : users) {
            System.out.println("------------------------");
            System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }
}
发布了476 篇原创文章 · 获赞 152 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_41879343/article/details/104926033