2011-10-28 46 views
2

[真正的問題標記爲加下劃線。這裏遵循的短我的情況可能的解釋]使用EJB緩存JPA實體

我有以下的JPA實體:

@Entity Genre{ 
private String name; 
@OneToMany(mappedBy = "genre", cascade={CascadeType.MERGE, CascadeType.PERSIST}) 
private Collection<Novel> novels; 
} 
@Entity 
class Novel{ 
@ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST}) 
private Genre genre; 
private String titleUnique; 
@OneToMany(mappedBy="novel", cascade={CascadeType.MERGE, CascadeType.PERSIST}) 
private Collection<NovelEdition> editions; 
} 
@Entity 
class NovelEdition{ 
private String publisherNameUnique; 
private String year; 
@ManyToOne(optional=false, cascade={CascadeType.PERSIST, CascadeType.MERGE}) 
private Novel novel; 
@ManyToOne(optional=false, cascade={CascadeType.MERGE, CascadeType.PERSIST}) 
private Catalog appearsInCatalog; 
} 
@Entity 
class Catalog{ 
private String name; 
@OneToMany(mappedBy = "appearsInCatalog", cascade = {CascadeType.MERGE, CascadeType.PERSIST}) 
private Collection<NovelEdition> novelsInCatalog; 
} 

的想法是有幾個小說,屬於每一個特定的流派,可以存在超過一個版本(不同的出版商,年份等)。對於semplicity一個NovelEdition可以屬於剛剛 一個目錄,是由這樣一個文本文件表示這樣的目錄:

Catalog: Name Of Catalog 1 
----------------------- 
"Title of Novel 1", "Genre1 name","Publisher1 Name", 2009 
"Title of Novel 2", "Genre1 name","Pulisher2 Name", 2010 
..... 

Catalog: Name Of Catalog 2 
----------------------- 
"Title of Novel 1", "Genre1 name","Publisher2 Name", 2011 
"Title of Novel 2", "Genre1 name","Pulisher1 Name", 2011 
..... 

每個實體都有相關的無狀態EJB充當DAO,使用事務作用域EntityManager。例如:

@Stateless 
public class NovelDAO extends AbstractDAO<Novel> { 
    @PersistenceContext(unitName = "XXX") 
    private EntityManager em; 

    protected EntityManager getEntityManager() { 
     return em; 
    } 

    public NovelDAO() { 
     super(Novel.class); 
    } 
    //NovelDAO Specific methods 
} 

我感興趣時,編錄文件進行分析和相應的實體建造在(我通常一次讀取一整批產品目錄的)。

身爲解析字符串驅動程序,我不想再重複類似novelDAO.getByName(「小說1的標題」)行動所以我想使用集中式緩存String類型的映射-Identifier->實體對象。

目前我使用3個對象: 1)文件分析器,它確實是這樣的:

final CatalogBuilder catalogBuilder = //JNDI Lookup 
//for each file: 
String catalogName = parseCatalogName(file); 
catalogBuilder.setCatalogName(catalogName); 
//For each novel edition 
String title= parseNovelTitle(); 
String genre= parseGenre(); 
... 
catalogBuilder.addNovelEdition(title, genre, publisher, year); 
//End foreach 
catalogBuilder.build(); 

2)CatalogBu​​ilder是使用Cache和被重新初始化每次有狀態EJB新目錄文件被解析並在持久化目錄後被「移除」。

@Stateful 
public class CatalogBuilder { 

    @PersistenceContext(unitName = "XXX", type = PersistenceContextType.EXTENDED) 
    private EntityManager em; 
    @EJB 
    private Cache cache; 
    private Catalog catalog; 

    @PostConstruct 
    public void initialize() { 
     catalog = new Catalog(); 
     catalog.setNovelsInCatalog(new ArrayList<NovelEdition>()); 
    } 

    public void addNovelEdition(String title, String genreStr, String publisher, String year){ 
     Genre genre = cache.findGenreCreateIfAbsent(genreStr);//** 
     Novel novel = cache.findNovelCreateIfAbsent(title, genre);//** 
     NovelEdition novEd = new NovelEdition(); 
     novEd.setNovel(novel); 
     //novEd.set publisher year catalog 
     catalog.getNovelsInCatalog().add(); 
    } 

    public void setCatalogName(String name) { 
     catalog.setName(name); 
    } 

    @Remove 
    public void build(){ 
     em.merge(catalog); 
    } 
} 

3)最後,有問題的豆:緩存。對於CatalogBu​​ilder,我使用了一個EXTENDED持久化上下文(我需要它作爲解析器執行幾個成功的事務)以及一個有狀態的EJB; 但在這種情況下,我不是很確定我需要什麼。事實上,緩存

  1. 應該留在內存中,直到解析器完成了它的任務, 但不再(不應該是一個單身)作爲分析只是一個 非常特別的活動,其很少發生。
  2. 應該保留所有的 上下文中的實體,並且應該返回管理實體形成打上 方法*,否則試圖堅持目錄 應該失敗(重複插入)。*
  3. 應該作爲CatalogBu​​ilder使用相同的持久性上下文 。

我現在擁有的是:

@Stateful 
public class Cache { 

    @PersistenceContext(unitName = "XXX", type = PersistenceContextType.EXTENDED) 
    private EntityManager em; 

    @EJB 
    private sessionbean.GenreDAO genreDAO; 
    //DAOs for other cached entities 

    Map<String, Genre> genreName2Object=new TreeMap<String, Genre>(); 

    @PostConstruct 
    public void initialize(){ 
    for (Genre g: genreDAO.findAll()) { 
     genreName2Object.put(g.getName(), em.merge(g)); 
    } 
    } 

    public Genre findGenreCreateIfAbsent(String genreName){ 
    if (genreName2Object.containsKey(genreName){ 
     return genreName2Object.get(genreName); 
    } 
    Genre g = new Genre(); 
    g.setName(); 
    g.setNovels(new ArrayList<Novel>()); 
    genreDAO.persist(t); 
    genreName2Object.put(t.getIdentifier(), em.merge(t)); 
return t; 
} 
} 

不過說實話我找不到它在同一時間滿足這3點的解決方案。例如,使用另一個具有擴展持久性上下文的有狀態bean將爲第一個解析的文件工作,但我有 不知道第二個文件上應該發生什麼。事實上,對於第一個文件,PC將被創建並傳播從CatalogBu​​ilder緩存,然後將使用相同的PC。但build()返回後,CatalogBu​​ilder的PC應該(我猜)在連續解析期間被刪除並重新創建,儘管Cache的PC應該保持「活躍」:不應該在這種情況下拋出異常?另一個問題是 緩存bean被鈍化時該怎麼做。目前我得到的例外:

"passivateEJB(), Exception caught -> 
java.io.IOException: java.io.IOException 
    at com.sun.ejb.base.io.IOUtils.serializeObject(IOUtils.java:101) 
    at com.sun.ejb.containers.util.cache.LruSessionCache.saveStateToStore(LruSessionCache.java:501)" 

因此,我不知道如何實現我的緩存。你將如何解決這個問題?

+1

對JPA用於在PersistenceContexts之間共享緩存實體的L2共享緩存感興趣嗎? –

+0

嗨!據我瞭解,EclipseLink默認啓用L2緩存。爲了確保激活它,我在我的persistence.xml中添加了 ALL。不應該自動緩存每個查詢的結果嗎?我改變了我的Cache類直接查詢數據庫(通過DAO的方法),而不是將值存儲到地圖中,但通過FINE日誌級別,我可以看到每次調用findXXXCreateIfAbsent()方法時都會執行SQL查詢。這種方法是錯誤的嗎? – Federico

回答

0

EclipseLink的配置:

  • 您可以指定配置文件屬性爲所有的entites。

    <property name="eclipselink.cache.shared.default" value="true"/>

  • 在與@Cache註釋實體水平,指定不同的屬性。

一般JPA配置:

  • 在實體級別通過使用@Cacheable註釋與value屬性作爲true

  • 通過CacheRetrieveMode指定檢索模式,屬性USE啓用其他BYPASS

    entityManager.setProperty("javax.persistence.cache.retrieveMode", CacheRetrieveMode.USE);

  • 通過CacheStoreMode與屬性BYPASSUSEREFRESH配置在高速緩存中的存儲空間。

    entityManager.setProperty("javax.persistence.cache.storeMode",CacheStoreMode.REFRESH);

  • Cache接口表示共享高速緩存。要刪除全部或部分緩存的實體,可以調用其中一種evict方法。您可以從EntityManagerFactory獲得緩存界面的參考。