一對多,是最常見的一種設計。就是 A 表的一條記錄,對應 B 表的多條記錄,且 A 的主鍵作為 B 表的外鍵。這主要看以哪張表為中心,下面的測試數據中,從employee 表來看,一個員工對應一個部門,是一對一關系,如果從部門角度來看,則是一對多的關系,一個部門對應多個員工,本節主要研究一對多的關系。
查詢部門的時候將部門對應的所有員工信息也查詢出來
數據表建立
新建數據表department,有兩個字段,插入兩條數據如下:
id | dept_name |
1 | CIA |
2 | FSB |
新建數據表employee,有三個字段,其中dept_id是外鍵,關聯department表的主鍵id。插入數據如下:
id | last_name | dept_id |
1 | Tom | 1 |
2 | Jerry | 2 |
3 | Neo | 1 |
4 | Cypher | 2 |
新建maven工程,添加依賴,主要是mybatis和mysql
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies>
編寫數據庫表對應的實體。
對於department表,對應實體如下:注意增加一個包含了Employee集合。
package com.yefengyu.mybatis.entity; import java.util.List; public class Department { private Integer id; private String deptName; private List<Employee> employees; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } @Override public String toString() { return "Department{" + "id=" + id + ", deptName='" + deptName + '\'' + ", employees=" + employees + '}'; } }
對於employee表,實體如下,注意在Employee實體中把外鍵直接變成對於Department對象的引用。
package com.yefengyu.mybatis.entity; public class Employee { private Integer id; private String lastName; private Department department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } @Override public String toString() { return "Employee{" + "id=" + id + ", lastName='" + lastName + '\'' + ", department=" + department + '}'; } }
編寫mapper接口
package com.yefengyu.mybatis.mapper; import com.yefengyu.mybatis.entity.Department; public interface DepartmentMapper { public Department getDeptById(Integer id); }
根據部門ID查詢部門信息和對應的所有員工信息。
編寫mapper映射文件(本節重點)
collection嵌套結果集方法:
<?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="com.yefengyu.mybatis.mapper.DepartmentMapper"> <resultMap id="dept" type="com.yefengyu.mybatis.entity.Department"> <id column="d_id" property="id"/> <result column="dept_name" property="deptName"/> <!-- collection定義關聯集合類型的屬性的封裝規則 ofType:指定集合里面元素的類型 --> <collection property="employees" ofType="com.yefengyu.mybatis.entity.Employee" javaType="java.util.ArrayList"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> </collection> </resultMap> <select id="getDeptById" resultMap="dept"> select e.id id, e.last_name last_name, e.dept_id dept_id, d.id d_id,d.dept_name dept_name from department d left join employee e on e.dept_id = d.id where d.id = #{id} </select> </mapper>
新建一個mybatis全局配置文件,詳細信息見官網
<?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"> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8&allowMultiQueries=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/DepartmentMapper.xml"/> </mappers> </configuration>
測試
public static void main(String[] args) throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); try { DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class); Department dept = mapper.getDeptById(1); System.out.println(dept); } finally { sqlSession.close(); } }
結果如下:
Created connection 1938056729. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619] ==> Preparing: select e.id id, e.last_name last_name, e.dept_id dept_id, d.id d_id,d.dept_name dept_name from department d left join employee e on e.dept_id = d.id where d.id = ? ==> Parameters: 1(Integer) <== Columns: id, last_name, dept_id, d_id, dept_name <== Row: 1, Tom, 1, 1, CIA <== Row: 3, Neo, 1, 1, CIA <== Total: 2 Department{id=1, deptName='CIA', employees=[Employee{id=1, lastName='Tom', department=null}, Employee{id=3, lastName='Neo', department=null}]} Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]
上面查詢雖然可以查詢出數據,但是數據過多則會有性能問題,因此好的做法是分步查詢。
分步查詢
1、新增查詢員工的接口,特別注意是根據部門id來查詢
package com.yefengyu.mybatis.mapper; import com.yefengyu.mybatis.entity.Employee; public interface EmployeeMapper { Employee getEmpByDeptId(Integer deptId); }
2、編寫對應的mapper映射文件
<?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="com.yefengyu.mybatis.mapper.EmployeeMapper"> <select id="getEmployee" resultType="com.yefengyu.mybatis.entity.Employee"> select * from employee where dept_id = #{id} </select> </mapper>
3、編寫查詢部門的接口
package com.yefengyu.mybatis.mapper; import com.yefengyu.mybatis.entity.Department; public interface DepartmentMapper { public Department getDeptById(Integer id); }
4、編寫對應的映射文件
<?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="com.yefengyu.mybatis.mapper.DepartmentMapper"> <resultMap id="dept" type="com.yefengyu.mybatis.entity.Department"> <id column="id" property="id"/> <result column="dept_name" property="deptName"/> <!-- 擴展:多列的值傳遞過去: 將多列的值封裝map傳遞:column="{key1=column1,key2=column2}" fetchType="lazy":表示使用延遲加載; - lazy:延遲 - eager:立即 --> <collection property="employees" select="com.yefengyu.mybatis.mapper.EmployeeMapper.getEmployee" column="id" fetchType="lazy"> </collection> </resultMap> <select id="getDeptById" resultMap="dept"> select id ,dept_name from department where id = #{id} </select> </mapper>
5、測試結果
Created connection 1694556038. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86] ==> Preparing: select id ,dept_name from department where id = ? ==> Parameters: 1(Integer) <== Columns: id, dept_name <== Row: 1, CIA <== Total: 1 ==> Preparing: select * from employee where dept_id = ? ==> Parameters: 1(Integer) <== Columns: id, last_name, dept_id <== Row: 1, Tom, 1 <== Row: 3, Neo, 1 <== Total: 2 Department{id=1, deptName='CIA', employees=[Employee{id=1, lastName='Tom', department=null}, Employee{id=3, lastName='Neo', department=null}]} Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
如果測試代碼的打印改為:
System.out.println(dept.getDeptName());
那么結果如下,不會查詢員工信息。
Created connection 1694556038. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86] ==> Preparing: select id ,dept_name from department where id = ? ==> Parameters: 1(Integer) <== Columns: id, dept_name <== Row: 1, CIA <== Total: 1 CIA Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86] Returned connection 1694556038 to pool.
全局配置與局部配置
1、全局配置
lazyLoadingEnabled:延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。
默認值:false
aggressiveLazyLoading:當開啟時,任何方法的調用都會加載該對象的所有屬性。 否則,每個屬性會按需加載(參考 lazyLoadTriggerMethods)。
默認值:false (在 3.4.1 及之前的版本默認值為 true),現在新版本可以不用關注此設置。
<setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/>
2、局部配置fetchType
<association property="" fetchType="eager"></association> <collection property="" fetchType="lazy"></collection>
- lazy:延遲
- eager:立即
3、區別(查詢部門信息,不查看員工信息時)
全局 | 局部 | 是否延遲 |
不開啟 | 不開啟 | 否 |
不開啟 | lazy | 是 |
不開啟 | eager | 否 |
開啟 | 不開啟 | 是 |
開啟 | lazy | 是 |
開啟 | eager | 否 |