查询
一对一查询
案例
一个银行账户只能由一个用户使用,即在银行的角度来看,查询账户信息是一对一查询。
数据表person和baccount
CREATE TABLE `person` (
`id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`birthday` date NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
insert into `person`(`id`,`name`,`birthday`) values
(41,'老王','2018-02-27 17:47:08'),
(42,'小二王','2018-03-02 15:09:37'),
(43,'小二王','2018-03-04 11:34:34'),
(45,'传智播客','2018-03-04 12:04:06'),
(46,'老王','2018-03-07 17:37:26'),
(48,'小马宝莉','2018-03-08 11:44:00');
CREATE TABLE `baccount` (
`ID` int(11) NOT NULL COMMENT '编号',
`PID` int(11) NULL DEFAULT NULL COMMENT '用户编号',
`MONEY` double NULL DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`) USING BTREE,
INDEX `FK_Reference_8`(`PID`) USING BTREE,
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`PID`) REFERENCES `person` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
insert into `baccount`(`ID`,`PID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);


对应的javabean
public class Person {
private Integer id;
private Date birthday;
private String name;
//省略get/set,toString方法
}
public class BAccount {
private Integer id;
private Integer pid;
private Double money;
private Person person;
//省略get/set,toString方法
}
dao接口:
public interface BAccountDao {
List<BAccount> findAllBAccountAndPerson();
}
映射文件:
<?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.on1.dao.BAccountDao">
<resultMap id="baccountMap" type="BAccount">
<id property="id" column="bid" ></id>
<result property="pid" column="pid"></result>
<result property="money" column="money"></result>
<!-- 一对一映射关系-->
<association property="person" column="pid" javaType="Person">
<id property="id" column="id"></id>
<result column="name" property="name"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<!-- 查询账户的id,用户id用户的全部信息,-->
<select id="findAllBAccountAndPerson" resultMap="baccountMap">
select p.*, ba.id as bid, ba.pid, ba.money from baccount ba, person p where ba.pid = p.id;
</select>
</mapper>
mybatis配置文件:
<?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 resource="jdbcConfig.properties">
</properties>
<typeAliases>
<package name="com.on1.bean"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<!--配置事务类型:使用了 JDBC 的提交和回滚设置-->
<transactionManager type="JDBC"/>
<!--配置数据池-->
<dataSource type="UNPOOLED">
<!--连接数据库的基本信息-->
<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/on1/dao/AccountDao.xml"/>
<mapper resource="com/on1/dao/BAccountDao.xml"/>
<mapper resource="com/on1/dao/PersonDao.xml"/>
</mappers>
</configuration>
测试类:
public class day3 {
InputStream in;
SqlSession sqlSession;
BAccountDao bAccountDao;
@Before
public void init() throws Exception {
// 加载配置文件
in = Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
// 获取代理对象
bAccountDao = sqlSession.getMapper(BAccountDao.class);
}
@After
public void destroy() throws Exception {
sqlSession.close();
in.close();
}
@Test
public void testFindAllBAccountAndPerson() {
List<BAccount> bAccountList = bAccountDao.findAllBAccountAndPerson();
for(BAccount bau : bAccountList) {
System.out.println(bau);
System.out.println("账户所属用户信息:");
System.out.println(bau.getPerson());
System.out.println("--------------------------");
}
}
}
控制台输出:

一对多查询
案例
站在用户的角度,一个用户可以持有多个银行账户信息。
代码
Person类
public class Person {
private Integer id;
private Date birthday;
private String name;
private List<BAccount> bAccountList;
//省略get /set方法,toString方法
}
dao接口
public interface PersonDao {
List<Person> findAllPersonAndBAccount();
}
PeroDao.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.on1.dao.PersonDao">
<resultMap id="personBAccountMap" type="Person">
<id property="id" column="id"></id>
<result property="name" column="name"/>
<result property="birthday" column="birthday"/>
<!--指定一对多的对应关系,ofType属性表示集合的数据类型-->
<collection property="bAccountList" ofType="BAccount">
<!-- id别名bid -->
<id column="bid" property="id"></id>
<result property="pid" column="pid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<!-- 返回用户全部信息,该用户持有账户的id,pid及金额信息-->
<select id="findAllPersonAndBAccount" resultMap="personBAccountMap">
select p.*, ba.id as bid, ba.pid, ba.money from person p left outer join baccount ba on p.id = ba.pid;
</select>
</mapper>
测试方法:
@Before
public void init() throws Exception {
// 加载配置文件
in = Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
// 获取代理对象
personDao = sqlSession.getMapper(PersonDao.class);
}
@After
public void destroy() throws Exception {
sqlSession.close();
in.close();
}
@Test
public void testFindAllPersonAndBAccount() {
List<Person> personList = personDao.findAllPersonAndBAccount();
for(Person person : personList) {
System.out.println("--------用户信息----------");
System.out.println(person);
System.out.println("该用户持有账户信息:");
System.out.println(person.getbAccountList());
System.out.println("--------------------------");
}
}
控制台输出:

多对多查询
案例
新建一个表示人物所扮演角色(职位)的role表和记录两表id的person_role表。

站在Role的角度,一个Pole可以由多个Person扮演,这样的关系是一对多的; 站在Person的角度,一个Person可以扮演多个Role,这样的关系同样是一对多的,因此我们可以认为Role和Person的多对多关系可以拆分为两个一对多的关系。
现在我们需要查询角色表和它对应的人物信息,但查询角色对应的人物信息只能通过中间表person_role表提供的pid才能知道人物信息。
Role到Person的多对多
role表和person_role表
CREATE TABLE `role` (
`ID` int(11) NOT NULL COMMENT '编号',
`ROLE_NAME` varchar(30) default NULL COMMENT '角色名称',
`ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values
(1,'院长','管理整个学院'),
(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
CREATE TABLE `person_role` (
`PID` int(11) NOT NULL COMMENT '任务编号',
`RID` int(11) NOT NULL COMMENT '人物编号',
PRIMARY KEY (`PID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`PID`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `person_role`(`PID`,`RID`) values (41,1),(45,1),(41,2);


Role类:
public class Role {
private Integer roleId;
private String roleName;
private String roleDesc;
private List<Person> personList;
//省略get/set toString方法
}
dao接口:
public interface RoleDao {
List<Role> findAllRoles();
}
在mybatis配置文件中的mappers标签里添加:
<mapper resource="com/on1/dao/RoleDao.xml"/>
resources文件夹下新建映射文件RoleDap.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.on1.dao.RoleDao">
<resultMap id="roleMap" type="Role">
<id property="roleId" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<collection property="personList" ofType="Person">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
</collection>
</resultMap>
<!-- 获取每个人物的全部信息,该人物扮演角色的id,角色名,角色名解释-->
<select id="findAllRoles" resultMap="roleMap">
select p.*, r.id as rid, r.role_name, r.role_desc from role r
left outer join person_role pr on r.id = pr.rid
left outer join person p on p.id = pr.pid
</select>
</mapper>
测试方法:
//记得在测试类里添加
//RoleDao roleDao;并在init()方法添加对应的获取代理步骤
//roleDao = sqlSession.getMapper(RoleDao.class);
@Test
public void testFindAllRoles() {
List<Role> roleList = roleDao.findAllRoles();
for(Role role : roleList) {
System.out.println("--------角色信息----------");
System.out.println(role);
System.out.println("该角色的扮演人物");
System.out.println(role.getPersonList());
}
}
控制台输出:

Person到Role的多对多
现在我们要返回每个Person的全部信息以及它扮演的角色信息。
Person类:
public class Person {
private Integer id;
private Date birthday;
private String name;
private List<Role> roleList;
//省略get/set toString方法
}
PersonDao接口添加方法:
List<Person> findAllPersonAndRole();
映射文件PersonDao.xml中的mapper标签里添加:
<resultMap id="personMap" type="Person">
<id property="id" column="id"></id>
<result property="name" column="name"/>
<result property="birthday" column="birthday"/>
<collection property="roleList" ofType="Role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
</collection>
</resultMap>
<!-- 返回该人物全部信息及其担任角色id-->
<select id="findAllPersonAndRole" resultMap="personMap">
select p.*, r.id as rid, r.role_name, r.role_desc from Person p
left outer join person_role pr on p.id = pr.pid
left outer join role r on r.id = pr.rid
</select>
测试方法:
@Test
public void testFindAllPersonAndRole() {
List<Person> personList = personDao.findAllPersonAndRole();
for(Person person : personList) {
System.out.println("--------人物信息----------");
System.out.println(person);
System.out.println("该人物担任的角色:");
System.out.println(person.getRoleList());
System.out.println("--------------------------");
}
}
控制台输出:

延迟加载
延迟加载是指在需要用到数据时才加载,例如在实际开发中很多时候 我们并不需要总是在加载人物信息时就一定要加载它的账户信息。
这样就会先从单表查询,等需要时再从关联表去关联查询,速度会快很多(查询单表比查询关联多张表快);但也存在弊处:只有当需要时才进行查询,这样在大批量数据查询时,耗费时间增加。
使用association标签实现延迟加载
查询账户(BAccount)信息并且管理查询人物(Person)信息,在前面我们使用了 association、collection 实现一对一及一对多映射,而这两个标签也具备延迟加载的功能。
两个数据表:
CREATE TABLE `person` (
`id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`birthday` date NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `person` VALUES (41, '老王', '2018-02-27');
INSERT INTO `person` VALUES (42, '小二王', '2018-03-02');
INSERT INTO `person` VALUES (43, '小二王', '2018-03-04');
INSERT INTO `person` VALUES (45, '传智播客', '2018-03-04');
INSERT INTO `person` VALUES (46, '老王', '2018-03-07');
INSERT INTO `person` VALUES (48, '小马宝莉', '2018-03-08');
CREATE TABLE `baccount` (
`ID` int(11) NOT NULL COMMENT '编号',
`PID` int(11) NULL DEFAULT NULL COMMENT '用户编号',
`MONEY` double NULL DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`) USING BTREE,
INDEX `FK_Reference_8`(`PID`) USING BTREE,
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`PID`) REFERENCES `person` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `baccount` VALUES (1, 46, 1000);
INSERT INTO `baccount` VALUES (2, 45, 1000);
INSERT INTO `baccount` VALUES (3, 46, 2000);


Person类
public class Person {
private Integer id;
private Date birthday;
private String name;
private List<BAccount> bAccountList;
//省略get /set方法,toString方法
}
public class BAccount {
private Integer id;
private Integer pid;
private Double money;
//省略get /set方法,toString方法
}
dao层接口
public interface BAccountDao {
List<BAccount> findAllBAccountAndPerson();
}
public interface PersonDao {
Person findById(Integer pid);
}
映射文件:
<mapper namespace="com.on1.dao.PersonDao">>
<select id="findById" resultType="person" parameterType="int">
select * from person where id = #{pid}
</select>
</mapper>
<mapper namespace="com.on1.dao.BAccountDao">
<resultMap id="baccountMap" type="BAccount">
<id property="id" column="id" />
<result property="pid" column="pid"/>
<result property="money" column="money"/>
<!--select属性指定 查询人物的唯一标识,即PersonDao.xml里的sql语句:findById -->
<!--column属性指定 根据id查询人物所需要的参数值 -->
<association property="person" javaType="Person"
select="com.on1.dao.PersonDao.findById" column="pid">
</association>
<select id="findAllBAccountAndPerson" resultMap="baccountMap">
select * from baccount;
</select>
测试方法:
@Test
public void testFindAllBAccountAndPerson() {
List<BAccount> bAccountList = bAccountDao.findAllBAccountAndPerson();
for(BAccount bau : bAccountList) {
System.out.println("------------账户信息-------");
System.out.println(bau);
System.out.println("账户所属用户信息:");
System.out.println(bau.getPerson());
System.out.println("--------------------------");
}
}
在配置文件中开启 Mybatis 的延迟加载策略
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

在没开启延迟加载的支持时,控制台的输出如下,


开启后控制台的输出如下,可以发现,查询对应的人物信息是一段一段的执行,而非一股脑的在开始时就全部执行


如果测试方法中不遍历,即不去查询账户的人物信息
@Test
public void testFindAllBAccountAndPerson() {
List<BAccount> bAccountList = bAccountDao.findAllBAccountAndPerson();
}
则延迟加载后的控制台输出如下,仅执行了查询账户的全部信息

而没有开启延迟加载的控制台输出如下,它执行了查询账户及所属人物的全部信息

使用Collection实现延迟加载
同样的例子,不过本次主要查询人物信息,等需要时才查询持有的账户信息。
dao层接口:
public interface PersonDao {
Person findById(Integer pid);
}
public interface BAccountDao {
List<BAccount> findBAccountByPid(int pid);
}
映射文件:
<mapper namespace="com.on1.dao.BAccountDao">
<resultMap id="baccountMap" type="BAccount">
<id property="id" column="id" />
<result property="pid" column="pid"/>
<result property="money" column="money"/>
</resultMap>
<!-- 根据pid查询账户-->
<select id="findBAccountByPid" resultType="BAccount" parameterType="int">
select * from baccount where pid = #{pid}
</select>
<mapper namespace="com.on1.dao.PersonDao">
<resultMap id="personBAccountMap" type="Person">
<id property="id" column="id"></id>
<result property="name" column="name"/>
<result property="birthday" column="birthday"/>
<collection property="bAccountList" ofType="BAccount"
select="com.on1.dao.BAccountDao.findBAccountByPid" column="id">
</collection>
</resultMap>
<select id="findAllPersonAndBAccount" resultMap="personBAccountMap">
select * from person;
</select>
测试方法:
@Test
public void testFindAllPersonAndBAccount() {
List<Person> personList = personDao.findAllPersonAndBAccount();
// for(Person person : personList) {
// System.out.println("--------用户信息----------");
// System.out.println(person);
// System.out.println("该用户持有账户信息:");
// System.out.println(person.getbAccountList());
// System.out.println("--------------------------");
// }
}
开启延迟加载后控制台输出:

取消掉测试方法中的全部注解后,运行后的控制台输出为:

