MyBatis的对象关系映射---一对多N+1策略★★★★★
在实际开发中,一个业务可能涉及到多个数据表的查询,那么多表查询就涉及连接查询(等值连接), 等值连接 表与表之间有一个外键关键,但是程序中最终获取的表封装的对象, 对象与对象之间是没有外键关系的,对象和对象之间只有依赖关系;
对象之间关系主要是四种:
一对一 关系
一个人对应身份证id,一个QQ号对应一个QQ空间
一对多 关系
一个部门对应多个员工
多对一 关系
多个员工对应一个部门
多对多 关系
多个学生对应多个老师,多个学生对应多个课程
什么关系应该从哪个对象作为中心点来看
一对多, 以one方作为中心点
MyBatis框架支持多表查询封装对象之间关系
<collection> 一对多查询 <association>多对一和一对一查询
MyBatis的对象关系映射---一一对多N+1策略:
mapper表:
1 package top.abcdit.mybatis.mapper; 2 3 import top.abcdit.mybatis.pojo.Department; 4 5 public interface One2ManyMapper { 6 7 /** 8 * 根据id查询出部门的所有信息(包括部门下面的所有员工) 9 * @param id 部门id 10 * @return 部门对象 11 */ 12 13 Department selectByPrimaryKey(Integer id); 14 15 }
One2ManyMapper.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"> <!-- 映射标签,内部就编写具体的映射sql语句 namespace :命名空间,值 必须是 对应接口的全限定名 = 包名 +简单类名 top.abcdit.mybatis.mapper.One2ManyMapper --> <mapper namespace="top.abcdit.mybatis.mapper.One2ManyMapper"> <select id="selectByPrimaryKey" parameterType="int" resultMap="dept_map"> select * from department where id = #{id} </select> <resultMap type="top.abcdit.mybatis.pojo.Department" id="dept_map"> <id column="id" property="id"/> <result column="name" property="name"/> <!-- 问题: private List<Employee> employees; 员工集合如何映射? 解决方案:使用 <collection>集合标签 <collection property="" column="" select=""/> property :需要映射的属性 此时就是 employees column:当前部门的主键 id select : 需要去查询的对应员工的功能id --> <collection property="employees" column="id" select="selectEmployeesByDeptId"/> </resultMap> <!-- 联合查询的功能 --> <!-- 根据部门的id查询出所有的员工 --> <select id="selectEmployeesByDeptId" parameterType="int" resultType="top.abcdit.mybatis.pojo.Employee"> select * from employee where dept_id = #{id} </select> </mapper>
mybatis-config.xml文件:
<?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"> <!-- mybatis配置标签 内部就是mybatis具体配置 --> <configuration> <!-- mybatis的配置环境 default:默认使用环境,值就是下面的环境id --> <settings> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true"/> </settings> <environments default="mysql"> <!-- 具体某一个环境 --> <environment id="mysql"> <!-- MyBatis操作需要事务管理,默认使用JDBC JDBC:mybatis的别名 --> <transactionManager type="JDBC"/> <!--配置mybatis的数据源(连接池) POOLED(别名) :mybatis内置的一个连接池 后期和spring集成使用,druid阿里巴巴连接池 --> <dataSource type="POOLED"> <!-- 数据库驱动 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!-- url地址 --> <property name="url" value="jdbc:mysql://localhost:3306/gzmybatis1?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"/> <!-- 数据库的账号 --> <property name="username" value="root"/> <!-- 数据库的密码 --> <property name="password" value="myroot"/> </dataSource> </environment> </environments> <!-- 配置映射文件 --> <mappers> <!-- 读取单个映射文件 --> <mapper resource="top/abcdit/mybatis/mapper/One2ManyMapper.xml"/> </mappers> </configuration>
员工和部门表:
package top.abcdit.mybatis.pojo; import java.util.List; public class Department { private Integer id; private String name; //一个部门对应多个员工:一对多关系 //此时我们必须有集合来描述 private List<Employee> employees; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } @Override public String toString() { return "Department [id=" + id + ", name=" + name + ", employees=" + employees + "]"; } }
package top.abcdit.mybatis.pojo; public class Employee { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + "]"; } }
Util工具表:
package top.abcdit.mybatis.util; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class MyBatisUtil { private MyBatisUtil() {} private static SqlSessionFactory sqlSessionFactory; static { //读取主配置文件 String resource = "mybatis-config.xml"; //java7(jdk1.7)开始,支持在try()括号里直接自动释放资源 try(InputStream inputStream = Resources.getResourceAsStream(resource);) { sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (Exception e) { e.printStackTrace(); } } public static SqlSession openSession() { //创建sqlSession回话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); return sqlSession; } public static void main(String[] args) { SqlSession sqlSession = openSession(); System.out.println(sqlSession); } }
测试表:
1 package top.abcdit.mybatis.mapper; 2 3 import static org.junit.Assert.*; 4 5 import org.apache.ibatis.session.SqlSession; 6 import org.junit.Test; 7 8 import top.abcdit.mybatis.pojo.Department; 9 import top.abcdit.mybatis.util.MyBatisUtil; 10 11 public class One2ManyMapperTest { 12 13 @Test 14 public void testSelectByPrimaryKey() { 15 16 SqlSession session = MyBatisUtil.openSession(); 17 One2ManyMapper mapper = session.getMapper(One2ManyMapper.class); 18 19 Department dept = mapper.selectByPrimaryKey(2); 20 System.out.println(dept); 21 22 } 23 24 }
输出结果:
DEBUG [main] - ==> Preparing: select * from department where id = ? DEBUG [main] - ==> Parameters: 2(Integer) TRACE [main] - <== Columns: id, name TRACE [main] - <== Row: 2, 研发部 DEBUG [main] - ====> Preparing: select * from employee where dept_id = ? DEBUG [main] - ====> Parameters: 2(Integer) TRACE [main] - <==== Columns: id, name, dept_id TRACE [main] - <==== Row: 2, 虚竹, 2 DEBUG [main] - <==== Total: 1 DEBUG [main] - <== Total: 1 Department [id=2, name=研发部, employees=[Employee [id=2, name=虚竹]]]