搜索 K
Appearance
博客正在加载中...
Appearance
之前我们都是查询单表,现在我们来讲下查询多表的情况
表与表之间的关系有很多:
一对多,
多对一
一对一
多对多 我们举几个生活中的例子:
用户和订单就是一对多,订单和用户就是多对一,一个用户可以下多个订单,多个订单属于同一个用户
人和身份证号就是一对一,一个人只能有一个身份证号,一个身份证号只能属于一个人
老师和学生之间就是多对多,一个学生可以被多个老师教过,一个老师可以交多个学生
特例:如果拿出每一个订单,他都只能属于一个用户,所以 Mybatis 就把多对一看成了一对一。
我们通过户和账户之间的关系,来学习 Mybatis 如何实现多表查询。
一个用户可以有多个账户,一个账户只能属于一个用户,多个账户也可以属于同一个用户。
步骤:
package com.peterjxl.domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
}注:请读者自行生成 setter 和 getter 方法
package com.peterjxl.dao;
import com.peterjxl.domain.Account;
import java.util.List;
public interface IAccountDao {
List<Account> findAll();
}
<?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.peterjxl.dao.IAccountDao">
<select id="findAll" resultType="account">
select * from account
</select>
</mapper>
该测试类和我们之前的测试类,几乎一样,可以复制后修改下:
package com.peterjxl.test;
import com.peterjxl.dao.IAccountDao;
import com.peterjxl.domain.Account;
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 AccountTest {
private InputStream in;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory factory;
private SqlSession session;
private IAccountDao accountDao;
@Before
public void init() throws IOException {
// 1. 读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建SqlSessionFactory工厂
builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
// 3. 使用工厂生成SqlSession对象
session = factory.openSession();
// 4. 使用SqlSession创建Dao接口的代理对象
accountDao = session.getMapper(IAccountDao.class);
}
@After
public void destroy() throws IOException {
// 6. 释放资源
session.close();
in.close();
}
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
for (Account account : accounts){
System.out.println(account);
}
}
} 此时我们运行下测试方法,应该是正常的;
此时,我们就可以开始写多表查询的代码了。
首先,我们想要的效果是:查询所有账户,并获取到当前账户的所属用户信息(用户名和住址)。我们先写 SQL:
SELECT a.*, u.username, u.address
FROM account a, user u
WHERE u.id = a.UID 查询结果如下:
ID UID MONEY username address
1 46 1000 小七 北京
2 45 1000 赵六 北京
3 46 2000 小七 北京由于我们需要 Account 表的信息,并且还要用户名和住址,此时有一种方式就是,通过写一个 Account 类的子类,里面加上这两个成员变量,然后查询数据库并返回。
package com.peterjxl.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 + '\'' +
'}';
}
} 接口 IAccountDao 中新增方法:
/**
* 查询所有账户,并带有用户名称和地址信息
*/
List<AccountUser> findAccountUser(); 然后在 IUserDao.xml 中配置:
<!-- 查询所有账户,同时包含用户名和地址信息 -->
<select id="findAccountUser" resultType="accountUser">
SELECT a.*, u.username, u.address
FROM account a, user u
WHERE u.id = a.UID
</select>我们新建一个测试方法:
@Test
public void testFindAccountUser(){
List<AccountUser> accounts = accountDao.findAccountUser();
for (AccountUser account : accounts){
System.out.println(account);
}
} 运行结果:
Account{id=1, uid=46, money=1000.0} AccountUser{username='小七', address='北京'}
Account{id=2, uid=45, money=1000.0} AccountUser{username='赵六', address='北京'}
Account{id=3, uid=46, money=2000.0} AccountUser{username='小七', address='北京'} 可以看到能正常查询出来的。
为了实现多表查询,还得写一个子类,这太不方便,几乎不用,我们可以用其他方式来实现多表查询。
如何在类中体现类之间的关系呢?我们可以在 Account 中新建一个成员变量 user,因为 account 是 user 的从表
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
private User user;
}注:自行生成 getter 和 setter 。 当我们在查询 account 的时候,我们希望将 user 的信息封装到 user 中;此时我们需要定义一个封装 account 和 user 的 resultMap,我们在 IAccountDao.xml 里新建一个:
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!-- 建立一对一的关系映射,配置封装user的内容 -->
<association property="user" column="uid" javaType="user">
<id property="userId" column="id"/>
<result property="userName" column="username"/>
<result property="userAddress" column="address"/>
</association>
</resultMap>我们用 association 标签,表明一对一的映射,property 表明查询结果封装到 account 的哪个属性中, column 属性表面两表之间用什么字段关联,javatype 表明实体类对应的类名。然后标签里写要封装的 user 属性 我们修改 findAll 标签中,查询的 SQL:
<select id="findAll" resultMap="accountUserMap">
SELECT a.*, u.username, u.address
FROM account a, user u
WHERE u.id = a.UID
</select>我们在测试方法中,加上打印 user 属性的语句:
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
for (Account account : accounts){
System.out.print(account);
System.out.println(account.getUser());
}
}执行结果:
Account{id=1, uid=46, money=1000.0}User{userId=1, userName='小七', userBirthday=null, userSex='null', userAddress='北京'}
Account{id=2, uid=45, money=1000.0}User{userId=2, userName='赵六', userBirthday=null, userSex='null', userAddress='北京'}
Account{id=3, uid=46, money=2000.0}User{userId=3, userName='小七', userBirthday=null, userSex='null', userAddress='北京'} 可以看到能正常封装 userName 和 userAddress 属性
例如,我们想要在查询所有用户的时候,同时查询到用户下所有账户的信息;我们先定义 SQL
select u.*, a.ID aid, a.UID, a.MONEY
from user u LEFT JOIN account a
on u.id = a.UID我们需要在 User 类中添加一对多的映射,主表实体类应该包含从表实体类的集合引用;我们在 User 类中,新建一个成员变量 accounts
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
private List<Account> accounts;
}注:自行生成 getter 和 setter
<resultMap id="userAccountMap" type="user">
<id property="userId" column="id"/>
<result property="userName" column="userName"/>
<result property="userAddress" column="address"/>
<result property="userSex" column="sex"/>
<result property="userBirthday" column="birthday"/>
<!-- 配置user对象中account集合的映射 -->
<collection property="accounts" ofType="account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
</collection>
</resultMap>我们用 collection 属性表示查询一个集合出来,ofType 表示集合中元素的类型;然后里面写 account 表的列 然后我们将 IUserDao.xml 的 findAll 标签,修改 resultMap:
<select id="findAll" resultMap="userAccountMap">
select u.*, a.ID aid, a.UID, a.MONEY
from user u LEFT JOIN account a
on u.id = a.UID
</select> 修改 MybatisTest 中的测试方法,增加打印 accounts 的语句:
@Test
public void helloMybatis() throws Exception{
List<User> users = userDao.findAll();
for(User user : users){
System.out.print(user);
System.out.println(user.getAccounts());
}
}运行结果:可以看到 45 和 46 的用户,能成功查询出来 Accout 信息,其他用户则没有
本文所有代码已上传到了 GitHub 和 Gitee 上,并且创建了分支 demo14,读者可以通过切换分支来查看本文的示例代码。