2015年7月1日 星期三

Mybatis3.x HelloWorld(Mybatis3.x一)


<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

※還要有資料庫驅動程式,oracle 在這裡,可用 SELECT * FROM v$version; 查版本


mybatis和Hibernate都是連資料庫轉換成物件的東東
1.所以一開始先將DB的驅動程式和mybatis的jar放到lib裡
2.寫一隻和DB相關的properties,名稱隨便取,我是叫jdbc.properties,如下
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
username=your account
password=your password

3.寫一隻mybatis-config.xml,從這邊copy後,再修改 如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"></properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" />
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/model/Dept.xml" />
    </mappers>
</configuration>

※environments 裡可以寫很多的 environment,譬如 oracle、mysql…等資料庫,改 default 的字串,就可以輕鬆切換

※我知道加這行properties resource,是因為這裡有講,所以也可以將properties的內容寫在裡面或混用

※4.第3步裡面有mapper resource的xml,所以就寫一隻xml吧!檔案名稱可和DB的表名稱不一樣,Dept.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">
<mapper namespace="Dept">
    <select id="getDeptById" parameterType="int" resultType="org.mybatis.model.Dept">
        select * from dept where deptno = #{deptNo}
    </select>
</mapper>

parameterType為傳進去的參數型態是什麼,給#{}接收; resultType為回傳的型態是什麼

5.resultType會連到java,寫一下唄
package org.mybatis.model;
public class Dept {
    private int deptNo;
    private String dName;
    private String loc;

    public int getDeptNo() {
        return deptNo;
    }

    public void setDeptNo(int deptNo) {
        this.deptNo = deptNo;
    }

    public String getdName() {
        return dName;
    }

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

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }
}

6.寫一支測試類來測一下,從這裡copy後,稍加修改
public static void main(String[] args) {
    InputStream is = null;
    SqlSession sqlSession = null;
    try {
        is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder()
                .build(is);
        sqlSession = factory.openSession();

        Dept dept = sqlSession.selectOne("Dept.getDeptById", 40);
        System.out.println("deptNo=" + dept.getDeptNo());
        System.out.println("dName=" + dept.getdName());
        System.out.println("loc=" + dept.getLoc());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        sqlSession.close();
        try {
            if (is != null) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

※Dept.getDeptById為namespace.id

※如果 java bean 的名稱和資料庫的名稱不一樣,可以用 as 或者使用 <resultMap>,後續會說明



※介面編程

以上是舊的寫法,現在用的是介面來寫,首先寫一個介面,介面定義一些方法,然後 xml 的namespace 寫 interface 的全類名,id 為方法名,這樣就會自動對應了(但不能用overloading),如下:


interface

public Dept getDeptById(int id);
    
public Dept getDeptByIdAndLoc(@Param("id") int id, @Param("loc") String loc);
    
public Dept getDeptByList(@Param("ids") List<Integer> ids);
    
public Dept getDeptByMap(Map<String, Object> map);
    
public List<Dept> getListByName(String name);
    
public Map<String, Object> getMapById(int id);
    
@MapKey("deptNo")
public Map<Integer, Dept> getMapByName(String name);



xml

<select id="getDeptById" resultType="mp.bean.Dept">
    select * from dept where
    deptno = #{deptNo}
</select>
    
<select id="getDeptByIdAndLoc" resultType="mp.bean.Dept">
    select * from dept
    where deptno = #{id}
    and loc = #{loc}
    
<!--         where deptno = #{param1} -->
<!--         and loc = #{param2} -->
    
<!--         where deptno = #{arg0} -->
<!--         and loc = #{arg1} -->
</select>
    
<select id="getDeptByList" resultType="mp.bean.Dept">
    select * from dept
    where deptno = #{ids[0]} <!-- #{list[0]} -->
</select>
    
<select id="getDeptByMap" resultType="mp.bean.Dept">
    select * from dept
    where deptno = #{xxx}
    and loc = #{loc}
</select>
    
<select id="getListByName" resultType="mp.bean.Dept">
    select * from dept
    where dname like #{name}
</select>
    
<select id="getMapById" resultType="map">
    select * from dept
    where deptno = #{id}
</select>
    
<select id="getMapByName" resultType="mp.bean.Dept">
    select * from dept
    where dname like #{name}
</select>

※回傳值寫 map、list 會幫我們對應到 java 常用的類別或介面了,這是因為 mybatis 已經有內鍵的別名了,還有很多,可參考官網

測試類

// Dept dept = sqlSession.selectOne("Dept.getDeptById", 40);
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
            
/*
Dept dept = mapper.getDeptById(40);
System.out.println("dept=" + dept);
System.out.println("deptNo=" + dept.getDeptNo());
System.out.println("dName=" + dept.getdName());
System.out.println("loc=" + dept.getLoc());
*/

/*
Dept dept = mapper.getDeptByIdAndLoc(40, "BOSTON");
System.out.println("dept=" + dept);
*/

/*
List<Integer> list = new ArrayList<>();
list.add(40);
Dept dept = mapper.getDeptByList(list);
System.out.println("dept=" + dept);
*/

/*
Map<String, Object> map = new HashMap<>();
map.put("xxx", 40);
map.put("loc", "BOSTON");
Dept dept = mapper.getDeptByMap(map);
System.out.println("dept=" + dept);
*/

/*
List<Dept> listByName = mapper.getListByName("%O%");
System.out.println(listByName);//找不到不是null,是空的 list
listByName.forEach((dept) -> {
    System.out.println(dept.getdName());
});
*/

/*
Map<String, Object> mapById = mapper.getMapById(40);
System.out.println(mapById);// 找不到為null
*/

Map<Integer, Dept> mapByName = mapper.getMapByName("%O%");
System.out.println(mapByName);// 找不到不是null,是空的 map

※注意:
1.parameterType 可不寫,mybatis 會用TypeHandler 自動判斷

2.傳一個參數時,#{} 裡面可以隨便寫
傳多個參數時,第一個參數可寫 args0 或 param1,第二個參數可寫 args1 或 param2…依此類推,但如果配合 @Param 時,只能寫 @Param 的值和 paramX,就不能寫 argsX 了
錯誤訊息有提示 Available parameters are [arg1, arg0, param1, param2]

3.回傳值是 List 或 Set 時,查詢不到返回的是空 list,不是 null

4.回傳值是 Map 時,如果泛型沒有 java bean 查詢不到時會回傳 null;但如果有 java bean,會回傳空 map

5.List、Map 裡的泛型有 java bean,在 xml 裡的 resultType 也要寫 java bean 才可以,否則List 會出 java.lang.UnsupportedOperationException 的錯; Map 只有一筆

6.mybatis-config.xml 的 mappers 標籤的 mapper resource 除了可以一次設定一個 xml 路徑外,還可以用掃瞄包的方式 package name="mp.bean.dao" 一次就抓很多的 xml 了,但要注意 xml 必須放在和 interface 同一層目錄(下一層也不行)且同名才行
或者在 interface 使用 @Mapper 也行,但因為沒有 SQL了,還得加上類似 @Select 的註解即可

7.如果參數是物件,xml 裡直接寫屬性名即可,但如果加了 @Param,那就只能用 param1或@Param 裡面的字串



※在 Console 印 SQL

mybatis 支援很多 log,可看官網,這裡以 log4j2 為例
1.取得 jar 檔,推薦用 maven、gradle 等
2. 寫 log4j2.xml 放在 classpath 目錄下,然後將 Root level 放 DEBUG 以上層級
以上兩個可參考我 log4j2 的文章,其中第 2 步有很多做法,我是用官網自動設定的第9招

3.mybatis 設定 <setting name="logImpl" value="LOG4J2" /> 即可
typeAliases 使用 package name 時會有亂碼,不知道為什麼,不過不影響 SQL 就是了
有興趣的可以看源碼的 DefaultVFS 的 list 方法,第二個 for 迴圈

setting 的設定很多,可看官網

沒有留言:

張貼留言