2015年7月25日 星期六

雙向一對多關聯 Mybatis3.x(七)

※事前規劃

一個部門有很多員工,多個員工屬於一個部門
DROP TABLE DEPARTMENT;
DROP TABLE EMPLOYEE;
DROP SEQUENCE EMPLOYEE_SEQUENCE;

CREATE TABLE DEPART(
    DEPTNO NUMBER(9),
    DNAME  VARCHAR(20),
    CONSTRAINT DEPART_PK PRIMARY KEY(DEPTNO)
);
COMMENT ON TABLE DEPART IS '部門表';
COMMENT ON COLUMN DEPART.DEPTNO IS '部門代號';
COMMENT ON COLUMN DEPART.DNAME IS '部門名稱';

CREATE TABLE EMPLOYEE(
    EMPNO     NUMBER(9),
    ENAME     VARCHAR(10),
    DEPTNO    NUMBER(9),
    CONSTRAINT EMPLOYEE_PK PRIMARY KEY(EMPNO),
    CONSTRAINT EMPLOYEE_FK FOREIGN KEY(DEPTNO) REFERENCES DEPART(DEPTNO) ON DELETE CASCADE
);
COMMENT ON TABLE EMPLOYEE IS '員工表';
COMMENT ON COLUMN EMPLOYEE.EMPNO IS '員工編號';
COMMENT ON COLUMN EMPLOYEE.ENAME IS '員工姓名';
COMMENT ON COLUMN EMPLOYEE.DEPTNO IS '部門代號';

CREATE SEQUENCE EMPLOYEE_SEQUENCE;

INSERT INTO DEPART VALUES(10, '財務部');
INSERT INTO DEPART VALUES(20, '研發部');
INSERT INTO DEPART VALUES(30, '業務部');
INSERT INTO DEPART VALUES(40, '生管部');

INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'king', 10);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'blake', 30);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'clark', 10);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'jones', 20);

INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'martin', 30);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'allen', 30);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'turner', 30);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'james', 30);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'ward', 30);

INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'ford', 20);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'smith', 20);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'scott', 20);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'adams', 20);
INSERT INTO EMPLOYEE VALUES(EMPLOYEE_SEQUENCE.NEXTVAL, 'miller', 20);

※開始戰鬥

兩張類也加一下,一對多可用List或Set
public class Department {
    private Integer deptno;
    private String dname;
    private Set employees;
    //setter/getter...
}

public class Employee {
    private Integer empno;
    private String ename;
    private Integer deptno;
    private Department department;
    //setter/getter...
}

Employee.xml是多對一,所以要設定「一」
<mapper namespace="Emp">
    <sql id="column">
        EMPNO, ENAME, DEPTNO
    </sql>

    <resultMap type="Employee" id="EmpInterface">
        <id property="empno" column="EMPNO" />
        <result property="ename" column="ENAME" />
        <result property="deptno" column="DEPTNO" />

        <!-- 多個員工有「一」個部門 -->
        <association property="department" column="DEPTNO"
            javaType="Department" select="Dept.findById" />
        <!-- resultMap="Dept.DeptInterface" /> 用這個非關聯字段會null,和select擇其一-->
    </resultMap>

    <select id="findAllByDepartment" resultMap="EmpInterface">
        SELECT <include refid="column" />
        FROM EMPLOYEE
        <where>
            DEPTNO = #{ooxx}
        </where>
    </select>

    <select id="findById" parameterType="java.lang.Integer"
        resultMap="EmpInterface">
        SELECT <include refid="column" />
        FROM EMPLOYEE
        <where>
            EMPNO = #{ooo}
        </where>
    </select>
</mapper>

Department.xml是一對多,所以要設定「多」,這是一對一沒有的
javaType="java.util.Set" ofType="Employee",其實就是Set<Employee>
<mapper namespace="Dept">
    <sql id="column">
        DEPTNO, DNAME
    </sql>

    <resultMap type="Department" id="DeptInterface">
        <id property="deptno" column="DEPTNO" />
        <result property="dname" column="DNAME" />

        <!-- 一個部門有很「多」員工 -->
        <collection property="employees" column="DEPTNO" javaType="java.util.Set"
            ofType="Employee" select="Emp.findAllByDepartment" />
    </resultMap>

    <select id="findById" parameterType="java.lang.Integer"
        resultMap="DeptInterface">
        SELECT <include refid="column" />
        FROM DEPARTMENT
        <where>
            DEPTNO = #{xxx}
        </where>
    </select>
</mapper>

測試類:
SqlSession sqlSession = MybatisUtil.getSession();

System.out.println("--------------多對一測試--------------");
Employee e = sqlSession.selectOne("Emp.findById", 2);
System.out.println(e.getEmpno());
System.out.println(e.getEname());
System.out.println(e.getDeptno());

// 如果Employee.xml的resultMap的association用resultMap,非關聯字段會null;
Department dept = e.getDepartment();
System.out.println(dept.getDeptno());
System.out.println(dept.getDname() + "\r\n");//非關聯字段

System.out.println("--------------一對多測試--------------");
Department d = sqlSession.selectOne("Dept.findById", 20);
System.out.println(d.getDeptno());
System.out.println(d.getDname() + "\r\n");

Set<Employee> l = d.getEmployees();
for (Employee emp : l) {
    System.out.println(emp.getEmpno());
    System.out.println(emp.getEname());
    System.out.println(emp.getDeptno());
    System.out.println("================================");
}

sqlSession.commit();
MybatisUtil.closeSession();




※一對多關聯的兩種方式


<resultMap type="ooo" id="collection1">
    <!-- java bean 就算和資料庫名稱一樣也要寫,不寫就是 null -->
    <id column="deptno" property="deptNo" />
    <result column="dname" property="dName" />
    <!-- <result column="loc" property="loc" /> -->
    
    <collection property="emps" javaType="java.util.Set" ofType="mp.bean.Emp">
        <!-- java bean 就算和資料庫名稱一樣也要寫,不寫就是 null -->
        <id column="empno" property="empno" />
        <result column="ename" property="ename" />
    </collection>
</resultMap>
    
<select id="getDeptAndEmpsById" resultMap="collection1">
    select empno, ename, job, mgr, hiredate, sal, comm, e.deptno, 
        d.dname, d.loc
    from emp e, dept d 
    where e.deptno = d.deptno
    and d.deptno = #{id}
</select>
    
<resultMap type="ooo" id="collection2">
    <!-- java bean 和 資料庫名稱一樣可不寫,但關聯字段一定要 -->
    <id column="deptno" property="deptNo" />
    <result column="dname" property="dName" />
    <!-- <result column="loc" property="loc" /> -->
    
    <collection property="emps" select="mp.bean.dao.EmpMapper.getEmpByDeptno" 
            javaType="java.util.Set" column="deptno" fetchType="lazy">
        <!-- java bean 和 資料庫名稱一樣可不寫 -->
        <id column="empno" property="empno" />
        <result column="ename" property="ename" />
    </collection>
</resultMap>
    
<select id="getDept2Step" resultMap="collection2">
    select * from dept where deptno = #{id}
</select>

※一對一用的是 association;一對多用的是 collection,一對多只有兩種關聯方式,沒有用「.」的方式

※第一種是 SQL 的功,連 resultMap 裡的東西全部都要打才會有值

※第二種是分步的方式,和資料庫名稱一樣可不打,一樣和懶加載有關

※javaType 可以不寫,不管用 List 或 Set 去接都不會有問題,但要寫就一定要寫正確

沒有留言:

張貼留言