一級緩存:
會話session、事務級別的,事務退出,緩存就失效了。
實體管理器在事務執行期間持有一份數據的拷貝,而非直接操作數據源。
二級緩存:
進程范圍級或集群范圍的緩存,這個級別的緩存可配置和修改,整個應用程序的生命周期內都可以訪問。
由於多個事務會同時訪問二級緩存中相同的數據,因此二級緩存必須提供必要的並發訪問策略
以id為標識放到緩存(針對id)
訪問過程:先一級緩存、后二級緩存,再數據源
橋接第三方緩存,hibernate二級緩存的實現:
1.ehcache
2.OScache
3.JBossCache
4.Memcached
......
什么樣的數據適合二級緩存呢?
1.很少被修改的數據
2.不是很重要,允許偶爾出現並發的數據
3.不會被高並發訪問的數據
4.參數數據,通常是數量有限,極少被修改,大量的被其它實例引用的
不適合使用二級緩存?
1.經常被修改的數據,代價太大,得不償失
2.金錢敏感的數據,絕對不允許出現並發
3.與其他應用共享的數據
查詢緩存:
批量的緩存、批量的獲取,如按查詢條件、查詢結果進行緩存;
JPA+Ehcache緩存配置:
1.加入Ehcache依賴
<!--Ehcache-core 包 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.9</version> </dependency> <!--添加Hibernate-Ehcache包 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>${hibernate-version}</version> </dependency>
2.配置ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <diskStore path="java.io.tmpdir/ehcache"/> <!-- 默認緩存 --> <defaultCache maxElementsInMemory="1000" <!-- 默認緩存中存最多數據項目 --> eternal="false" <!--是否永不過期--> timeToIdleSeconds="120" <!--空閑多長時間后從緩存中刪除--> timeToLiveSeconds="120" <!--活躍多長時間后從緩存中刪除--> overflowToDisk="false"/><!--超過maxElementsInMemory之后是否存儲到硬盤--> <!-- 題目緩存--> <cache name="questionCache" maxElementsInMemory="1000" eternal="true" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" memoryStoreEvictionPolicy="LRU" <!--數據項失效策略--> /> </ehcache>
3.persistence.xml配置加入緩存配置
entry key="hibernate.cache.use_second_level_cache" value="false" /><!--開啟二級緩存-->
<entry key="hibernate.cache.use_query_cache" value="false" /><!--開啟查詢緩存-->
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/> <!--ehcache支持-->
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" /><!--ehcache支持-->
<entry key="hibernate.cache.provider_configuration" value="classpath:ehcache.xml"/><!--ehcache詳細配置-->
4.配置需二級緩存實體和屬性
在實體類和實體的那些集合屬性上啟用二級緩存使用
@Entity @Table(name = "user") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE,region="指定的cache") @Cacheable(true) public class User implements java.io.Serializable { private static final long serialVersionUID = 6980093847795726310L; private String id; private String userName; private String password; private Set<Role> roles = new HashSet<Role>(); ....省略 /** *注:如果一個實體需要二級緩存,若該實體含有<set...>,<list...>等屬性時,也必須要指定緩存策略。 */ @ManyToMany(fetch = FetchType.LAZY) @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) }) public Set<Role> getRoles() { return this.roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } }
Usage提供緩存對象的事務隔離機制有如下幾種:
(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)
ehcache不支持transaction事務機制,但其他三種可以使用:
read-only::
無需修改, 那么就可以對其進行只讀 緩存,注意,在此策略下,如果直接修改數據庫,即使能夠看到前台顯示效果,
但是將對象修改至cache中會報error,cache不會發生作用。另:刪除記錄會報錯,因為不能在read-only模式的對象從cache中刪除。
read-write:
需要更新數據,那么使用讀/寫緩存 比較合適,前提:數據庫不可以為serializable transaction isolation level(序列化事務隔離級別)
nonstrice-read-write:
只偶爾需要更新數據(也就是說,兩個事務同時更新同一記錄的情況很不常見),也不需要十分嚴格的事務隔離,那么比較適合使用非嚴格讀/寫緩存策略。
5.配置查詢緩存
a.通過添加@QueryHints來實現查詢緩存
public interface DictDao extends JpaRepository<Dict, Integer>,JpaSpecificationExecutor<Dict>{ // spring-data-jpa默認繼承實現的一些方法,實現類為SimpleJpaRepository。 // 該類中的方法不能通過@QueryHint來實現查詢緩存。 @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") }) List<Dict> findAll(); @Query("from Dict") @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") }) List<Dict> findAllCached(); @Query("select t from Dict t where t.name = ?1") @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") }) Dict findDictByName(String name); }
b.自定義Repository擴展接口內使用查詢緩存
public class UserReposiotryImpl { @PersistenceContext private EntityManager em; /** * 使用查詢緩存,調用setHint方法配置. */ public User findByCustomizeId() { return (User) em.createQuery("from User p where p.id=1") .setHint("org.hibernate.cacheable", true).getSingleResult(); }