Mybatis学习笔记【part05】多表查询

Mybatis 学习笔记 Part05

3. 动态SQL语句

3.1 简化编写的SQL片段

<!-- 定义 -->
<sql id="defaultUser">
        select * from user
</sql>
<!-- 引用 -->
<include refid="defaultUser"/> 

3.2 if 标签

    <select id="findByUser" resultType="user" parameterType="user">
            select * from user where 1=1
        <if test="username!=null and username != '' ">
            and username like #{username}
        </if> 
        <if test="address != null">
            and address like #{address}
        </if>
    </select>

	注意:<if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。
另外要注意 where 1=1 的作用~!

3.3 where标签

    <select id="findByUser" resultType="user" parameterType="user"> 
        <include refid="defaultSql"/>
        <where> 
        <if test="username!=null and username != '' ">
            and username like #{username}
        </if> 
        <if test="address != null">
            and address like #{address}
        </if>
        </where>
    </select>

3.4 foreach标签

SQL 语句:select 字段 from user where id in (?)

这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。

//在 QueryVo 中加入一个 List 集合用于封装参数
public class QueryVo implements Serializable {
    
	private List<Integer> ids;
    
    public List<Integer> getIds() {
		return ids; 
    }
    
	public void setIds(List<Integer> ids) {
		this.ids = ids; 
	} 
}
    
//持久层Mapper.java接口
List<User> findInIds(QueryVo vo);
//持久层Mapper.xml映射配置
<!-- 查询所有用户在 id 的集合之中,sql语句:select * from user where id in (1,2,3,4,5);--> 
<select id="findInIds" resultType="user" parameterType="queryvo">
    <include refid="defaultSql"/> 
    <where> 
        <if test="ids != null and ids.size() > 0"> 
            <foreach collection="ids" open="id in ( " close=")" item="uid" separator=",">
                #{uid}
            </foreach>
        </if>
	</where>
</select>

<foreach>标签用于遍历集合,它的属性:
    cllection:	代表要遍历的集合元素,注意编写时不要写#{}
    item:		代表遍历集合的每个元素,生成的变量名
    open:		代表语句的开始部分
    close:		代表结束部分
    sperator:	代表分隔符

4. 多表查询之一对一(多对一)

例:一个账号对应一个用户,但一个用户可以拥有多个账户。

​ 一对一:查询一个账户,关联查询该账户的用户。

​ 多对一:查询一个用户,关联查询该用户的所有账户。

<!-- SQL语句 -->
SELECT account.*,user.username,user.address
FROM   account,user
WHERE  account.uid = user.id
4.1 解决方法一
//定义 AccountUser 类
 	为了能够封装上面 SQL 语句的查询结果,定义 AccountCustomer 类中要包含账户信息同时还要包含用户信
息,所以我们要在定义 AccountUser 类时可以继承 User 类。
public class AccountUser extends Account implements Serializable {
    
    private String username;
    private String address;
    
	public String setter/getter/toString(){}
}
//持久层Mapper接口
List<AccountUser> findAll();
<!-- 持久层Mapper.xml -->
<mapper namespace="com.itheima.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>

	注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType
的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。	

​ 总结:定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

4.2 解决方法二
//使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
	通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户
是哪个用户的。
	public class Account implements Serializable {
        private Integer id;
        private Integer uid;
        private Double money;
        private User user; //在这里~!
		
        public void setter/getter/toString(){}
    }
<!--持久层Mapper接口-->
List<Account> findAll();
    
	注意:第二种方式,将返回值改为了Account 类型。因为Account类中包含了一个User类的对象,它可以封装账户所对应的用户信息。
<!-- 持久层Mapper.xml -->
<mapper namespace="com.itheima.dao.IAccountDao">
    <resultMap type="account" id="accountMap"> 
        <id column="aid" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
    <!-- 它是用于指定从表方的引用实体属性的 --> 
        <association property="user" javaType="user"> 
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="birthday" property="birthday"/>
            <result column="address" property="address"/>
    	</association>
    </resultMap> 
    <select id="findAll" resultMap="accountMap">
		select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;
	</select>
</mapper>

5. 多表查询之一对多

例:一个用户可以拥有多个账户

​ 一对多:查询所有用户信息及用户关联的账户信息。

	用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。
	
<!-- SQL语句 -->
SELECT u.*, acc.id id,acc.uid,acc.money
FROM user u
LEFT JOIN account acc 
	ON u.id = acc.uid

​ 需要区分好视角和参照物,多对一是以账户为视角,一对多是以用户为参照物,实现起来都是不一样的。

//定义用户类
public class User implements Serializable {
	private Integer id;
	private String username;
	private Date birthday;
	private String sex;
	private String address;
	private List<Account> accounts;//看这里~!
    
    public void setter/getter/toString(){}
//持久层Mapper.java
List<User> findAll();
<!-- 持久层Mapper.xml -->
<mapper namespace="com.itheima.dao.IUserDao"> 
    <resultMap type="user" id="userMap"> 
        <id column="id" property="id"></id> 
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
<!-- collection 是用于建立一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型--> 			
        <collection property="accounts" ofType="account"> 
            <id column="aid" property="id"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
    	</collection>
	</resultMap>
<!-- 配置查询所有操作 --> 
    <select id="findAll" resultMap="userMap">
		select u.*,a.id as aid ,a.uid,a.money 
        from user u 
        left outer join account a 
        on u.id =a.uid
	</select>
</mapper>

collection 部分定义了用户关联的账户信息,表示关联查询结果集。
属性:
property="accList":
	关联查询的结果集存储在 User 对象的上哪个属性。
ofType="account":
	指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

6.多表查询之多对多

例:一个人可以有多种职业,一个职业也有很多人来充当。

​ Role 与 User 可以看成是双向的一对多关系,即多对多关系。

​ 从 User 出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。

<!-- SQL语句 -->
SELECT r.*,u.id uid,u.username username,u.birthday birthday,u.sex sex,u.address address
FROM ROLE r
INNER JOIN USER_ROLE ur
    ON ( r.id = ur.rid)
INNER JOIN USER u
    ON (ur.uid = u.id);
//两个实体类

public class Role implements Serializable {
    
    private Integer roleId;
    private String roleName;
    private String roleDesc;
    private List<User> users;//在这里~!多对多的关系映射:一个角色可以赋予多个用户
    
    public void setter/getter/toString(){}
   
public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    private List<Account> accounts;
    private List<Role> roles;//在这里~!多对多的关系映射:一个用户可以拥有多个角色	
//两个实体的接口Mapper.java

public interface RoleMapper{
    List<Role> findAll();
}

public interface UserMapper {
    List<User> findAll();
}
<!-- 两个实体的映射配置Mapper.xml -->   连接时注意column属性的填写,需要和查询出来的别名一致!(id,rid,uid) 

<!--定义 role 表的 ResultMap-->  (左外连接实现,以下代码段user.id别名为uid,其余不变。)
<resultMap id="roleMap" type="role">
    <id property="roleId" column="ID"/>
    <result property="roleName" column="role_name"/>
    <result property="roleDesc" column="role_desc"/>
    <collection property="users" ofType="user">
        <id property="id" column="uid"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
    </collection>
</resultMap>
<select id="findAll" resultMap="roleMap">
    select r.*,u.id uid,u.username username,u.birthday birthday,u.sex sex,u.address address  
    from role r
    left outer join user_role ur 
    	on r.ID = ur.RID
    left outer join user u 
    	on ur.UID = u.id
</select>


<!--定义 role 表的 ResultMap-->  (左外连接实现,以下代码段user.id别名为uid,其余不变。)
<resultMap id="urMap" type="user">
    <id property="id" column="uid"/>
    <result property="username" column="username"/>
    <result property="address" column="address"/>
    <result property="sex" column="sex"/>
    <result property="birthday" column="birthday"/>
    <collection property="roles" ofType="role">
        <id property="roleId" column="ID"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDesc" column="role_desc"/>
    </collection>
</resultMap>
<select id="findAll" resultMap="urMap">
    select u.id uid,u.username username,u.birthday birthday,u.sex sex,u.address address,r.*
    from user u
    left outer join user_role ur
    	on u.id = ur.UID
    left outer join role r
   		on ur.RID = r.ID
</select>

7.多表查询 标签总结

7.1 association 标签

​ 用于一对一(多对一)

​ 用于将查询出来的数据封装进类中类对象。

属性:

​ property:类名

​ javaType:类名

public class Account implements Serializable {
        private Integer id;
        private Integer uid;
        private Double money;
        private User user; //在这里~! association用于将查询出来的数据封装进类中类对象。
		
        public void setter/getter/toString(){}
    }
<resultMap type="account" id="accountMap"> 
        <id column="aid" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
    <!-- 在这里~! association用于将查询出来的数据封装进类中类对象。 --> 
        <association property="user" javaType="user"> 
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="birthday" property="birthday"/>
            <result column="address" property="address"/>
    	</association>
    </resultMap> 

7.2 collection 标签

​ 用于一对多,多对多(即两个一对多)

​ 用于将查询出来的数据封装成对象并加进集合。

属性:

​ property:集合属性的变量名

​ ofType: 集合的元素类型

public class User implements Serializable {
	private Integer id;
	private String username;
	private Date birthday;
	private String sex;
	private String address;
	private List<Account> accounts;//看这里~!collection用于将查询出来的数据封装成对象并加进集合。
    
    public void setter/getter/toString(){}
<resultMap type="user" id="userMap"> 
        <id column="id" property="id"></id> 
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
<!-- 看这里~!collection用于将查询出来的数据封装成对象并加进集合。 --> 			
        <collection property="accounts" ofType="account"> 
            <id column="aid" property="id"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
    	</collection>
	</resultMap>

上一篇:『无为则无心』Python基础 — 43、文件备份的实现


下一篇:如何将电梯门禁卡复制到手机/手环上