MyBatis进阶

对象之间的关系:

关联关系: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(多对一)

项目结构划分 ![ ![Snip20170710_19.png](http://upload-images.jianshu.io/upload_images/5220087-b62f8650b7d08264.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](http://upload-images.jianshu.io/upload_images/5220087-623b65933633efe3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

domain文件夹:创建两个实体类 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 + "]";
}
}
mapper

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文件中找到对应的方法来执行相关方法

EmployeeMapper.xml
DepartmentMapper.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类


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方法来更新数据

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,176评论 5 469
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,190评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,232评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,953评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,879评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,177评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,626评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,295评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,436评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,365评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,414评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,096评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,685评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,771评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,987评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,438评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,032评论 2 341

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,537评论 18 399
  • 一. Java基础部分.................................................
    wy_sure阅读 3,784评论 0 11
  • 基本知识 resultMap constructor–实例化的时候通过构造器将结果集注入到类中oidArg– ID...
    stutterr阅读 670评论 0 0
  • 接着上一篇的进阶教程,继续学习Mybatis的一些常用用法。以便我们更好使用Mybatis。 知识点汇总 数据表一...
    Real_man阅读 642评论 2 4
  • 脑袋不清楚,不知道自己的位置,遇事不淡定,这是我最大的问题,简单说情商太低,每次和海星聊天,都能发现很多问题。我是...
    叶子卷阅读 195评论 1 1