1. 關聯關係 1.1 關聯關係概念說明 表與表之間的關係 : 1 對 1 1對多 多對多關係 ,通過主外鍵來實現。 外鍵在多的一方。比如員工和部門: 1個員工對應一個部門,一個部門可以有多個員工 要將表與表之間的關係:映射稱為 類與類之間的關係(準確的說應該是 對象和對象之間的關係 ) 一對一 夫 ...
1. 關聯關係
1.1 關聯關係概念說明
表與表之間的關係 : 1 對 1 1對多 多對多關係 ,通過主外鍵來實現。
外鍵在多的一方。比如員工和部門: 1個員工對應一個部門,一個部門可以有多個員工
要將表與表之間的關係:映射稱為 類與類之間的關係(準確的說應該是 對象和對象之間的關係 )
一對一
夫妻關係,人和身份證號的關係
一對多
部門 員工
多對多:一般都是引入第三張表來解決。
學生 課程 成績
老師 學生
2. 創建模型
create database mybatisdb03 default charset = utf8;
use mybatisdb03;
drop table sys_emp; drop table sys_dept;
create table sys_dept( dept_id bigint auto_increment, dept_name varchar(20), dept_createdate varchar(20), dept_tel varchar(20) unique, dept_status enum('y','n') default 'y', primary key(dept_id) );
INSERT INTO `sys_dept` VALUES (null, '學術部', '2011-1-1', '0710-312345',default); INSERT INTO `sys_dept` VALUES (null, '市場部', '2012-3-1', '0710-394566',default); INSERT INTO `sys_dept` VALUES (null, '教質部', '2012-1-1', '0710-335667',default); INSERT INTO `sys_dept` VALUES (null, '就業部', '2015-2-3', '0710-393568',default);
create table sys_emp( emp_id bigint auto_increment, emp_name varchar(20), emp_pwd varchar(20), emp_gender enum('m','f'), emp_salary double(10,5), emp_status enum('y','n') default 'y', dept_id bigint comment '外鍵', primary key(emp_id) );
INSERT INTO `sys_emp` VALUES (null, '範冰冰', 'fbb', 'f', 100.5,default,1); INSERT INTO `sys_emp` VALUES (null, '李冰冰', 'lbb', 'f', 300,default,2); INSERT INTO `sys_emp` VALUES (null, '張彬彬', 'zbb', 'm', 599,default,3); INSERT INTO `sys_emp` VALUES (null, '萬茜', 'wq', 'm', 4000,default,1); INSERT INTO `sys_emp` VALUES (null, '李若彤', 'lrt', 'm', 5000.8,default,1);
select * from `sys_dept`; select * from `sys_emp`; |
現在一般外面做項目,如果數據量過大,只建立外鍵,但是不建立外鍵約束。
package com.hy.bean;
import java.util.Date; import java.util.List;
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter;
@Setter @Getter @NoArgsConstructor @AllArgsConstructor public class Dept { private Long deptId; private String deptName; private Date deptCreatedate; private String deptTel; private String deptStatus;
//體現對多的關係 private List<Emp> empList;
//去ID,去關係的 public Dept(String deptName, Date deptCreatedate, String deptTel, String deptStatus) { super(); this.deptName = deptName; this.deptCreatedate = deptCreatedate; this.deptTel = deptTel; this.deptStatus = deptStatus; }
@Override public String toString() { return "Dept [deptId=" + deptId + ", deptName=" + deptName + ", deptCreatedate=" + deptCreatedate + ", deptTel=" + deptTel + ", deptStatus=" + deptStatus + ", empList=" + empList + "]"; } }
|
package com.hy.bean;
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter;
@Setter @Getter @NoArgsConstructor @AllArgsConstructor public class Emp { private Long empId; private String empName; private String empPwd; private String empGender; private Double empSalary; private String empStatus; private Long deptId;
// 體現對一的關係 private Dept dept;
public Emp(String empName, String empPwd, String empGender, Double empSalary, String empStatus, Long deptId) { super(); this.empName = empName; this.empPwd = empPwd; this.empGender = empGender; this.empSalary = empSalary; this.empStatus = empStatus; this.deptId = deptId; }
@Override public String toString() { return "Emp [empId=" + empId + ", empName=" + empName + ", empPwd=" + empPwd + ", empGender=" + empGender + ", empSalary=" + empSalary + ", empStatus=" + empStatus + ", deptId=" + deptId + ", dept=" + dept + "]"; } } |
2. 演示[一對多]關係,聯接查詢
關聯關係的方法,主要體現在Java實體類的對象中:
單向:雙方中只有一方能夠訪問到對方
Dept類的對象中empList屬性為null
Emp 類的對象中的dept的屬性為null
雙向:雙方都可以訪問到對方
Dept類的對象中empList屬性不為null,並且包含了該部門的員工對象
Emp 類的對象中的dept的屬性為不為null,指向了該員工對象的部門對象
3.1 對一的關係(單向)
我們希望查出Emp類的對象的時候,將其關聯的Dept信息也查詢出來,並且填充進去。
3.1.1 創建EmpMapper介面
package com.hy.mapper;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Emp;
public interface EmpMapper { abstract public Emp selectByIdWithDept(@Param("empId") long empId); }
|
3.1.2 創建EmpMapper.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">
<mapper namespace="com.hy.mapper.EmpMapper"> <resultMap id="empResultMap" type="com.hy.bean.Emp" > <!-- 映射Emp本身的屬性 --> <!-- property屬性:給Emp對象中的哪一個屬性設置數據 --> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="emp_pwd" property="empPwd"/> <result column="emp_gender" property="empGender"/> <result column="emp_salary" property="empSalary"/> <result column="emp_status" property="empStatus"/> <result column="dept_id" property="deptId"/>
<!-- association標簽,映射對一關聯關係:給Emp的dept屬性填充數據 --> <!-- javaType屬性:property屬性的類型,可省略 --> <association property="dept" javaType="com.hy.bean.Dept"> <id column="dept_id" property="deptId"/> <result column="dept_name" property="deptName"/> <result column="dept_createdate" property="deptCreatedate"/> <result column="dept_tel" property="deptTel"/> <result column="dept_status" property="deptStatus"/> </association> </resultMap>
<!-- 註意,這裡不能用resultType,否則無法將查詢出來的sys_dept表中的欄位映射到,映射Emp類的對象中--> <select id="selectByIdWithDept" resultMap="empResultMap"> select * from sys_emp emp inner join sys_dept dept on emp.dept_id = dept.dept_id where emp.emp_id = #{empId} </select> </mapper>
|
3.1.3 測試類:
@Test public void testSelectByIdWithDept() throws IOException { // 5. 通過sqlSession對象通過反射機制,直接生成一個EmpMapper介面的匿名類的對象 EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
// 6. 執行語句,列印結果 Emp emp = empMapper.selectByIdWithDept(1L); System.out.println(emp); System.out.println(emp.getDept()); } |
一般不會這麼設計在1的一端,比如部門,記住所有的員工,但是在多的一端不能記住1的一端。
讓姚明 記住 全國人民簡單 還是 讓全國人民記住 姚明簡單。
記住,在多的一端維護1的一端關係,比較簡單。
1 *
比如Dept--------------Emp ,Emp這邊是多的一端,讓Emp去維護 這個對1的關係。
3.2 對多的關係(單向)
在“對多”的關聯關係中,有很多配置,但是關鍵的是collection 和 ofType
3.2.1 創建DeptMapper介面
package com.hy.mapper;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Dept;
public interface DeptMapper { abstract public Dept selectByIdWithEmp(@Param("deptId") long deptId); } |
3.2.2 創建DeptMapper.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">
<mapper namespace="com.hy.mapper.DeptMapper"> <resultMap id="deptResultMap" type="com.hy.bean.Dept"> <id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/> <result column="dept_createdate" property="deptCreatedate"/> <result column="dept_tel" property="deptTel"/> <result column="dept_status" property="deptStatus"/>
<collection property="empList" ofType="com.hy.bean.Emp"> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="emp_pwd" property="empPwd"/> <result column="emp_gender" property="empGender"/> <result column="emp_salary" property="empSalary"/> <result column="emp_status" property="empStatus"/> <result column="dept_id" property="deptId"/> </collection> </resultMap>
<select id="selectByIdWithEmp" resultMap="deptResultMap"> select * from sys_emp emp left join sys_dept dept on emp.dept_id = dept.dept_id where dept.dept_id = #{deptId} </select> </mapper> |
3.2.3 測試類
package com.hy.mybatis.test;
import java.io.IOException;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test;
import com.hy.bean.Dept; import com.hy.mapper.DeptMapper;
public class TestDeptMapperPro { private SqlSession sqlSession;
// junit會在每一個@Test方法前, 執行@Before方法 @Before public void init() throws IOException { //1,2,3,4 sqlSession = new SqlSessionFactoryBuilder().build( Resources.getResourceAsStream("mybatis-config.xml") ) .openSession();
}
@Test public void testSelectByIdWithEmp() throws IOException { // 5. 通過sqlSession對象通過反射機制,直接生成一個EmpMapper介面的匿名類的對象 DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
// 6. 執行語句,列印結果 Dept dept = deptMapper.selectByIdWithEmp(1); System.out.println(dept); System.out.println(dept.getEmpList()); }
// junit會在每一個@Test方法後, 執行@After方法 @After public void clear() throws IOException { // 7. 提交事務 sqlSession.commit();
// 8. 關閉sqlSession sqlSession.close(); } } |
Dept [deptId=1, deptName=學術部, deptCreatedate=Sun Sep 29 00:00:00 CST 110, deptTel=0710-312345, deptStatus=y,
empList=[Emp [empId=1, empName=範冰冰, empPwd=fbb, empGender=f, empSalary=100.5, empStatus=y, deptId=1, dept=null], Emp [empId=4, empName=萬茜, empPwd=wq, empGender=m, empSalary=4000.0, empStatus=y, deptId=1, dept=null], Emp [empId=5, empName=李若彤, empPwd=lrt, empGender=m, empSalary=5000.8, empStatus=y, deptId=1, dept=null]]]
[Emp [empId=1, empName=範冰冰, empPwd=fbb, empGender=f, empSalary=100.5, empStatus=y, deptId=1, dept=null], Emp [empId=4, empName=萬茜, empPwd=wq, empGender=m, empSalary=4000.0, empStatus=y, deptId=1, dept=null], Emp [empId=5, empName=李若彤, empPwd=lrt, empGender=m, empSalary=5000.8, empStatus=y, deptId=1, dept=null]]
3.3 一對多的關係(雙向)
3.3.1 DeptMapper介面中的方法
package com.hy.mapper;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Dept;
public interface DeptMapper { abstract public Dept selectByIdWithEmp(@Param("deptId") long deptId);
abstract public Dept selectByIdWithEmpBoth(@Param("deptId") long deptId); } |
3.3.2 DeptMapper.xml
<!-- 分割線,雙線連接查詢 --> <resultMap id="deptWithEmpListResultMapBoth" type="com.hy.bean.Dept"> <id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/> <result column="dept_createdate" property="deptCreatedate"/> <result column="dept_tel" property="deptTel"/> <result column="dept_status" property="deptStatus"/>
<collection property="empList" ofType="com.hy.bean.Emp" resultMap="com.hy.mapper.EmpMapper.empResultMapBoth"/> </resultMap>
<select id="selectByIdWithEmpBoth" resultMap="deptWithEmpListResultMapBoth"> select * from sys_dept dept left join sys_emp emp on dept.dept_id = emp.dept_id where dept.dept_id = #{deptId} </select> |
3.3.3 EmpMapper.xml
<!-- 雙向關聯 --> <resultMap id="empResultMapBoth" type="com.hy.bean.Emp" > <!-- 映射Emp本身的屬性 --> <!-- property屬性:給Emp對象中的哪一個屬性設置數據 --> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="emp_pwd" property="empPwd"/> <result column="emp_gender" property="empGender"/> <result column="emp_salary" property="empSalary"/> <result column="emp_status" property="empStatus"/> <result column="dept_id" property="deptId"/>
<!-- association標簽,映射對一關聯關係:給Emp的dept屬性填充數據 --> <!-- javaType屬性:property屬性的類型,可省略 --> <association property="dept" javaType="com.hy.bean.Dept" resultMap="com.hy.mapper.DeptMapper.deptWithEmpListResultMapBoth" /> </resultMap> |
3.3.4 測試類:
@Test public void selectByIdWithEmpBoth() throws IOException { // 5. 通過sqlSession對象通過反射機制,直接生成一個EmpMapper介面的匿名類的對象 DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
// 6. 執行語句,列印結果 Dept dept = deptMapper.selectByIdWithEmpBoth(1L); System.out.println(dept); System.out.println(dept.getEmpList()); } |
4. 演示[1對多],分步查詢
上面不論是從Dept查Emp,還是從Emp查Dept,都是通過一條連接查詢的SQL語句來完成的,將dept和emp的信息一起查出來了,然後通過mybatis組裝起來。此時就有一個問題,查詢出來的關聯對象不一定會被用到。查詢dept的時候,不管emp用不用,都會將emp查詢出來,並封裝起來。此時如果查詢出來,但是暫時又用不上,然後你又裝配起來了,此時就會造成記憶體你的浪費,但是如果你不查出來,但是萬一又用了,此時又沒有。
此時,我希望既能維持關聯關係,又希望在用的時候才將其查詢出來,不用的時候,不查。這就叫延遲載入—也叫懶載入。就是到使用的時候才用將其查詢出來。使用場景:
所以分佈查詢是懶載入的基礎。
4.1 實現
為了實現懶載入—延遲載入,對sys_dept和sys_emp的查詢必須分開,分成兩步來做,才能夠實現。為此,我們需要單獨查詢sys_dept 和 sys_emp的SQL語句。
4.2 分佈對1的關係:
4.2.1 bean的寫法
package com.hy.bean;
import java.util.Date; import java.util.List;
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter;
@Setter @Getter @NoArgsConstructor @AllArgsConstructor public class Dept { private Long deptId; private String deptName; private Date deptCreatedate; private String deptTel; private String deptStatus;
//體現對多的關係 private List<Emp> empList;
//去ID,去關係的 public Dept(String deptName, Date deptCreatedate, String deptTel, String deptStatus) { super(); this.deptName = deptName; this.deptCreatedate = deptCreatedate; this.deptTel = deptTel; this.deptStatus = deptStatus; }
@Override public String toString() { return "Dept [deptId=" + deptId + ", deptName=" + deptName + ", deptCreatedate=" + deptCreatedate + ", deptTel=" + deptTel + ", deptStatus=" + deptStatus;// + ", empList=" + empList + "]"; } }
|
package com.hy.bean;
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter;
@Setter @Getter @NoArgsConstructor @AllArgsConstructor public class Emp { private Long empId; private String empName; private String empPwd; private String empGender; private Double empSalary; private String empStatus; private Long deptId;
// 體現對一的關係 private Dept dept;
//去ID的,去關係 public Emp(String empName, String empPwd, String empGender, Double empSalary, String empStatus, Long deptId) { super(); this.empName = empName; this.empPwd = empPwd; this.empGender = empGender; this.empSalary = empSalary; this.empStatus = empStatus; this.deptId = deptId; }
@Override public String toString() { return "Emp [empId=" + empId + ", empName=" + empName + ", empPwd=" + empPwd + ", empGender=" + empGender + ", empSalary=" + empSalary + ", empStatus=" + empStatus + ", deptId=" + deptId ; // "+ , dept=" + dept + "]"; } } |
4.2.2 mapper介面的寫法
package com.hy.mapper;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Dept;
public interface DeptMapper { abstract public Dept selectByIdWithTwoStep(@Param("deptId") long deptId); } |
package com.hy.mapper;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Emp;
public interface EmpMapper { abstract public Emp selectByIdWithDeptTwoStep(@Param("empId") long empId); } |
4.2.3 映射的文件的寫法
<?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.hy.mapper.DeptMapper"> <!-- 定位到當前SQL語句的方式:com.hy.mapper.DeptMapper.selectById --> <!-- 這條語句僅僅是根據id查詢部門 --> <select id="selectById" resultType="com.hy.bean.Dept"> select dept_id,dept_name,dept_createdate,dept_tel,dept_status from sys_dept where dept_id = #{deptId} </select>
<!-- 兩步 --> <resultMap type="com.hy.bean.Dept" id="deptResultMapBoth"> <id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/> <result column="dept_createdate" property="deptCreatedate"/> <result column="dept_tel" property="deptTel"/> <result column="dept_status" property="deptStatus"/>
<collection property="empList" column="dept_id" ofType="com.hy.bean.Emp" select="com.hy.mapper.EmpMapper.selectByFid"/>
</resultMap> <select id="selectByIdWithTwoStep" resultMap="deptResultMapBoth"> select dept_id,dept_name,dept_createdate,dept_tel,dept_status from sys_dept where dept_id = #{deptId} </select> </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.hy.mapper.EmpMapper"> <resultMap id="empResultMap" type="com.hy.bean.Emp" > <!-- 映射Emp本身的屬性 --> <!-- property屬性:給Emp對象中的哪一個屬性設置數據 --> <id column="emp_id" property="empId"/> <result column="emp_name" property="empName"/> <result column="emp_pwd" property="empPwd"/> <result column="emp_gender" property="empGender"/> <result column="emp_salary" property="empSalary"/> <result column="emp_status" property="empStatus"/> <result column="dept_id" property="deptId"/>
<!-- association標簽,映射對一關聯關係:給Emp的dept屬性填充數據 --> <!-- select屬性:定位到另外一條專門查詢dept的SQL語句--> <!-- column屬性:指定用來給查詢 sys_dept 的SQL語句傳參的欄位--> <association property="dept" column="dept_id" select="com.hy.mapper.DeptMapper.selectById" /> </resultMap>
<select id="selectByIdWithDeptTwoStep" resultMap="empResultMap"> select * from sys_emp where emp_id = #{empId} </select>
<select id="selectByFid" resultType="com.hy.bean.Emp"> select * from sys_emp where dept_id = #{deptId} </select> </mapper> |
4.2.4 mybaits-config.xml全局配置文件中註意
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> |
<?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> <properties resource="jdbc.properties"/>
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
<environments default="development"> <!-- environment表示配置Mybaits的一個具體的環境 --> <environment id="development"> <!-- Mybaits的內置的事務管理器 --> <transactionManager type="JDBC" /> <!-- 配置數據源,這裡使用的是Mybaits內置的數據源--> <dataSource type="POOLED"> <!-- 建立資料庫連接的具體信息 --> <property name="driver" value="${mybatis03.dev.driver}" /> <property name="url" value="${mybatis03.dev.url}" /> <property name="username" value="${mybatis03.dev.username}" /> <property name="password" value="${mybatis03.dev.password}" /> </dataSource> </environment> </environments> <!-- 設置映射文件的路徑,還沒有寫 --> <mappers> <mapper resource="mappers/EmpMapper.xml" /> <mapper resource="mappers/DeptMapper.xml" /> </mappers> </configuration> |
4.2.5 測試類
package com.hy.mybatis.test;
import java.io.IOException;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test;
import com.hy.bean.Dept; import com.hy.bean.Emp; import com.hy.mapper.DeptMapper;
public class TestDeptMapperPro { private SqlSession sqlSession;
// junit會在每一個@Test方法前, 執行@Before方法 @Before public void init() throws IOException { //1,2,3,4 sqlSession = new SqlSessionFactoryBuilder().build( Resources.getResourceAsStream("mybatis-config.xml") ) .openSession();
}
@Test public void testSelectByIdWithTwoStep() throws IOException { // 5. 通過sqlSession對象通過反射機制,直接生成一個EmpMapper介面的匿名類的對象 DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
// 6. 執行語句,列印結果 Dept dept = deptMapper.selectByIdWithTwoStep(1L); System.out.println(dept); System.out.println(dept.getEmpList()); }
// junit會在每一個@Test方法後, 執行@After方法 @After public void clear() throws IOException { // 7. 提交事務 sqlSession.commit();
// 8. 關閉sqlSession sqlSession.close(); } } |
package com.hy.mybatis.test;
import java.io.IOException;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test;
import com.hy.bean.Emp; import com.hy.mapper.EmpMapper;
public class TestEmpMapperPro { private SqlSession sqlSession;
// junit會在每一個@Test方法前, 執行@Before方法 @Before public void init() throws IOException { //1,2,3,4 sqlSession = new SqlSessionFactoryBuilder().build( Resources.getResourceAsStream("mybatis-config.xml") ) .openSession();
}
@Test public void testSelectByIdWithDept() throws IOException { // 5. 通過sqlSession對象通過反射機制,直接生成一個EmpMapper介面的匿名類的對象 EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
// 6. 執行語句,列印結果 Emp emp = empMapper.selectByIdWithDeptTwoStep(1); System.out.println(emp); System.out.println(emp.getDept()); }
// junit會在每一個@Test方法後, 執行@After方法 @After public void clear() throws IOException { // 7. 提交事務 sqlSession.commit();
// 8. 關閉sqlSession sqlSession.close(); } } |
4.3 圖解:
4.4 懶查詢
配置fetchType=”lazy”