【java框架】MyBatis(3)--Mapper映射关联及缓存

1.(多对一)关联映射

1.1.数据库映射关系介绍

多对一:

多个员工属于一个部门:单向的多对一关系,在Domain类中,在多方配置一个一方的属性。

数据库的表现:在多方有一个一方的外键。

一对多:

一个部门有多个员工:单向的一对多。在Domain中,在一方配置一个集合,集合的内容属性是多方。

数据库的表现:在多方有一个一方的外键。

多对多:

数据库使用中间表。Domain也是放一个集合属性。

一对一:

数据库的表现形式:类似于单向的多对一。

说明:现在基于多对一的情况,有用户和部门两个对象,一个部门有多个用户,这时需要从多方入手,即从用户方入手,查询一个用户,并得到用户所在的部门,就应该在用户所在的Domain类中增加一个部门Dept的字段。

1.2.多对一嵌套结果

创建对应多对一的Domain类:

Department类:

/**
 *  部门类:多对一情况,多个员工对应一个部门
 */
public class Department {
    private Long id;

    private String dName; //部门名称

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getdName() {
        return dName;
    }

    public void setdName(String dName) {
        this.dName = dName;
    }

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", dName='" + dName + '\'' +
                '}';
    }
}

Employee类:

/**
 * 员工类:基于多对一的情况进行创建:多个员工对应一个部门
 * 此时在多方有一个一方的属性
 */
public class Employee {
    private Long id;

    private String name;  //员工姓名

    private String password; //密码

    //单向的多对一
    private Department dept; //部门

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Department getDept() {
        return dept;
    }

    public void setDept(Department dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", dept=" + dept +
                '}';
    }
}

Mapper层:

EmployeeMapper:

public interface EmployeeMapper {
    Employee queryOneById(Long id);
}

EmployeeMapper.xml:

嵌套结果查询只需要发送一条Sql语句,使用左外连接left join去连接查询,再将查询到的结果Employee进行封装,注意相同列别名在column中的体现:

<?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">
<!-- namespace:这里使用Mapper映射代理接口,namespace对应mapper接口文件 -->
<mapper namespace="cn.yif.mybatis.manytoone.mapper.EmployeeMapper">
    <!--单向多对一情况-->
    <!--1.嵌套结果:使用一条sql进行联合查询-->
    <resultMap id="manytooneMap" type="cn.yif.mybatis.manytoone.domain.Employee">
        <!--对返回的Employee结果进行封装-->
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <result property="password" column="password"></result>
        <!--javaType:连接查询返回结果是一个什么类型-->
        <association property="dept" javaType="cn.yif.mybatis.manytoone.domain.Department">
            <!--返回的是一个连接查询到的Department对象,需要将属性封装成对象,注意column中
                如果取了别名就需要使用别名作为column列的列名-->
            <result property="id" column="did"></result>
            <result property="dName" column="dname"></result>
        </association>
    </resultMap>
    <select id="queryOneById" parameterType="Long" resultMap="manytooneMap">
        SELECT
        emp.id,
        emp.name,
        emp.password,
        dep.id did,
        dep.name dname
        FROM
        t_employee emp
        LEFT JOIN t_dept dep ON emp.dept_id = dep.id
        WHERE emp.id = #{id}
    </select>
</mapper>

1.3.多对一嵌套查询

Domain类创建与多对一嵌套结果查询相同,此处略。

Mapper层:

EmployeeMapper:

public interface EmployeeMapper {
Employee queryOneById(Long id);
Department queryDeptById(Long id);
}

EmployeeMapper.xml:

嵌套查询需要发送两条Sql,其中一条查询正常的t_employee中的数据列,另外一条查询到对应的t_dept中的数据列,使用select属性进行联接,将Domain中的dept对象进行返回映射,封装成需要的数据:

<?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">
<!-- namespace:这里使用Mapper映射代理接口,namespace对应mapper接口文件 -->
<mapper namespace="cn.yif.mybatis.manytoone.mapper2sql.EmployeeMapper">
    <!--单向多对一情况-->
    <!--2.嵌套查询:使用两条sql,其中一条查询出dept_id后使用另外一条sql进行关联查询name-->
    <resultMap id="manytooneMap" type="cn.yif.mybatis.manytoone.domain.Employee">
        <!-- 封装查询的Employee结果 -->
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <result property="password" column="password"></result>
        <!-- 将数据库dept_id的值再查询getDeptById,封装到dept属性中 -->
        <association property="dept" column="dept_id" select="queryDeptById">
        </association>
    </resultMap>
    <!--发送一条sql查询出Employee对象进行封装-->
    <select id="queryOneById" parameterType="Long" resultMap="manytooneMap">
        select id, name, password, dept_id from t_employee where id = #{id}
    </select>
    <!--发送一条sql查询出Department部门对象-->
    <select id="queryDeptById" parameterType="Long" resultType="cn.yif.mybatis.manytoone.domain.Department">
        select id, name dName from t_dept where id = #{id}
    </select>
</mapper>

2.(一对多)集合映射

2.1.数据库映射关系介绍

最常见的是员工和部门的关系:在部门方需要查询某一个部门下的所有员工。部门与员工之间的关系是一对多的关系。

基于多对一的关联查询的数据库表,一对多关注的重点是部门方,在部门一方中应该有一个多方员工的集合属性,用于与员工方进行关联,而数据库中的表现则是多方有一个一方的外键属性。

2.2.一对多嵌套结果

一对多关注的对象是部门,在部门Domain中有一个集合属性的员工对象;

Department类:

/**
 *  部门类:一对多情况,一个部门对应多个员工
 */
public class Department {
    private Long id;

    private String dName; //部门名称

    private List<Employee> employeeList; //一对多,在部门中有一个集合类型的员工属性
}

Employee类:

/**
 *  一对多情况,一个部门对应多个员工,关注点在部门方
 */
public class Employee {
    private Long id;

    private String name;  //员工姓名

    private String password; //密码

}

对应DepartmentMapper接口:

public interface DepartmentMapper {
    /**
     * 查询一个部门下的所有员工
     * @param id
     * @return
     */
    Department queryOneDeptById(Long id);
}

对应DepartmentMapper.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">
<!-- namespace:这里使用Mapper映射代理接口,namespace对应mapper接口文件 -->
<mapper namespace="cn.yif.mybatis.onetomany.mapper.DepartmentMapper">
    <!--单向一对多情况-->
    <!--1.嵌套结果:使用一条sql进行联合查询-->
    <resultMap id="onetomanyMap" type="cn.yif.mybatis.onetomany.domain.Department">
        <result property="id" column="dId"></result>
        <result property="dName" column="dName"></result>
        <!--嵌套结果一对多中使用:
        collection标签来封装集合属性;ofType表示集合中每个元素的类型-->
        <collection property="employeeList" ofType="cn.yif.mybatis.onetomany.domain.Employee">
            <result property="id" column="id"></result>
            <result property="name" column="name"></result>
            <result property="password" column="password"></result>
        </collection>
    </resultMap>
    <select id="queryOneDeptById" parameterType="Long" resultMap="onetomanyMap">
        select dept.id dId, dept.name dName, emp.id, emp.name, emp.password, emp.dept_id
        from t_dept dept left join t_employee emp on
        dept.id = emp.dept_id
        where dept.id = #{id}
    </select>
</mapper>

2.3.一对多嵌套查询

对应Domain层与一对多前台结果是一样的。

对应DepartmentMapper接口:

public interface DepartmentMapper {
    /**
     * 查询一个部门下的所有员工
     * @param id
     * @return
     */
    Department queryOneDeptById(Long id);

    List<Employee> queryAllEmployee();
}

对应DepartmentMapper.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">
<!-- namespace:这里使用Mapper映射代理接口,namespace对应mapper接口文件 -->
<mapper namespace="cn.yif.mybatis.onetomany.mapper2sql.DepartmentMapper">
    <!--单向一对多情况-->
    <!--1.嵌套查询:使用两条sql进行联合查询-->
    <resultMap id="onetomanyMap" type="cn.yif.mybatis.onetomany.domain.Department">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <!--这里的association实际上是去关联另一条sql,封装employeeList属性:
            column表示数据库联合查询要使用的列;select表示联合查询要使用到的查询语句-->
        <association property="employeeList" column="dept_id" select="queryAllEmployee"/>
    </resultMap>
    <select id="queryOneDeptById" parameterType="Long" resultMap="onetomanyMap">
        select id, name from t_dept where id = #{id}
    </select>
    <select id="queryAllEmployee">
        select id, name, password, dept_id from t_employee
    </select>
</mapper>

3.MyBatis缓存

3.1.SqlSession一级缓存

MyBastis中默认开启的一级缓存SqlSession,缓存对象存储周期为第一次获取,到SqlSession对象被销毁,或者是SqlSession.clearCache()调用的时候。作用是使用到同一个SqlSession获取查询语句的时候,不会下发多个查询语句,具体示例如下:

在EmployeeMapper接口中配置了查询所有的员工接口:

public interface EmployeeMapper {
    List<Employee> queryAll();
}

对应Mapper.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">
<!-- namespace:这里使用Mapper映射代理接口,namespace对应mapper接口文件 -->
<mapper namespace="cn.yif.mybatis.cache.mapper.EmployeeMapper">
    <!--特别注意:这里取到的返回值不应该是List本身,而是返回的集合中的元素类型-->
    <resultMap id="queryAllMap" type="cn.yif.mybatis.manytoone.domain.Employee">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <result property="password" column="password"></result>
        <association property="dept" javaType="cn.yif.mybatis.manytoone.domain.Department">
            <result property="id" column="dId"></result>
            <result property="dName" column="dName"></result>
        </association>
    </resultMap>
    <select id="queryAll" resultMap="queryAllMap">
       select emp.id, emp.name, emp.password, emp.dept_id,
       dept.id dId, dept.name dName from t_employee emp
       left join t_dept dept on
       emp.dept_id = dept.id
    </select>
</mapper>

编写对应测试类测试SqlSession一级缓存:

public class EmployeeMapperTest {
    @Test
    public void testCache(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        List<Employee> list = mapper.queryAll();
        System.out.println(list);
        System.out.println(list.size());
        System.out.println("===========华丽分割线============");
        sqlSession.getMapper(EmployeeMapper.class);
        list = mapper.queryAll();
        System.out.println(list.size());
    }
}

可以看到测试结果如下:

3.2.SqlSessionFactory二级缓存

MyBatis的二级缓存是SqlSessionFactory级别缓存,缓存对象存储周期为第一次获取,到SqlSessionFactory被销毁为止。

注意:

  1. 默认情况下,只开启一级缓存SqlSession,如果需要开启MyBatis二级缓存,需要我们在对应的Mapper.xml中添加一个<cache/>标签,开启二级缓存。
  2. 对应的Domain对象,实现java.io.Serializable接口。

猜你喜欢

转载自www.cnblogs.com/yif0118/p/12587008.html
今日推荐