技术框架中对高级查询多对多查询的学习
高级查询之一对多查询
查询条件
根据游戏名称,查询游戏账号信息
我们在之前创建的映射器接口 GameMapper.java 中添加接口方法,如下:
/**
* 根据游戏名查询游戏账号
* @param name 游戏名
* @return 游戏实体类
*/
public GameEntity selectGameByName(String name);
接下来,我分别演示关联查询和子查询方式实现接口方法的映射。
关联查询方式
现在我们暂时先抛开 MyBatis 框架,直接从数据库出发写一写关联查询的 SQL 语句,如下:
select g.*,a.* from tb_game as g join tb_account as a on g.id=a.game_id where g.name ='英雄联盟'
我们在数据库中执行这条 SQL 语句
现在,我们回到 MyBatis 框架,看一下在 XML 映射文件中,如何实现关联查询映射。
首先,我们需要建立 GameEntity 实体类与 AccountEntity 实体类之间的关联关系,如下:
public class GameEntity {
private int id;
private String name;
private String type;
private String operator;
private List<AccountEntity> accounts; //关联引用游戏账号集合
}
这样,通过游戏名查询的账号信息就可以映射到 accounts 属性中。注意 accounts 属性的 get 和 set 方法要记得添加上去,还有 toString 方法要重写一下,添加 accounts 属性的打印信息。
你可能有疑惑,这里为何要使用 List 集合呢?原因是一款游戏对应的账号信息可能有多个,而 MyBatis 可以通过使用 resultMap 的 collection 标记将关联查询的多条记录映射到一个 List 集合属性中。
现在,我们来编写映射文件中 SQL 语句映射,如下:
<?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="mapper.GameMapper">
<resultMap id="gameResultMap" type="entity.GameEntity">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="type" column="type" />
<result property="operator" column="operator" />
<!-- collection:一对多映射,关联当前分类下产品信息
property:映射集合结果
ofType:结果集类型 -->
<collection property="accounts" ofType="entity.AccountEntity">
<id property="id" column="aid" />
<result property="userName" column="user_name" />
<result property="password" column="password" />
</collection>
</resultMap>
<select id="selectGameByName" resultMap="gameResultMap">
select g.*,a.id as aid,a.user_name,a.password from tb_game as g join tb_account as a on g.id=a.game_id where g.name =#{name}
</select>
</mapper>
以上和一对一关联查询相比,association 标记改为 collection 标记,表示是一对多映射;javaType 属性改为ofType 属性,表示集合里的元素类型。除此以外,和一对一关联查询差不多。
最后,我们在 MyBatisTest 中添加一个单元测试方法,如下:
@Test
public void selectGameByNameTest() {
GameEntity gameEntity = gameMapper.selectGameByName("英雄联盟");
System.out.println(gameEntity);
Assert.assertNotNull(gameEntity);
}
执行单元测试方法,结果如下:
2020-07-15 18:34:05,260 [main] [mapper.GameMapper.selectGameByName]-[DEBUG] ==> Preparing: select g.*,a.* from tb_game as g join tb_account as a on g.id=a.game_id where g.name =?
2020-07-15 18:34:05,307 [main] [mapper.GameMapper.selectGameByName]-[DEBUG] ==> Parameters: 英雄联盟(String)
2020-07-15 18:34:05,354 [main] [mapper.GameMapper.selectGameByName]-[DEBUG] <== Total: 2
GameEntity{id=1, name='英雄联盟', type='MOBA', operator='腾讯游戏', accounts=[AccountEntity{id=1, userName='潇洒哥', password='12345'}, AccountEntity{id=4, userName='进击巨人', password='11111'}]}
子查询方式
首先,我们暂时先抛开 MyBatis 框架,直接从数据库出发写一写子查询的 SQL 语句,如下:
select * from tb_account where game_id = (select id from tb_game where name ='英雄联盟')
接着,这里我们直接来编写映射文件中 SQL 语句映射,如下:
<?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="mapper.GameMapper">
<resultMap id="gameResultMap" type="entity.GameEntity">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="type" column="type" />
<result property="operator" column="operator" />
<!-- collection:用于映射关联查询多个对象信息
property:将关联查询的账号信息映射到属性 accounts 上
column:传递子查询参数
select:子查询id
-->
<collection property="accounts" ofType="entity.AccountEntity"
column="id" select="selectAccountById" />
</resultMap>
<select id="selectGameByName" resultMap="gameResultMap">
select * from tb_game where name =#{name}
</select>
<select id="selectAccountById" resultType="entity.AccountEntity">
select * from tb_account where game_id=#{gameId}
</select>
</mapper>
执行测试,结果如下:
2020-07-15 21:25:11,086 [main] [mapper.GameMapper.selectGameByName]-[DEBUG] ==> Preparing: select * from tb_game where name =?
2020-07-15 21:25:11,164 [main] [mapper.GameMapper.selectGameByName]-[DEBUG] ==> Parameters: 英雄联盟(String)
2020-07-15 21:25:11,211 [main] [mapper.GameMapper.selectAccountById]-[DEBUG] ====> Preparing: select * from tb_account where game_id=?
2020-07-15 21:25:11,211 [main] [mapper.GameMapper.selectAccountById]-[DEBUG] ====> Parameters: 1(Integer)
2020-07-15 21:25:11,211 [main] [mapper.GameMapper.selectAccountById]-[DEBUG] <==== Total: 2
2020-07-15 21:25:11,211 [main] [mapper.GameMapper.selectGameByName]-[DEBUG] <== Total: 1
GameEntity{id=1, name='英雄联盟', type='MOBA', operator='腾讯游戏', accounts=[AccountEntity{id=1, userName='潇洒哥', password='12345'}, AccountEntity{id=4, userName='进击巨人', password='11111'}]}