【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());
}
}
}