[真正的問題標記爲加下劃線。這裏遵循的短我的情況可能的解釋]使用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)CatalogBuilder是使用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)最後,有問題的豆:緩存。對於CatalogBuilder,我使用了一個EXTENDED持久化上下文(我需要它作爲解析器執行幾個成功的事務)以及一個有狀態的EJB; 但在這種情況下,我不是很確定我需要什麼。事實上,緩存:
- 應該留在內存中,直到解析器完成了它的任務, 但不再(不應該是一個單身)作爲分析只是一個 非常特別的活動,其很少發生。
- 應該保留所有的 上下文中的實體,並且應該返回管理實體形成打上 方法*,否則試圖堅持目錄 應該失敗(重複插入)。*
- 應該作爲CatalogBuilder使用相同的持久性上下文 。
我現在擁有的是:
@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將被創建並傳播從CatalogBuilder到緩存,然後將使用相同的PC。但build()返回後,CatalogBuilder的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)"
因此,我不知道如何實現我的緩存。你將如何解決這個問題?
對JPA用於在PersistenceContexts之間共享緩存實體的L2共享緩存感興趣嗎? –
嗨!據我瞭解,EclipseLink默認啓用L2緩存。爲了確保激活它,我在我的persistence.xml中添加了 ALL shared-cache-mode>。不應該自動緩存每個查詢的結果嗎?我改變了我的Cache類直接查詢數據庫(通過DAO的方法),而不是將值存儲到地圖中,但通過FINE日誌級別,我可以看到每次調用findXXXCreateIfAbsent()方法時都會執行SQL查詢。這種方法是錯誤的嗎? –
Federico