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>