對象之間的關(guān)系:
關(guān)聯(lián)關(guān)系:A對象依賴B對象,并且把B對象作為A對象的一個屬性,則A和B是依賴關(guān)系.
** 按照多重性分:**
1).一對一:一個A對象屬于一個B對象,一個B對象屬于一個A對象.
2).一對多:一個A對象包含多個B對象.
3).多對一:多個A對象屬于一個B對象,并且每個A對象只能屬于一個B對象.
4).多對多:一個A對象屬于多個B對象,一個B對象屬于多個A對象.
按照導(dǎo)航性分:如果通過A對象中的某一個屬性可以訪問該屬性對應(yīng)的B對象,則說A可以導(dǎo)航到B.
1).單向:只能從A通過屬性導(dǎo)航到B,B不能導(dǎo)航到A.(較多)
2).雙向:A可以通過屬性導(dǎo)航到B,B也可以通過屬性導(dǎo)航到A.
判斷方法:
1,判斷都是從對象的實例上面來看的;
2,判斷關(guān)系必須確定一對屬性;
3,判斷關(guān)系必須確定具體需求;
無論是一對多還是多對一都應(yīng)該在多方設(shè)置外鍵
單向many2one(多對一)
domain文件夾:創(chuàng)建兩個實體類 Department和Employee
Department.java
@Setter@Getter
public class Department {
private Long id;
private String name;
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + "]";
}
}
Employee.java
@Setter@Getter
public class Employee {
private Long id;
private String name;
private Department dept;
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
}
EmployeeMapper.java
public interface EmployeeMapper {
void save(Employee e);
Employee get(Long id);
//體現(xiàn)多對一
List<Employee> list();
}
DepartmentMapper.java
public interface DepartmentMapper {
void save(Department dept);
Department get(Long id);
List<Department> list();
}
EmployeeMapper.xml
<mapper namespace="com.jd.many2one.mapper.EmployeeMapper">
<resultMap type="com.jd.many2one.domain.Employee" id="base_mapper">
<id property="id" column="id" />
<result property="name" column="name" />
<!-- 配置多對一的關(guān)聯(lián)映射關(guān)系
property:關(guān)聯(lián)對象的屬性名
javaType:關(guān)聯(lián)對象的類型
select:查詢到關(guān)聯(lián)對象需要發(fā)送的SQL語句
column:指定執(zhí)行該SQL需要的參數(shù)所在的列名-->
<association property="dept" javaType="com.jd.many2one.domain.Department">
<result property="id" column="did" />
<result property="name" column="dname" />
</association>
<!-- <association property="dept"
javaType="com.jd.many2one.domain.Department">
<result property="id" column="did" />
<result property="name" column="dname" />
</association> -->
</resultMap>
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into employee(name,dept_id)
values(#{name},#{dept.id})
</insert>
<select id="get" resultMap="base_mapper">
select * from employee where id=#{id}
</select>
<select id="list" resultMap="base_mapper">
<!--內(nèi)聯(lián)查詢方式 -->
select e.id,e.name,d.id did,d.name
dname from employee e left join
department d on e.dept_id=d.id
</select>
</mapper>
DepartmentMapper.xml
<mapper namespace="com.jd.many2one.mapper.DepartmentMapper">
<resultMap type="com.jd.many2one.domain.Department" id="base_mapper">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into department(name) values(#{name})
</insert>
<select id="get" resultMap="base_mapper">
select *from department where
id=#{id}
</select>
<select id="list" resultMap="base_mapper">
select * from department
</select>
</mapper>
配置文件mybatis-config.xml
<!-- 取別名 -->
<typeAliases>
<!-- <typeAlias type="com.jd.pss.domain.User" alias="User" /> -->
<!-- 指定掃描哪些包中的類,自動為其生成別名,默認(rèn)是使用類的簡單名稱 -->
<package name="com.jd.pss.domain" />
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
//映射器
<mappers>
<mapper resource="com/jd/one2many/mapper/DepartmentMapper.xml" />
<mapper resource="com/jd/one2many/mapper/EmployeeMapper.xml" />
<mapper resource="com/jd/many2one/mapper/EmployeeMapper.xml" />
<mapper resource="com/jd/many2one/mapper/DepartmentMapper.xml" />
</mappers>
</configuration>
工具類MyBatisUtil.java
public class MyBatisUtil {
private MyBatisUtil() {
}
private static SqlSessionFactory fac;
static {
try {
fac = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession() {
return fac.openSession();
}
}
測試類
//添加操作主要是通過創(chuàng)建Employee對象和Department對象通過外鍵將兩者關(guān)聯(lián)起來
要先保存部門獲取部門再保存員工 因為兩者已經(jīng)建立好關(guān)系了Employee對象會調(diào)用:
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into employee(name,dept_id)
values(#{name},#{dept.id})
</insert>
Department對象會調(diào)用:
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into department(name) values(#{name})
</insert>
同樣get和list也會到mapper.xml文件中找到對應(yīng)的方法來執(zhí)行相關(guān)方法
public class Many2OneTest
public class Many2OneTest {
@Test
public void testSave() {
Employee e = new Employee();
e.setName("CoderZS");
Employee e2 = new Employee();
e2.setName("CoderJ");
Department dept = new Department();
dept.setName("UI");
//建立兩者之間的關(guān)系
e.setDept(dept);
e2.setDept(dept);
//先保存部門,獲取到部門的編號
SqlSession session = MyBatisUtil.getSession();
DepartmentMapper deptMapper = session.getMapper(DepartmentMapper.class);
deptMapper.save(dept);
//再保存員工,使用上面的部門編號
EmployeeMapper empMapper = session.getMapper(EmployeeMapper.class);
empMapper.save(e);
empMapper.save(e2);
session.commit();
session.close();
}
@Test
public void testGet() {
SqlSession session = MyBatisUtil.getSession();
EmployeeMapper empMapper = session.getMapper(EmployeeMapper.class);
Employee emp = empMapper.get(41L);
System.out.println(emp);
//清空一級緩存
session.clearCache();
Employee emp2 = empMapper.get(41L);
System.out.println(emp2);
session.close();
}
@Test
public void testList() {
SqlSession session = MyBatisUtil.getSession();
EmployeeMapper empMapper = session.getMapper(EmployeeMapper.class);
List<Employee> emps = empMapper.list();
for (Employee employee : emps) {
System.out.println(employee + ":" + employee.getDept().getName());
}
}
}
什么是延遲加載?
在查詢many方的時候,很可能不需要查詢到one方的數(shù)據(jù)(不關(guān)心),那么此時我們就沒有不要在查詢many方的數(shù)據(jù)的時候去查詢one方, 現(xiàn)在的情況是在查詢many方的時候,都會發(fā)送額外的SQL去查詢one方
解決方案:使用延遲加載
將查詢one方的操作,往后延遲,延遲到我們?nèi)ナ褂胦ne方的時候,再去查詢.在mybatis中使用延遲加載的方式(默認(rèn)是禁用的)
<!-- 啟用延遲加載 -->
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<!-- 設(shè)置為false之后表示在訪問many方的屬性(非關(guān)聯(lián)的對象的屬性)的時候不要觸發(fā)延遲加載 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 當(dāng)訪問Object中的clone方法的時候觸發(fā)延遲加載 -->
<setting name="lazyLoadTriggerMethods" value="clone" />
</settings>
注意:在mybatis中,訪問one方任何屬性,都會觸發(fā)延遲加載
解決N+1問題
需求:在查詢Many方的時候,會去關(guān)心one的數(shù)據(jù),.此時,每次都需要去發(fā)送一條額外的SQL去查詢數(shù)據(jù)
使用上面的方式,會出現(xiàn)N+1的問題
比如,當(dāng)要查詢100員工信息的時候,同時將員工所在的部門信息一起查詢到,此時需要發(fā)送101條SQL去查詢(每個員工所在的部門都不一樣)
解決方案:
使用內(nèi)聯(lián)查詢的方式解決
使用一條SQL語句將我們所需要的所有的數(shù)據(jù)一次性的查詢到
SQL語句:select e.id,e.name, d.id, d.name from employee e left join department d on e.dept_id=d.id
單向one2many(一對多)
項目結(jié)構(gòu)劃分
domain實體類
Department.java
@Setter@Getter
public class Department {
private Long id;
private String name;
//體現(xiàn)一對多
private List<Employee> emps=new ArrayList<>();
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + "]";
}
}
Employee.java
@Setter@Getter
public class Employee {
private Long id;
private String name;
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
}
創(chuàng)建mapper類
DepartmentMapper.java
接口
public interface DepartmentMapper {
void save(Department dept);
Department get(Long id);
List<Department> list();
void deoete(Long id);
}
EmployeeMapper.java
接口
public interface EmployeeMapper {
void save(Employee e);
Employee get(Long id);
List<Employee> list();
//更新員工和部門的信息
void updateRelation(@Param("did")Long id1,@Param("eid")Long id2);
void deleteByDeptId(Long id);
}
mybatis自動將使用@Param標(biāo)簽標(biāo)記的參數(shù)封裝到一個Map集合中(自動封裝) 標(biāo)簽的value的值作為map的key,,參數(shù)真實的值作為mapvalue
注意:@Param注解在有多個參數(shù)的時候使用
DepartmentMapper.xml
<mapper namespace="com.jd.one2many.mapper.DepartmentMapper">
<resultMap type="com.jd.one2many.domain.Department" id="base_mapper">
<id property="id" column="id" />
<result property="name" column="name" />
<!-- 發(fā)送額外sql -->
<!-- <collection property="emps" ofType="com.jd.one2many.domain.Employee"
select="com.jd.one2many.mapper.EmployeeMapper.getByDeptId" column="id"> </collection> -->
<collection property="emps" ofType="com.jd.one2many.domain.Employee">
<result property="id" column="eid" />
<result property="name" column="ename" />
</collection>
</resultMap>
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into department(name) values(#{name})
</insert>
<select id="get" resultMap="base_mapper">
<!--使用內(nèi)聯(lián)方式 解決N+1問題 -->
select e.id eid,e.name ename,d.id,d.name from department d left join
employee e on d.id=e.dept_id
</select>
<select id="list" resultMap="base_mapper">
select * from department
</select>
<delete id="delete">
delete * from department where id=#{id}
</delete>
</mapper>
EmployeeMapper.xml
<mapper namespace="com.jd.one2many.mapper.EmployeeMapper">
<resultMap type="com.jd.one2many.domain.Employee" id="base_mapper">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into employee(name) values(#{name})
</insert>
<select id="get" resultMap="base_mapper">
select * from employee where id=#{id}
</select>
<select id="list" resultMap="base_mapper">
select * from employee
</select>
<update id="updateRelation">
update employee set dept_id=#{did} where id=#{eid}
</update>
<select id="getByDeptId" resultMap="base_mapper">
select * from department where dept_id=#{deptId}
</select>
<delete id="deleteByDeptId">
delete from employee where dept_id=#{deptId}
</delete>
</mapper>
測試類One2ManyTest
測試類One2ManyTest
public class One2ManyTest {
@Test
public void testSave() {
Employee e = new Employee();
e.setName("LL");
Employee e2 = new Employee();
e2.setName("KK");
Department dept = new Department();
dept.setName("develop");
List<Employee> emps = new ArrayList<>();
emps.add(e);
emps.add(e2);
dept.setEmps(emps);
// 保存員工
SqlSession session = MyBatisUtil.getSession();
EmployeeMapper empMapper = session.getMapper(EmployeeMapper.class);
empMapper.save(e);
empMapper.save(e2);
// 保存部門
DepartmentMapper deptMapper = session.getMapper(DepartmentMapper.class);
deptMapper.save(dept);
// 發(fā)送一條更新的SQL將部門編號更新到員工表中
for (Employee employee : dept.getEmps()) {
empMapper.updateRelation(dept.getId(), employee.getId());
}
session.commit();
session.close();
}
@Test
public void testList(){
SqlSession session = MyBatisUtil.getSession();
DepartmentMapper deptMapper = session.getMapper(DepartmentMapper.class);
List<Department> depts=deptMapper.list();
for (Department department : depts) {
System.out.println(department);
}
}
@Test
public void testGet() throws Exception {
SqlSession session = MyBatisUtil.getSession();
DepartmentMapper deptMapper = session.getMapper(DepartmentMapper.class);
Department dept = deptMapper.get(17L);
System.out.println(dept);
session.close();
}
@Test
public void testDelete(){
SqlSession session =MyBatisUtil.getSession();
EmployeeMapper empMapper=session.getMapper(EmployeeMapper.class);
empMapper.deleteByDeptId(16L);
DepartmentMapper deptMapper=session.getMapper(DepartmentMapper.class);
deptMapper.deoete(16L);
session.commit();
session.close();
}
}
在保存操作中先創(chuàng)建Employee對象和Department對象通過dept.setEmps(emps);將員工和部門關(guān)聯(lián)起來然后保存員工和部門.但是在保存操作中 employee表中只保存了員工的name并沒有將員工所在部門的部門編號保存下來.我們希望在保存員工的同時將部門編號保存到員工表中我們需要定義一個updateRelation方法來更新數(shù)據(jù)