hibernate 二級快取 + cluster
原文出自:http://mmmmtodd.blogspot.com/2012/03/hibernate-cluster.html
?
result as below我們相同嗎 ? true 我是一級快取
public String firstCache() { Users userA = usersService.getById(1L); // session.clear() || session.evict(userA); Users userB = usersService.getById(1L); System.out.println("我們相同嗎 ? " + (userA == userB)); System.out.println("我是一級快取"); return SUCCESS; }
我們相同嗎 ? false 我是一級快取
public String noCache() { Users uA = usersService.getByAccount("markyeh"); System.out.println("我沒有 快取 : " + uA.getUpdateTime()); return SUCCESS; }然後我對點了這個連結兩次 http://localhost:8080/Cache/noCache.do
Hibernate: /* FROM Users WHERE account = ? */ select users0_.id as id0_, users0_.account as account0_, users0_.address as address0_, users0_.area1 as area4_0_, users0_.area2 as area5_0_, users0_.create_time as create6_0_, users0_.email as email0_, users0_.icon as icon0_, users0_.mobile as mobile0_, users0_.name as name0_, users0_.password as password0_, users0_.reg_ip as reg12_0_, users0_.reg_random_code as reg13_0_, users0_.roc_id as roc14_0_, users0_.score as score0_, users0_.state as state0_, users0_.update_time as update17_0_ from users users0_ where users0_.account=?我沒有 快取 : 2012-03-15 10:37:41.395Hibernate: /* FROM Users WHERE account = ? */ select users0_.id as id0_, users0_.account as account0_, users0_.address as address0_, users0_.area1 as area4_0_, users0_.area2 as area5_0_, users0_.create_time as create6_0_, users0_.email as email0_, users0_.icon as icon0_, users0_.mobile as mobile0_, users0_.name as name0_, users0_.password as password0_, users0_.reg_ip as reg12_0_, users0_.reg_random_code as reg13_0_, users0_.roc_id as roc14_0_, users0_.score as score0_, users0_.state as state0_, users0_.update_time as update17_0_ from users users0_ where users0_.account=?我沒有 快取 : 2012-03-15 10:37:41.395執行結果:hibernate 對 db 進行了兩次的 select,但明明相同的東西,怎麼做才能只對 db select 一次就好呢?
<bean id="sessionFactory" ref="ihergoSqlDataSource" /> <property name="hibernateProperties"> <props> <!-- <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop> --> <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop> <prop key="hibernate.connection.characterEncoding">utf-8</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.use_sql_comments">true</prop> <prop key="hibernate.cache.use_second_level_cache">true</prop> <!-- 要不要使用二級快取 --> <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> <!-- 指定為 EHCache --> <prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory</prop> <prop key="hibernate.cache.use_query_cache">true</prop> <!-- query 也要快取--> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> <property name="packagesToScan"> <list> <value>mt.buyer.db.entity</value> </list> </property> </bean>2. 設定 entity (以 annotation 為例)
@Entity@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // <---這裡, 只定為 read_write@Table(name = "users")public class Users implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; /** * 帳號 */ @Column(name = "account", length = 20, nullable = false) private String account; ...}3. 編寫 dao
// 快取 public Users getByAccountUserCache(final String account) { final String hql = "FROM Users WHERE account = :account "; List memberList = getHibernateTemplate().executeFind( new HibernateCallback>() { public List doInHibernate(Session hbmSs) throws HibernateException, SQLException { // TODO Auto-generated method stub Query query = hbmSs.createQuery(hql); query.setString("account", account); query.setCacheable(true); // <---- 這裡 return query.list(); } }); if (memberList != null && memberList.size() > 0) { return (Users) memberList.get(0); } return null; } // 沒有快取 public Users getByAccount(String account) { final String hql = "FROM Users WHERE account = ?"; List memberList = getHibernateTemplate().find(hql, account); if (memberList != null && memberList.size() > 0) { return (Users) memberList.get(0); } return null; }4. 增加設定檔 ehcache.xml 並放在 src 下(預設讀取入徑, 也可用 spring 注入)
<?xml version="1.0" encoding="UTF-8"?><ehcache><diskStore path="java.io.tmpdir" /><defaultCache maxElementsInMemory="10000" eternal="false"overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="180"diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /><!-- Hibernate --><cache name="org.hibernate.cache.StandardQueryCache"maxElementsInMemory="5" eternal="false" timeToLiveSeconds="120"overflowToDisk="true"></cache><cache name="org.hibernate.cache.UpdateTimestampsCache"maxElementsInMemory="5000" eternal="true" overflowToDisk="true"></cache></ehcache>5. 測試程式 (action)
public String cache() { Users uA = usersService.getByAccountUserCache("markyeh"); Users uB = usersService.getByAccountUserCache("markyeh"); System.out.println("快取 : " + uA.getUpdateTime()); System.out.println("快取 : " + uB.getUpdateTime()); return SUCCESS; }6. 執行連結 : http://localhost:8080/Cache/cache.do
Hibernate: /* FROM Users WHERE account = :account */ select users0_.id as id0_, users0_.account as account0_, users0_.address as address0_, users0_.area1 as area4_0_, users0_.area2 as area5_0_, users0_.create_time as create6_0_, users0_.email as email0_, users0_.icon as icon0_, users0_.mobile as mobile0_, users0_.name as name0_, users0_.password as password0_, users0_.reg_ip as reg12_0_, users0_.reg_random_code as reg13_0_, users0_.roc_id as roc14_0_, users0_.score as score0_, users0_.state as state0_, users0_.update_time as update17_0_ from users users0_ where users0_.account=?快取 : 2012-03-15 10:37:41.395快取 : 2012-03-15 10:37:41.3957. 可以發現, 第二次的查詢就已經沒有再進 db search 了, 如果再執行相同連結一次(也就是再發一個 request 到後台)
快取 : 2012-03-15 10:37:41.395快取 : 2012-03-15 10:37:41.395可以看到,都是從二級快取的地方取得資料,這時,大家會問,那修改時怎麼判斷要不要取得新資料 8. 我們加入下面的程式碼:
public String update() { // Users u = usersService.getByAccountUserCache("markyeh"); Users u = usersService.getByAccount("markyeh"); u.setArea1(3); u.setUpdateTime(new Date()); usersService.update(u); System.out.println("我修改了 : " + u.getUpdateTime()); return SUCCESS; }9. 分別依順序執行 3 個連結 a) http://localhost:8080/Cache/cache.do b) http://localhost:8080/Cache/update.do c) http://localhost:8080/Cache/cache.do
// a) selectHibernate: /* FROM Users WHERE account = :account */ select users0_.id as id0_, users0_.account as account0_, users0_.address as address0_, users0_.area1 as area4_0_, users0_.area2 as area5_0_, users0_.create_time as create6_0_, users0_.email as email0_, users0_.icon as icon0_, users0_.mobile as mobile0_, users0_.name as name0_, users0_.password as password0_, users0_.reg_ip as reg12_0_, users0_.reg_random_code as reg13_0_, users0_.roc_id as roc14_0_, users0_.score as score0_, users0_.state as state0_, users0_.update_time as update17_0_ from users users0_ where users0_.account=?快取 : 2012-03-15 16:39:16.944快取 : 2012-03-15 16:39:16.944第一次執行, 結果一樣, 不會撈兩次// b) updateHibernate: /* FROM Users WHERE account = ? */ select users0_.id as id0_, users0_.account as account0_, users0_.address as address0_, users0_.area1 as area4_0_, users0_.area2 as area5_0_, users0_.create_time as create6_0_, users0_.email as email0_, users0_.icon as icon0_, users0_.mobile as mobile0_, users0_.name as name0_, users0_.password as password0_, users0_.reg_ip as reg12_0_, users0_.reg_random_code as reg13_0_, users0_.roc_id as roc14_0_, users0_.score as score0_, users0_.state as state0_, users0_.update_time as update17_0_ from users users0_ where users0_.account=?Hibernate: /* update mt.buyer.db.entity.Users */ update users set account=?, address=?, area1=?, area2=?, create_time=?, email=?, icon=?, mobile=?, name=?, password=?, reg_ip=?, reg_random_code=?, roc_id=?, score=?, state=?, update_time=? where id=?我修改了 : Thu Mar 15 16:40:19 CST 2012// c) selectHibernate: /* FROM Users WHERE account = :account */ select users0_.id as id0_, users0_.account as account0_, users0_.address as address0_, users0_.area1 as area4_0_, users0_.area2 as area5_0_, users0_.create_time as create6_0_, users0_.email as email0_, users0_.icon as icon0_, users0_.mobile as mobile0_, users0_.name as name0_, users0_.password as password0_, users0_.reg_ip as reg12_0_, users0_.reg_random_code as reg13_0_, users0_.roc_id as roc14_0_, users0_.score as score0_, users0_.state as state0_, users0_.update_time as update17_0_ from users users0_ where users0_.account=?快取 : 2012-03-15 16:40:19.524快取 : 2012-03-15 16:40:19.524可以發現,ehcache 會自動判斷 cache 裡的東西跟 db 的記錄是不是不一樣了,如果不一樣會再 select 一次放入二級快取裡
<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"monitoring="autodetect"><terracottaConfig url="localhost:9510" /> <!-- 連接到 terracotta server 的 ip 與 port --><defaultCache maxElementsInMemory="10000" eternal="false"overflowToDisk="false" timeToIdleSeconds="300" timeToLiveSeconds="180"diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <!-- diskPersistent 必須設成 false --><!-- Hibernate --><cache name="org.hibernate.cache.StandardQueryCache"maxElementsInMemory="5" eternal="false" timeToLiveSeconds="120"overflowToDisk="false"> <!-- overflowToDisk 必須設成 false --><terracotta /></cache><cache name="org.hibernate.cache.UpdateTimestampsCache"maxElementsInMemory="5000" eternal="true" overflowToDisk="false"> <!-- overflowToDisk 必須設成 false --><terracotta /></cache></ehcache>5. 在剛那個資料夾裡, 找到 \ehcache-2.5.1\terracotta\bin\start-tc-server.bat 並執行, 將 server run 起來