2015年7月17日 星期五

別名與CRUD和MybatisUtil Mybatis3.x(二)

※別名

上個例子的Dept.xml裡面的resultType,有可能有很多支都回傳一樣的物件,所以可以用別名的方式來對應,據說2.x是在Dept.xml裡設裡,3.x方法是在mybatis-config.xml裡設定,集中起來感覺比較好找,可參考這裡如下:
<properties resource="jdbc.properties"></properties>
<typeAliases>
    <typeAlias type="org.mybatis.model.Dept" alias="Dept" />
    <package name="org.mybatis.model" />
</typeAliases>

注意如果有「The content of element type "configuration" must match
 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,enviro
 nments?,databaseIdProvider?,mappers?)".」這樣的錯,有可能是你沒有按照順序設定,如properties->settings->typeAliases…等
這時Dept.xml就只要寫resultType="Dept"就可以了(不一定要resultType才可以用)

也可以用註解,@Alias("xxx") 寫在 java bean class 的最上面,但要配合掃瞄包,也就是 package name,這別名可以在 xml 使用 

都設定不會有問題,都可以用,如果只有掃瞄包,不寫 @Alias,預設是類名稱,開頭大小寫都可以

※增刪改查

Dept.xml如下,注意#{}裡面是對應getter方法的field
而parameterType,為傳進去的參數類型; resultType為傳回的結果
雖然getDeptById,我寫int,但我傳Dept整個物件依然可以,不過為了好維護,大家都能看懂的方式,最好寫物件就傳物件,寫int就傳int
<?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">
<mapper namespace="Dept">
    <select id="getDeptById" parameterType="int" resultType="Dept">
        SELECT DEPTNO, DNAME, LOC FROM DEPT WHERE DEPTNO = #{deptNo}
    </select>
    
    <select id="findAll" resultType="Dept">
        SELECT DEPTNO, DNAME, LOC FROM DEPT
    </select>
    
    <insert id="insert" parameterType="Dept">
        INSERT INTO DEPT(DEPTNO, DNAME, LOC)
        VALUES(#{deptNo},#{dName},#{loc})
    </insert>
    
    <update id="update" parameterType="Dept">
        UPDATE DEPT SET
             DNAME = #{dName}
            ,LOC = #{loc}
        WHERE
            DEPTNO = #{deptNo}
    </update>
    
    <delete id="delete" parameterType="Dept">
        DELETE FROM DEPT WHERE DEPTNO = #{deptNo}
    </delete>
</mapper>

測試類:注意參數要對應Dept.xml的parameterType
InputStream is = null;
SqlSession sqlSession = null;
try {
    is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder()
            .build(is);
    sqlSession = factory.openSession();

    // 新增
    // System.out.println("----------Dept.insert----------");
    // Dept deptIns = new Dept();
    // deptIns.setDeptNo(60);
    // deptIns.setdName("Sales");
    // deptIns.setLoc("zh_CN");
    // int suc = sqlSession.insert("Dept.insert", deptIns);
    // System.out.println("成功新增" + suc + "筆!");

    // 修改
    // System.out.println("----------Dept.update----------");
    // Dept deptUpd = new Dept();
    // deptUpd.setDeptNo(60);
    // deptUpd.setdName("Shopping");
    // deptUpd.setLoc("zh_CN");
    // int upd = sqlSession.update("Dept.update", deptUpd);
    // System.out.println("成功修改" + upd + "筆!");

    // 刪除
    // System.out.println("----------Dept.delete----------");
    // Dept deptDel = new Dept();
    // deptDel.setDeptNo(60);
    // int del = sqlSession.delete("Dept.delete", deptDel);
    // System.out.println("成功刪除" + del + "筆!");

    // 查詢一筆
    System.out.println("----------Dept.getDeptById----------");
    //Dept d = new Dept();
    //d.setDeptNo(90);
    Dept dept = sqlSession.selectOne("Dept.getDeptById", 90);
    System.out.println("deptNo=" + dept.getDeptNo());
    System.out.println("dName=" + dept.getdName());
    System.out.println("loc=" + dept.getLoc());

    // 查詢全部
    // System.out.println("----------Dept.findAll----------");
    // List<Dept> dept = sqlSession.selectList("Dept.findAll");
    // for (Dept deptAll : dept) {
    // System.out.println("deptNo=" + deptAll.getDeptNo());
    // System.out.println("dName=" + deptAll.getdName());
    // System.out.println("loc=" + deptAll.getLoc());
    // }

    sqlSession.commit();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    sqlSession.close();
    try {
        if (is != null) {
            is.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※xml裡的變數

Dept.xml裡的DEPTNO, DNAME, LOC欄位重覆好幾次,可以統一用一個類似變數的東西控管,如下:
<sql id="column">
    DEPTNO, DNAME, LOC
</sql>

<select id="getDeptById" parameterType="int" resultType="Dept">
    SELECT
    <include refid="column" />
    FROM DEPT WHERE DEPTNO = #{deptNo}
</select>

<select id="findAll" resultType="Dept">
    SELECT
    <include refid="column" />
    FROM DEPT
</select>

<insert id="insert" parameterType="Dept">
    INSERT INTO DEPT(
    <include refid="column" />
    )
    VALUES(#{deptNo},#{dName},#{loc})
</insert>

<update id="update" parameterType="Dept">
    UPDATE DEPT SET
    DNAME = #{dName}
    ,LOC = #{loc}
    WHERE
    DEPTNO = #{deptNo}
</update>

<delete id="delete" parameterType="Dept">
    DELETE FROM DEPT WHERE DEPTNO
    = #{deptNo}
</delete>
用<sql>標籤當變數,用<include refid="">可取得

※MybatisUtil

和Hibernate一樣,每次都要呼叫SessionFactory太麻煩了,所以加上這個類,以下是高手提供的寫法,ThreadLocal(區域執行緒),目的是把變數存在目前的執行緒中, 讓每個執行中的執行緒都有一份, 而且彼此之間不會互相影響
package cn.mldn.util;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisSessionFactory {
    private static final String CONFIG = "mybatis-config.xml";
    //ThreadLocal<SqlSession>讓SqlSession不會重覆被呼叫
    private static final ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
    private static SqlSessionFactory sessionFactory;
    private static Reader reader = null;

    static {
        try {
            reader = Resources.getResourceAsReader(CONFIG);
            sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private MyBatisSessionFactory() {
    }

    public static SqlSession getSession() {
        SqlSession session = (SqlSession) threadLocal.get();
        if (session == null) {
            if (sessionFactory == null) {
                rebuildSessionFactory();
            }
            session = (sessionFactory != null) ? sessionFactory.openSession()
                    : null;
            threadLocal.set(session);
        }
        return session;
    }

    public static void rebuildSessionFactory() {
        try {
            reader = Resources.getResourceAsReader(CONFIG);
            sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void closeSession() {
        SqlSession session = (SqlSession) threadLocal.get();
        threadLocal.set(null);
        if (session != null) {
            session.close();
        }
    }
    
    public static SqlSessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
    public static Reader getConfiguration() {
        return reader;
    }
}

這樣子呼叫就把sqlSession = factory.openSession();改成sqlSession = MybatisUtil.getSession();
然後try catch拿掉,最後MybatisUtil.closeSession();即可,寫一下好了:
//本來是這樣
InputStream is = null;
SqlSession sqlSession = null;
try {
    is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder()
            .build(is);
    sqlSession = factory.openSession();
    //CRUD...
    sqlSession.commit();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    sqlSession.close();
    try {
        if (is != null) {
            is.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

//改成以下模樣
SqlSession sqlSession = MybatisUtil.getSession();
//CRUD...
sqlSession.commit();
MybatisUtil.closeSession();

※傳變數給xml

Map<String, Object> map = new HashMap<String, Object>();
//key要對應Dept.xml
map.put("column", "dname");//欄位名稱
map.put("keyWord", "%a%");//給like尋找的關鍵字
map.put("start", 2);// 從第幾筆
map.put("end", 4);// 到第幾筆

SqlSession session = MybatisUtil.getSession();
System.out.println("AllCount=" + session.selectOne("Dept.getAllCount", map));
List<Dept> all = session.selectList("Dept.findAllBySplit", map);
for(Dept d:all){
    System.out.print(d.getDeptNo() + "\t");
    System.out.print(d.getdName() + "\t");
    System.out.println(d.getLoc());
}
MybatisUtil.closeSession();

Demp.xml這樣設定,我用的是Oracle,所以就要用oracle的語法
<select id="findAllBySplit" parameterType="java.util.Map" resultType="Dept">
    SELECT <include refid="column" /> 
    FROM (
        SELECT ROWNUM AS RANK, <include refid="column" /> FROM DEPT ORDER BY DEPTNO
    )
    WHERE 
    LOWER(${column}) LIKE #{keyWord} AND
    RANK BETWEEN #{start} AND #{end}
</select>

<select id="getAllCount" parameterType="java.util.Map" resultType="java.lang.Integer">
    SELECT COUNT(deptno)
    FROM DEPT
    WHERE LOWER(${column}) LIKE #{keyWord}
</select>

沒有留言:

張貼留言