对象之间的关系:
关联关系:A对象依赖B对象,并且把B对象作为A对象的一个属性,则A和B是依赖关系.
** 按照多重性分:**
1).一对一:一个A对象属于一个B对象,一个B对象属于一个A对象.
2).一对多:一个A对象包含多个B对象.
3).多对一:多个A对象属于一个B对象,并且每个A对象只能属于一个B对象.
4).多对多:一个A对象属于多个B对象,一个B对象属于多个A对象.
按照导航性分:如果通过A对象中的某一个属性可以访问该属性对应的B对象,则说A可以导航到B.
1).单向:只能从A通过属性导航到B,B不能导航到A.(较多)
2).双向:A可以通过属性导航到B,B也可以通过属性导航到A.
判断方法:
1,判断都是从对象的实例上面来看的;
2,判断关系必须确定一对属性;
3,判断关系必须确定具体需求;
无论是一对多还是多对一都应该在多方设置外键
单向many2one(多对一)
domain文件夹:创建两个实体类 Department和EmployeeDepartment.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);
//体现多对一
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" />
<!-- 配置多对一的关联映射关系
property:关联对象的属性名
javaType:关联对象的类型
select:查询到关联对象需要发送的SQL语句
column:指定执行该SQL需要的参数所在的列名-->
<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">
<!--内联查询方式 -->
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" /> -->
<!-- 指定扫描哪些包中的类,自动为其生成别名,默认是使用类的简单名称 -->
<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();
}
}
测试类
//添加操作主要是通过创建Employee对象和Department对象通过外键将两者关联起来
要先保存部门获取部门再保存员工 因为两者已经建立好关系了Employee对象会调用:
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into employee(name,dept_id)
values(#{name},#{dept.id})
</insert>
Department对象会调用:
<insert id="save" useGeneratedKeys="true" keyColumn="id"
keyProperty="id">
insert into department(name) values(#{name})
</insert>
同样get和list也会到mapper.xml文件中找到对应的方法来执行相关方法
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");
//建立两者之间的关系
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方的数据(不关心),那么此时我们就没有不要在查询many方的数据的时候去查询one方, 现在的情况是在查询many方的时候,都会发送额外的SQL去查询one方
解决方案:使用延迟加载
将查询one方的操作,往后延迟,延迟到我们去使用one方的时候,再去查询.在mybatis中使用延迟加载的方式(默认是禁用的)
<!-- 启用延迟加载 -->
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<!-- 设置为false之后表示在访问many方的属性(非关联的对象的属性)的时候不要触发延迟加载 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 当访问Object中的clone方法的时候触发延迟加载 -->
<setting name="lazyLoadTriggerMethods" value="clone" />
</settings>
注意:在mybatis中,访问one方任何属性,都会触发延迟加载
解决N+1问题
需求:在查询Many方的时候,会去关心one的数据,.此时,每次都需要去发送一条额外的SQL去查询数据
使用上面的方式,会出现N+1的问题
比如,当要查询100员工信息的时候,同时将员工所在的部门信息一起查询到,此时需要发送101条SQL去查询(每个员工所在的部门都不一样)
解决方案:
使用内联查询的方式解决
使用一条SQL语句将我们所需要的所有的数据一次性的查询到
SQL语句:select e.id,e.name, d.id, d.name from employee e left join department d on e.dept_id=d.id
单向one2many(一对多)
项目结构划分
domain实体类Department.java
@Setter@Getter
public class Department {
private Long id;
private String name;
//体现一对多
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 + "]";
}
}
创建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标签标记的参数封装到一个Map集合中(自动封装) 标签的value的值作为map的key,,参数真实的值作为mapvalue
注意:@Param注解在有多个参数的时候使用
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" />
<!-- 发送额外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+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);
// 发送一条更新的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();
}
}
在保存操作中先创建Employee对象和Department对象通过dept.setEmps(emps);将员工和部门关联起来然后保存员工和部门.但是在保存操作中 employee表中只保存了员工的name并没有将员工所在部门的部门编号保存下来.我们希望在保存员工的同时将部门编号保存到员工表中我们需要定义一个updateRelation方法来更新数据