多表查询举例
- 一对一,人&身份证
- 一对多,人&银行账户
- 多对多,老师&学生
PS:在mybatis中,把多对一看成一对一
一对一查询
需求:每查询一个账户时,同时查询出他的用户信息
分析:确定用户和账户之间的关系,一个账户只能属于一个用户,账户和用户之间是多对一,在MyBatis中没有多对一,认定为一对一。
方式一
方式一的核心是新建一个类,该类的属性包含所有要查询的两个表的列,用于配置封装结果。
数据准备
表1,account(用户表)
表2,account(账户表)
编写查询的sql语句
select a.*,u.username,u.address from account a,user u where a.uid = u.id
新建实体类Account
package com.cncs.domain;
public class Account {
private int id;
private int uid;
private Double money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUid() {
return uid;
}
public void setUid(int 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 +
'}';
}
}
新建实体类User
package com.cncs.domain;
import java.util.Date;
public class User {
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int 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;
}
}
新建实体类AccountUser
package com.cncs.domain;
public class AccountUser extends Account {
private String username;
private String address;
@Override
public String toString() {
return "Account:"+super.toString()+ "AccountUser{" +
"username='" + username + '\'' +
", address='" + 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;
}
}
新建持久层接口IAccountDao
package com.cncs.dao;
import com.cncs.domain.AccountUser;
import java.util.List;
public interface IAccountDao {
/**
* 查询所有账号,同时查询他们的用户信息
* @return
*/
List<AccountUser> findAll();
}
新建持久层接口IUserDao
package com.cncs.dao;
import com.cncs.domain.User;
import java.util.List;
public interface IUserDao {
/**
* 查询所有
* @return
*/
List<User> findAll();
}
编辑MyBatis核心配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置properties-->
<properties resource="jdbcConfig.properties"></properties>
<!-- <properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>-->
<!--使用typeAliases配置别名,它只能配置domain中的别名-->
<typeAliases>
<!--type指定的是实体类的全限定类名,alias指定的是别名,别名一旦指定后,使用时不区分大小写。-->
<!--<typeAlias type="com.cncs.domain.User" alias="user"/>-->
<!--name属性指定要配置别名的包后,该包下的所有实体类都将注册别名,并且别名就是类名,使用时不区分大小写-->
<package name="com.cncs.domain"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<!--配置事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--指定每个映射配置文件的位置-->
<mappers>
<!--<mapper resource="com/cncs/dao/IUserDao.xml"/>-->
<!--package标签用于指定dao接口所在的包,当指定完后,mybatis就可以找到和接口对应的映射配置文件,也就替代了mapper标签-->
<package name="com.cncs.dao"></package>
</mappers>
</configuration>
新建数据库连接信息配置文件jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://localhost:3306/mybatis_learn
#jdbc连接数据url简写方式: jdbc:mysql:///数据库名
jdbc.url=jdbc:mysql:///mybatis_learn
jdbc.username=root
jdbc.password=123456
新建映射配置文件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.cncs.dao.IAccountDao">
<!-- 查询所有-->
<select id="findAll" resultType="accountUser">
select a.*,u.username,u.address from account a,user u where a.uid = u.id
</select>
</mapper>
新建映射配置文件IUserDao.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.cncs.dao.IUserDao">
<!-- 查询所有-->
<select id="findAll" resultType="user">
select * from user
</select>
</mapper>
新建测试类AccountTest
package com.cncs.test;
import com.cncs.dao.IAccountDao;
import com.cncs.dao.IUserDao;
import com.cncs.domain.AccountUser;
import com.cncs.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 AccountTest {
private InputStream in;
private SqlSession sqlSession;
private IAccountDao accountDao;
@Before //在测试方法之前执行
public void init() throws IOException {
//1.读取mybatis核心配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建工厂对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(in);
//3.通过工厂对象构建SqlSession对象
sqlSession = sessionFactory.openSession();
//4.通过SqlSession对象创建代理对象
accountDao = sqlSession.getMapper(IAccountDao.class);
}
@After //在测试方法之后执行
public void destory() throws IOException {
sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();
}
@Test
public void findAll() throws IOException {
List<AccountUser> list = accountDao.findAll();
for (AccountUser accountUser : list) {
System.out.println(accountUser);
}
}
}
方式二
方式二的思想是,直接在Account实体类中添加一个User类的属性,用于封装结果,这种方法应用更普遍。
在方式一的基础上修改。
在Account类中添加属性并设置getter和setter
private User user;
修改IAccountDao中方法
List<Account> findAll();
修改IAccountDao.xml配置
<resultMap id="accountMap" type="account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<association property="user" javaType="user" >
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<result property="address" column="address"/>
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select a.id as aid,uid,money,u.* from account a,user u where a.uid = u.id
</select>
PS:标签中的property属性就是实体类中的属性名,javaType属性是实体类中的属性名的JavaBean类型。
一对多查询
需求:每查询一个用户信息时,同时查询出他的账户信息
分析:一个用户可以有多个账户,用户和账户之间是一对多,在查询用户信息时,不考虑用户是否有账户,但用户信息必须查询出来,所以这里需要用左外连接。
修改User类,添加代码,并重新设置getter、setter和toString。
private List<Account> accountList;
修改映射配置文件IUserDao.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.cncs.dao.IUserDao">
<!--配置查询结果映射关系-->
<resultMap id="userMap" type="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<result property="address" column="address"/>
<collection property="accountList" ofType="account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
</collection>
</resultMap>
<!-- 查询所有-->
<select id="findAll" resultMap="userMap">
select u.*,a.id as aid,a.uid,a.money from user u left join account a on u.id = a.uid
</select>
</mapper>
新建测试类UserTest
package com.cncs.test;
import com.cncs.dao.IUserDao;
import com.cncs.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 UserTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
@Before //在测试方法之前执行
public void init() throws IOException {
//1.读取mybatis核心配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建工厂对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(in);
//3.通过工厂对象构建SqlSession对象
sqlSession = sessionFactory.openSession();
//4.通过SqlSession对象创建代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After //在测试方法之后执行
public void destory() throws IOException {
sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();
}
@Test
public void findAll() throws IOException {
//5.执行代理对象的方法
List<User> userList = userDao.findAll();
for (User user : userList) {
System.out.println(user);
}
}
}
多对多查询
需求:查询所有用户信息时,同时查询出每个用户的账户信息(需要同时包含用户信息和账户信息,如果二者之一不存在就舍弃这行数据)
分析:
- 一个用户可以有多个角色,一个角色可以有多个用户,它们之间是多对多的关系,在查询用户信息时,必须要查询到角色信息,否则舍弃该行数据。它们关系如图所示。
- 通过在user表中查询用户数据,通过设置中间表连接role表,最终获取数据时舍弃中间表的信息。查询user表然后左外连接user_role表,然后再左外连接role表获取角色信息,通过左外连接两次连接需要的信息。
执行sql查询结果
修改User实体类,添加属性
private List<Role> roleList;
新建实体类Role
package com.cncs.domain;
/**
* 角色的实体类
*/
public class Role {
private int id;
private String roleName;
private String roleDesc;
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
}
修改映射配置文件IUserDao.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.cncs.dao.IUserDao">
<!--配置查询结果映射关系-->
<resultMap id="userMap" type="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<result property="address" column="address"/>
<collection property="roleList" ofType="role">
<id property="id" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
</collection>
</resultMap>
<!-- 查询所有-->
<select id="findAll" resultMap="userMap">
select u.*,r.id as rid,r.role_name,r.role_desc from user u
join user_role ur on u.id=ur.uid
join role r on ur.rid=r.id
</select>
</mapper>
测试查询
查看查询结果,“老王”用户有两个角色;”马小玲“只有一个角色