MyBatis框架(三)
十三、MyBatis处理关联关系-多表连接【重点】
实体间的关系:关联关系(拥有 has、属于 belong)
-
OneToOne:一对一关系(Passenger— Passport)
-
OneToMany:一对多关系(Employee — Department)
-
ManyToMany:多对多关系(Student — Subject)
Table建立外键关系
Entity添加关系属性
Mapper中将属性与列名对应
13.1 OneToOne
SQL参考OneToOneExample.sql
<mapper namespace="com.qf.mybatis.part2.one2one.PassengerDao">
<!-- 结果映射(查询结果的封装规则) -->
<resultMap id="passengerResultMap" type="com.qf.mybatis.part2.one2one.Passenger">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="sex" column="sex" />
<result property="birthday" column="birthday" />
<!-- 关系表中数据的封装规则 --> <!-- 指定关系表的实体类型 -->
<association property="passport" javaType="com.qf.mybatis.part2.one2one.Passport">
<id property="id" column="passport_id" />
<result property="nationality" column="nationality" />
<result property="expire" column="expire" />
<result property="passenger_id" column="passenger_id" />
</association>
</resultMap>
<!-- 多表连接查询 --> <!-- 结果映射(查询结果的封装规则)-->
<select id="selectPassengerById" resultMap="passengerResultMap">
<!-- 别名(避免与p1.id冲突) -->
SELECT p1.id , p1.name , p1.sex , p1.birthday , p2.id as passport_id , p2.nationality , p2.expire , p2.passenger_id
FROM t_passengers p1 LEFT JOIN t_passports p2
ON p1.id = p2.passenger_id
WHERE p1.id = #{id}
</select>
</mapper>
- 注意:指定“一方”关系时(对象),使用< association javaType="" >
13.2 OneToMany
SQL参考OneToManyExample.sql
<mapper namespace="com.qf.mybatis.part2.one2many.DepartmentDao">
<!-- 封装规则 -->
<resultMap id="departmentResultMap" type="com.qf.mybatis.part2.one2many.Department">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="location" column="location" />
<!-- 关系表中数据的封装规则 --> <!-- 指定关系表的实体类型 -->
<collection property="emps" ofType="com.qf.mybatis.part2.one2many.Employee">
<id property="id" column="emp_id" />
<result property="name" column="emp_name" />
<result property="salary" column="salary" />
<result property="dept_id" column="dept_id" />
</collection>
</resultMap>
<!-- 多表连接查询 --> <!-- 封装规则 -->
<select id="selectDepartmentById" resultMap="departmentResultMap" >
<!-- 别名(避免与d.id、d.name冲突)-->
SELECT d.id , d.name , d.location , e.id AS emp_id , e.name emp_name , e.salary , e.dept_id
FROM t_departments d LEFT JOIN t_employees e
ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
</mapper>
- 注意:指定“多方”关系时(集合),使用< collection ofType="" >
13.3 ManyToMany
SQL参考ManyToManyExample.sql
建立第三张关系表
<mapper namespace="com.qf.mybatis.part2.many2many.StudentDao">
<!-- 映射查询只封装两表中的信息,可忽略关系表内容 -->
<resultMap id="allMap" type="com.qf.mybatis.part2.many2many.Student">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<collection property="subjects" ofType="com.qf.mybatis.part2.many2many.Subject">
<id property="id" column="sid" />
<result property="name" column="sname" />
<result property="grade" column="grade" />
</collection>
</resultMap>
<!-- 三表连接查询 -->
<select id="selectAllStudents" resultMap="allMap">
SELECT s1.* , ss.* , s2.id as sid , s2.name as sname , s2.grade
FROM t_students s1 LEFT JOIN t_stu_sub ss
ON s1.id = ss.student_id <!-- 通过t_stu_sub表建立二者之间的关系 -->
LEFT JOIN t_subjects s2
ON ss.subject_id = s2.id
</select>
</mapper>
- 注意:指定“多方”关系时(集合),使用< collection ofType="" >
13.4 关系总结
一方,添加集合;多方,添加对象。
双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。
持有对象关系属性,使用< association property="dept" javaType="department" >
持有集合关系属性,使用< collection property="emps" ofType="employee" >
十四、PageHelper
14.1 概念
PageHelper是适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作。
14.2 访问与下载
官方网站:https://pagehelper.github.io/
下载地址:https://github.com/pagehelper/Mybatis-PageHelper
14.3 开发步骤
PageHelper中提供了多个分页操作的静态方法入口。
14.3.1 引入依赖
pom.xml中引入PageHelper依赖。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
14.3.2 配置MyBatis-config.xml
在MyBatis-config.xml中添加< plugins >。
<configuration>
<typeAliases></typeAliases>
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<environments></environments>
</configuration>
14.3.3 PageHelper应用方式
使用PageHelper提供的静态方法设置分页查询条件。
@Test
public void testPagehelper(){
UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
PageHelper.startPage(1,2);//使用PageHelper设置分页条件
List<User> users = userDao.selectAllUsers();
for(User user : users){
System.out.println(user);
}
}
14.4 PageInfo对象
PageInfo对象中包含了分页操作中的所有相关数据。
PageInfo结构图
14.4.1 PageInfo应用方式
使用PageInfo保存分页查询结果。
@Test
public void testPageInfo(){
UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
PageHelper.startPage(6, 2);
List<User> users = userDao.selectAllUsers();
PageInfo<User> pageInfo = new PageInfo<User>(users);//将分页查询的结果集保存在PageInfo对象中
System.out.println(pageInfo);
}
14.4.2 注意事项
- 只有在PageHelper.startPage()方法之后的第一个查询会有执行分页。
- 分页插件不支持带有“for update”的查询语句。
- 分页插件不支持“嵌套查询”,会导致结果集折叠。
十五、补充【了解】
以下内容并非必备知识,了解即可。
15.1 MyBatis注解操作
通过在接口中直接添加MyBatis注解,完成CRUD。
-
注意:接口注解定义完毕后,需将接口全限定名注册到mybatis-config.xml的< mappers >中。
-
经验:注解模式属于硬编码到.java文件中,失去了使用配置文件外部修改的优势,可结合需求选用。
15.1.1 查询
public interface UserMapper {
@Select("SELECT * FROM t_users WHERE id = #{id}")
public User selectUserById(Integer id);
@Select("SELECT * FROM t_users WHERE id = #{id} AND password = #{pwd}")
public User selectUserByIdAndPwd_annotation(@Param("id") Integer id, @Param("pwd") String password);
}
15.1.2 删除
@Delete(value = "DELETE FROM t_users WHERE id = #{id}")
public int deleteUser(Integer id);
15.1.3 修改
@Update("UPDATE t_users SET name = #{name} , password = #{password} , salary = #{salary} , birthday = #{birthday} WHERE id = #{id}")
public int updateUser(User user);
15.1.4 插入
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUser(User user);
@Options(useGeneratedKeys = true , keyProperty = "id") // 自增key,主键为id
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUserGeneratedKeys(User user);
15.2 $符号的应用场景
${attribute} 可解决动态生降序问题,属于字符串拼接SQL,而非占位符,会有注入攻击问题。
15.2.1 $符号参数绑定
public List<User> selectUsers(@Param("rule") String rule); //必须使用@Param()否则会作为属性解析
<select id="selectAllUsers" resultType="user">
SELECT * FROM t_users
ORDER BY id ${rule} <!-- 拼接 asc | desc -->
</select>
List<User> ulist = userDao.selectAllUsers("desc"); //调用时传入asc | desc
15.2.2 $符号注入攻击
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_users
WHERE name LIKE '%${keyword}%' <!-- 会存在注入攻击 -->
</select>
注入攻击
15.3 MyBatis处理关联关系-嵌套查询【了解】
思路:查询部门信息时,及联查询所属的员工信息。
- DepartmentDao接口中定义selectDepartmentById,并实现Mapper。
- EmployeeDao接口中定义selectEmployeesByDeptId,并实现Mapper,
- 当selectDepartmentById被执行时,通过< collection >调用selectEmployeesByDeptId方法,并传入条件参数。
15.3.1 主表查询
定义selectEmployeesByDeptId,并书写Mapper,实现根据部门ID查询员工信息
public interface EmployeeDao {
/**
* 根据部门编号查询员工信息
* @param did 部门编号
* @return 该部门中的所有员工
*/
public List<Employee> selectEmployeeByDeptId(@Param("did") String did);
}
<mapper namespace="com.qf.mybatis.part2.one2many.EmployeeDao">
<!-- 根据部门编号查询所有员工 -->
<select id="selectEmployeeById" resultType="employee" >
SELECT id,name,salary,dept_id
FROM t_employees
WHERE dept_id = #{did}
</select>
</mapper>
15.3.2 及联调用
定义selectDepartmentById,并书写Mapper,实现根据部门ID查询部门信息,并及联查询该部门员工信息
public interface DepartmentDao {
/**
* 查询部门信息
* @param id
* @return
*/
public Department selectDepartmentById(@Param("id") String id);
}
<mapper namespace="com.qf.mybatis.part2.one2many.DepartmentDao">
<resultMap id="departmentResultMap" type="department">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="location" column="location" />
<!-- column="传入目标方法的条件参数" select="及联调用的查询目标"-->
<collection property="emps" ofType="Employee" column="id" select="com.qf.mybatis.part2.one2many.EmployeeDao.selectEmployeeByDeptId" />
</resultMap>
<select id="selectAllDepartments" resultMap="departmentResultMap">
SELECT id , name , location
FROM t_departments
WHERE id = #{id}
</select>
</mapper>
15.3.3 延迟加载
mybatis-config.xml中开启延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/> <!-- 开启延迟加载(默认false) -->
</settings>
- 注意:开启延迟加载后,如果不使用及联数据,则不会触发及联查询操作,有利于加快查询速度、节省内存资源。