2015年7月18日 星期六

PK是流水號、@Results、@Result、@ResultMap、@SelectProvider (Mybatis3.x 三)

※PK是流水號的問題

首先先創一張表
CREATE TABLE CHESS(
    CHESS_NO NUMBER(10),
    NAME VARCHAR(10),
    PRICE NUMBER(5),
    PRODUCT_DATE DATE,
    CONSTRAINT CHESS_PK PRIMARY KEY(CHESS_NO)
);
CREATE SEQUENCE CHESS_SEQUENCE
    INCREMENT BY 1
    START WITH 1
    NOMAXVALUE
    NOCYCLE
    CACHE 10;
COMMENT ON TABLE CHESS IS '棋表';
COMMENT ON COLUMN CHESS.CHESS_NO IS '棋編號';
COMMENT ON COLUMN CHESS.NAME IS '棋名稱';
COMMENT ON COLUMN CHESS.PRICE IS '棋價錢';
COMMENT ON COLUMN CHESS.PRODUCT_DATE IS '棋的生產日期';

然後增加棋類,欄位對應好,然後給setter/getter,下面是Chess.xml
<insert id="insert" parameterType="Chess">
    INSERT INTO CHESS(
    <include refid="column" />
    )
    VALUES(CHESS_SEQUENCE.NEXTVAL, #{name}, #{price}, #{productDate})
</insert>

增加別名時,因為是同一個包,乾脆就增加package就可以了,mybatis會去這個包找,還要記得Chess.xml要設定
<typeAliases>
    <!-- <typeAlias type="org.mybatis.model.Dept" alias="Dept" /> -->
    <!-- <typeAlias type="org.mybatis.model.Chess" alias="Chess" /> -->
    <package name="org.mybatis.model"/>
</typeAliases>
<!--資料庫操作省略-->
<mappers>
    <mapper resource="org/mybatis/model/Dept.xml" />
    <mapper resource="org/mybatis/model/Chess.xml" />
</mappers>

※新增時,PK的問題

這時會發現新增時,PK居然是null
 System.out.println("----------Chess.insert----------");
 Chess chess = new Chess();
 chess.setName("五子棋");
 chess.setPrice(35);
 chess.setProductDate(new Date());
 int suc = sqlSession.insert("Chess.insert", chess);
 System.out.println("成功新增" + suc + "筆!");
 System.out.println("PK=" + chess.getChessNo());
 System.out.println("name=" + chess.getName());
 System.out.println("price=" + chess.getPrice());
 System.out.println("productDate=" + chess.getProductDate());

由於我用的是oracle,所以oracle的解法是這樣
<insert id="insert" parameterType="Chess">
    <!-- selectKey因為設定BEFORE,所以會在執行之前,會先執行裡面的語法到setChessNo裡 -->
    <selectKey keyProperty="chessNo" order="BEFORE" resultType="java.lang.Integer">
        SELECT CHESS_SEQUENCE.NEXTVAL FROM DUAL
    </selectKey>
    INSERT INTO CHESS(
    <include refid="column" />
    )
    以下兩行選擇其一,一般會用第二種
    <!--VALUES(CHESS_SEQUENCE.CURRVAL, #{name}, #{price}, #{productDate})-->
    VALUES(#{chessNo}, #{name}, #{price}, #{productDate}, #{clazz}, #{score}, #{school})
</insert>

※查詢時,欄位名稱的問題

新增一組PK查詢
<select id="getChessById" parameterType="java.lang.Integer" resultType="Chess" >
    SELECT
    <include refid="column" />
    FROM CHESS WHERE CHESS_NO = #{chessNo}
</select>

測試類如下,其實還要增加查不到是null的問題,才不會報Exception,測試就算了
System.out.println("----------Chess.getChessById----------");
Chess chess = sqlSession.selectOne("Chess.getChessById", 12);
System.out.println("chessNo=" + chess.getChessNo());
System.out.println("name=" + chess.getName());
System.out.println("price=" + chess.getPrice());
System.out.println("productDate=" + chess.getProductDate());
此時發現chessNo和productDate是null,因為資料庫有_,java沒有,他把他放到java那裡了,所以可以增加個別名,我最後面剛好是PRODUCT_DATE,所以直接加在後面
<select id="getChessById" parameterType="java.lang.Integer" resultType="Chess">
    SELECT
    <include refid="column" /> as productDate
    FROM CHESS WHERE CHESS_NO = #{chessNo}
</select>
但chessNo還是null,因為我沒加別名,但仔細想想,這個做法真是太爛了,一點都不好維護,所以mybatis提供了一個叫resultMap的東西,resultMap和resultType只能選擇其一,做法如下:
<resultMap type="Chess" id="ChessInterface">
    <result property="chessNo" column="CHESS_NO" />
    <result property="productDate" column="PRODUCT_DATE" />
</resultMap>

<select id="getChessById" parameterType="java.lang.Integer" resultMap="ChessInterface" >
    SELECT
    <include refid="column" />
    FROM CHESS WHERE CHESS_NO = #{chessNo}
</select>
type有別名的關係,可以直接寫,id隨便取
然後將我們剛剛的resultType改成resultMap,裡面放resultMap的id即可
我目前做的專案都是把全部的屬性打在裡面,像這樣:
<resultMap type="Chess" id="ChessInterface">
    <id property="chessNo" column="CHESS_NO" />
    <result property="name" column="NAME" />
    <result property="price" column="PRICE" />
    <result property="productDate" column="PRODUCT_DATE" />
</resultMap>
我把PK改成id標籤,結果是一樣的,不過既然是PK,最好就用id,我還試不出差在哪 剛剛第一種做法直接加在欄位後面是不分大小寫的,但property是有分的喔!

還可以用建構子設值,首先在 java bean 的類別裡用工具產生一個所有欄位的建構子,然後xml如下設定(注意順序問題):
<constructor>
    <idArg column="CHESS_NO" javaType="java.lang.Integer" />
    <arg column="NAME" javaType="java.lang.String" />
    <arg column="PRICE" javaType="java.lang.Integer" />
    <arg column="PRODUCT_DATE" javaType="java.util.Date" />
</constructor>


※jdbcType

預設是 OTHER,這在 Oracle 會不知道如何處理,可以改為 NULL 即可
譬如在新增時,欄位是可以 null 的,但 insert 時,還是會報 OTHER 不知道怎麼處理的錯誤,所以可以使用 #{fieldName, jdbcType=NULL}
但如果有很多這樣的東西,可以設定在全域設定這個值,就是在設定資料庫帳密的地方,寫在 properties 同級的下面,如下:
<settings>
    <setting name="jdbcTypeForNull" value="NULL" />
</settings>

※大小寫要注意



※@Results、@Result、@ResultMap

@Mapper
public interface DeptMapper2 {
    @Results(id = "d2", value = { 
        @Result(id = true, column = "deptno", property = "dno"),
//      @Result(column = "dname", property = "name"), 
        @Result(column = "loc", property = "loc") })
    @Select("select * from dept where deptno = #{deptNo}")
    public Dept2 getDeptById(int id);
    
    @ResultMap("d2")
    @Select("select * from dept")
    public List<Dept2> getAllDept2();
}

※使用 @Results 時,不能像 XML一樣單獨定義,其他的方法可以用 @ResultMap 使用,但本身有 @Results 不能再用 @ResultMap

※@Results 寫的地方有差,以此例來說,寫在getAllDept2 上面,那 getDeptById 就算用 @ResultMap 也抓不到




※@SelectProvider

@ResultMap("d2")
@SelectProvider(type = MyProvider.class, method="noParam")
public List<Dept2> providerTest();
    
@ResultMap("d2")
@SelectProvider(type = MyProvider.class, method="param")
public Dept2 providerTest2(@Param("xxx") int id);
    
    
    
public class MyProvider {
    public String noParam() {
        return "select * from dept";
    }
    
    public static String param(Map<String, Integer> map) {
        return "select * from dept where deptno = " + map.get("xxx");
    }
}

※使用 @SelectProvider 時,可以用 static,但不能使用 overloading

※有參數時,必須用 Map 接,Key 固定給 String,Value 和資料庫的型態對應好即可,如果怕出錯可給 Object

※@InsertProvider、@DeleteProvider、@UpdateProvider 也差不多

沒有留言:

張貼留言