2015年7月23日 星期四

雙向一對一關聯 Mybatis3.x(六)

※事前規劃

假裝有一個USER_NAME表,因為之前沒規劃好,想多加幾個欄位,怕在這張表增加以後會影響現有的行為,所以增加一張表,叫USER_DATA,這樣就成了一個一對一的關聯
CREATE TABLE USER_NAME(
    USERID            VARCHAR(10) ,
    NAME            VARCHAR(20) ,
    CONSTRAINT USER_PK PRIMARY KEY(USERID)
);

CREATE TABLE USER_DATA(
    USERID            VARCHAR(10) ,
    AGE                NUMBER(3) ,
    BIRTHDAY        DATE ,
    CONSTRAINT USER_DATA_PK PRIMARY KEY(USERID) ,
    CONSTRAINT USER_DATA_FK FOREIGN KEY(USERID) REFERENCES USER_NAME(USERID) ON DELETE CASCADE
);

COMMENT ON TABLE USER_DATA IS '使用者資料表';
COMMENT ON COLUMN USER_DATA.USERID IS '使用者id';
COMMENT ON COLUMN USER_DATA.AGE IS '使用者年齡';
COMMENT ON COLUMN USER_DATA.BIRTHDAY IS '使用者生日';

COMMENT ON TABLE USER_NAME IS '使用者名稱表';
COMMENT ON COLUMN USER_NAME.USERID IS '使用者id';
COMMENT ON COLUMN USER_NAME.NAME IS '使用者姓名';

INSERT INTO USER_NAME(USERID, NAME)VALUES(1, 'xxx');
INSERT INTO USER_DATA(USERID, AGE, BIRTHDAY)VALUES(1, 20, TO_DATE('19990125', 'YYYYMMDD'));


因為是雙向,所以兩張java都各增加一個屬性連到對方:
public class UserName {
    private String userId;
    private String name;
    private UserData userData;
    //setter/getter...
}

public class UserData {
    private String userId;
    private Integer age;
    private Date birthday;
    private UserName userName;
    //setter/getter...
}

※association設定

因為是雙向,兩張xml都要設定,UserName.xml:
<mapper namespace="ooo.UserName">
    <resultMap type="UserName" id="mapperUserName">
        <id property="userId" column="USERID" />
        <result property="name" column="NAME" />
        
        <association property="userData" column="USERID" javaType="UserData"  select="xxx.UserData.getUserDataById"/>
    </resultMap>

    <select id="getUserNameById" parameterType="int" resultMap="mapperUserName">
        SELECT * FROM USER_NAME WHERE USERID = #{userId}
    </select>
</mapper>

UserData.xml:
<mapper namespace="xxx.UserData">
    <resultMap type="UserData" id="mapperUserData">
        <id property="userId" column="USERID" />
        <result property="age" column="AGE" />
        <result property="birthday" column="BIRTHDAY" />
        
        <association property="userName" column="USERID" javaType="UserName" select="ooo.UserName.getUserNameById" />
    </resultMap>

    <select id="getUserDataById" parameterType="int" resultMap="mapperUserData">
        SELECT * FROM USER_DATA WHERE USERID = #{userId}
    </select>
</mapper>

javaType因為有別名的關係,所以才這樣寫,select要namespace.id,選對方的
測試類:
SqlSession sqlSession = MyBatisSessionFactory.getSession();
        
UserName un = sqlSession.selectOne("ooo.UserName.getUserNameById", 1);
System.out.println("getUserId=" + un.getUserId());
System.out.println("getName=" + un.getName());
if(un.getUserData() != null){
    System.out.println("======================================");
    System.out.println("getAge=" + un.getUserData().getAge());
    System.out.println("getBirthday=" + un.getUserData().getBirthday());
}

System.out.println("----------------------------------------");
UserData ud = sqlSession.selectOne("xxx.UserData.getUserDataById", 1);
System.out.println("getUserId=" + ud.getUserId());
System.out.println("getAge=" + ud.getAge());
System.out.println("getBirthday=" + ud.getBirthday());
if(ud.getBirthday() != null){
    System.out.println("======================================");
    System.out.println("getName=" + ud.getUserName().getName());
}

MyBatisSessionFactory.closeSession();

如果不設association,連到對方的屬性會是null



※一對一關聯的三種方式

上面的方式是使用第三種方式,會有兩條 SQL 語句,其他兩種都一條


<resultMap type="mp.bean.Emp" id="association1">
    <!-- java bean 和 資料庫名稱一樣可不寫 -->
    <result column="dname" property="dept.dName"/>
    <result column="loc" property="dept.loc"/>
</resultMap>
    
<resultMap type="mp.bean.Emp" id="association2">
    <!-- java bean 就算和資料庫名稱一樣也要寫,不寫就是 null -->
    <id column="empno" property="empno" />
    <result column="ename" property="ename" />
    
    <association property="dept" javaType="mp.bean.Dept">
        <!-- java bean 就算和資料庫名稱一樣也要寫,不寫就是 null -->
        <id column="deptNo" property="deptNo" />
        <result column="dName" property="dName" />
    </association>
</resultMap>
    
<select id="getEmpById" resultMap="association1">
    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 empno = #{id}
</select>
    
    
    
<resultMap type="mp.bean.Emp" id="association3">
    <!-- java bean 和 資料庫名稱一樣可不寫 -->
    <association property="dept" column="deptno" 
        select="mp.bean.dao.DeptMapper.getDeptById" />
</resultMap>
    
<select id="getEmp2Step" resultMap="association3">
    select * from emp where empno = #{id}
</select>
    
    
-------------------------------------------------------
<mapper namespace="mp.bean.dao.DeptMapper">
    <select id="getDeptById" resultType="mp.bean.Dept">
        select * from dept where
        deptno = #{deptNo}
    </select>
</mapper>


※第一種是用「.」的方式,dept 是 java bean 的欄位名稱

※第二種都是 SQL 的功,缺點是什麼都要寫,否則就是 null

※第三種裡的 select 是連到其他 mapper 的 SQL,property 是 java bean 的欄位名稱
column 是從 getEmp2Step 取到的欄位名稱,有很多,當然是給要關聯的欄位名稱

第三種是為了提高效能用的,在全域的 setting 有兩個可以設定:
1.lazyLoadingEnabled:true為懶加載,但只要sql的xml裡的屬性fecthType有設定,不管是lazy或eager,都會被fecthType給覆蓋,可以在全域設定懶加載,針對特定的sql不使用懶加載,預設為 false
2.aggressiveLazyLoading:和 lazyLoadingEnabled 相反,false為懶加載;只要一給true,lazyLoadingEnabled、fetchType 都沒用了,預設為 false,但小於等於 3.4.1 版是 true,所以 3.4.1 之前如果不設為 false,lazyLoadingEnabled 會沒有用

沒有留言:

張貼留言