2019年4月30日 星期二

小心使用 split

String s1 = ",,,";
System.out.println(s1.split(",").length); // 0
String s2 = "a,,,";
System.out.println(s2.split(",").length); // 1
String s3 = ",a,,";
System.out.println(s3.split(",").length); // 2
String s4 = ",,a,";
String[] split = s4.split(",");
System.out.println(split.length); // 3
Arrays.stream(split).forEach(System.out::println); // 空空a
    
String s5 = "abcde";
System.out.println(s5.split("").length); // 5


※只會分割非分割符的前面,如果将 a 改成空格也是一樣的結果

※分割符什麼都沒有時,會每個字符都分割

※s1 長度是 0,所以使用 s1.split(",")[0] 會報 ArrayIndexOutOfBoundsException

2019年4月23日 星期二

分頁插件-PageHelper (Mybatis3.x 十四)

文檔

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.8</version>
</dependency>


※全域設定

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="param1" value="value1" />
    </plugin>
</plugins>


※測試

/*
Page<Object> page = PageHelper.startPage(2, 3); // 每頁三條記錄,顯示第二頁,
List<Emp> emp = mapper.getEmp();
emp.stream().forEach(x -> System.out.println(x.getEname()));
// SELECT * FROM ( SELECT TMP_PAGE.*, ROWNUM ROW_ID FROM ( select * from emp ) TMP_PAGE) WHERE ROW_ID <= ? AND ROW_ID > ? 
System.out.println("當前頁:" + page.getPageNum()); // 2
System.out.println("每頁顯示幾條記錄:" + page.getPageSize()); // 3
System.out.println("總共幾筆記錄:" + page.getTotal());
System.out.println("總共有幾頁:" + page.getPages());
*/
    
PageHelper.startPage(3, 5);
List<Emp> emp = mapper.getEmp();
PageInfo<Emp> pageInfo = new PageInfo<>(emp);
emp.stream().forEach(x -> System.out.println(x.getEname()));
System.out.println("當前頁:" + pageInfo.getPageNum());
System.out.println("每頁顯示幾條記錄:" + pageInfo.getPageSize());
System.out.println("當前頁有幾條記錄(最後一頁時,可能和 PageSize()不同):" + pageInfo.getSize());
System.out.println("總共幾筆記錄:" + pageInfo.getTotal());
System.out.println("總共有幾頁:" + pageInfo.getPages());
    
System.out.println("當前頁的第一筆是全部記錄的第幾筆記錄:" + pageInfo.getStartRow());
System.out.println("當前頁的最後一筆是全部記錄的第幾筆記錄:" + pageInfo.getEndRow());
System.out.println("上一頁是第幾頁:" + pageInfo.getPrePage()); // 沒有上一頁顯示 0
System.out.println("下一頁是第幾頁:" + pageInfo.getNextPage()); // 沒有下一頁顯示 0
System.out.println("是否是第一頁:" + pageInfo.isIsFirstPage());
System.out.println("是否是最後一頁:" + pageInfo.isIsLastPage());
System.out.println("是否有上一頁:" + pageInfo.isHasPreviousPage());
System.out.println("是否有下一頁:" + pageInfo.isHasNextPage());


※PageHelper.startPage 必須寫在呼叫之前才有用

※PageInfo 如果沒有配合 PageHelper,那就是看 SQL 下什麼,回來就有幾筆

※看原始碼其實就有說明每一個欄位的作用了

2019年4月20日 星期六

一、二級快取,整合 Ehcache 2.x (Mybatis3.x 十三)

<setting name="cacheEnabled" value="true" /> <!-- 二級快取的開關 -->
<setting name="localCacheScope" value="STATEMENT" /> <!-- 一級快取的範圍,SESSION為開;STATEMENT為關 -->

※一級快取預設是 SESSION,表示 SqlSession;二級快取預設是 true

※SESSION 表示每個一個 Session 中的所有語句都可以共享; STATEMENT 表示只有當前的  SQL 語句的結果可以共享 (看最下面的一、二級快取)


測試類
InputStream is = null;
SqlSession sqlSession = null;
SqlSession sqlSession2 = null;
try {
    is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    sqlSession = factory.openSession();
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    //一級快取
    Dept dept1 = mapper.getDeptById(40);
    //sqlSession.clearCache();清一級快取
    //增刪改操作之後也不會有快取
    Dept dept2 = mapper.getDeptById(40);
    //Dept dept2 = mapper.getDeptByIdAndLoc(40, "BOSTON");
    System.out.println(dept1 == dept2);
    
    //二級快取
    /*
    sqlSession2 = factory.openSession();
    DeptMapper mapper2 = sqlSession2.getMapper(DeptMapper.class);
    Dept dept2 = mapper2.getDeptById(40);
    System.out.println(dept1 == dept2);
    */
}
// ...

注意 dept1 == dept2,如果資料庫沒有值得到的結果是 null,那兩個判斷是 true


※一級快取:

1.指同一個 SqlSession 做查詢的快取
2.只有 where 條件一樣時,才有快取(如上的例子,就算回傳結果一樣,還是沒快取)
兩個查詢之間有增刪改和呼叫 SqlSession 的 clearCache 方法,都不會有快取


※二級快取:

1..指不同 SqlSession 做查詢的快取
2.使用 <cache /> 設定二級快取,其中的屬性可看官網的介紹,其中 readOnly 預設是 false,如果是 false,resultType 的 java bean 必需 implements Serializable,否則會報 java.io.NotSerializableException 的錯,而反序列化後,SQL 就是兩條了,就沒有快取了
3.二個查詢之間必須要將第一個快取 close()後,才會將一級快取裡的值移到二級快取,所以不關閉就不會成功
select 標籤有 useCache,當全域的二級快取是開時,可以針對不想二級快取的 select 關閉
4.<cache-ref> 使用在用另外一支 xml 的 cache


※一、二級快取

在 select 標籤中,還有個 flushCache 可以針對一、二級快取做清除的動作

如果一級快取是 STATEMENT;二級快取是 true,一級快取 close()後會將資料移到二級快取,但一級快取之間還是無法共享

sqlSession = factory.openSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    
//一級快取(SqlSession快取)
Dept dept1 = mapper.getDeptById(40);
Dept dept2 = mapper.getDeptById(40);
System.out.println(dept1 == dept2); // false
    
sqlSession.close();
    
sqlSession2 = factory.openSession();
DeptMapper mapper2 = sqlSession2.getMapper(DeptMapper.class);
    
Dept dept3 = mapper2.getDeptById(40);
//System.out.println(dept1 == dept3);
System.out.println(dept1 == dept3); // false
System.out.println(dept2 == dept3); // true

※如果注解 dept2,那 dept1 == dept3 會是 true,因為 STATEMENT 代表當前的語句



※整合 Ehcache2.x

官方文檔,要下載 ehcache2 和 mybatis 和 ehcache 的 jar 檔, 在根目錄要有 ehcache.xml,可去 ehcache 官網下載,至於設定的意思,可以看到 This file can also be downloaded from 後面有個超連結,會有解釋的;或者 2.x 的 PDF 裡都有
要使用時,只要在二級快取的標籤有個 type,指定為 ehcache 就可以了

<cache readOnly="true" type="org.mybatis.caches.ehcache.EhcacheCache" />

※因為使用 <cache> 標籤,所以還是二級快取,一切的行為都和二級快取一樣,但 ehcache.xml 有一個 <diskStore path="aaa.bbb">,這裡面打的 aaa.bbb 並不是 aaa 目錄下有 bbb 目錄,而是 aaa.bbb 的目錄,會出現在 src 下,也可改成磁碟的路徑,這個檔案就算將二級快取關閉,還是會出現,但是行為還是二級快取的行為

※P.S. 我在整合 Ehcache 3.x 的時候,我試了最低的版本和目前最高的版本,XML 都有問題,都會報 Element <ehcache:config> does not allow attribute "xmlns:ehcache" 或 Element <config> does not allow attribute "xmlns:xsi"