Кэшируйте объекты JPA с помощью EJB - PullRequest
2 голосов
/ 28 октября 2011

[Реальный вопрос отмечен жирным шрифтом вниз.Ниже приведено как можно более короткое объяснение моей ситуации]

У меня есть следующие объекты 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;
}

Идея состоит в том, чтобы иметь несколько романов принадлежность каждого к определенному жанру , для которого может существовать больше, чем издание (другой издатель, год и т. д.).Для простоты 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, используя Transity Scoped 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-> Entity object.

В настоящее время я использую 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 является Stateful EJB, который использует Cache и переинициализируется каждый раз, когда новый файл каталога анализируется и удаляется после сохранения каталога.

@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) Наконец,проблемный компонент: Cache .Для CatalogBuilder я использовал расширенный контекст персистентности (который мне нужен, так как Parser выполняет несколько последовательных транзакций) вместе с Stateful EJB;но в этом случае я не совсем уверен, что мне нужно.На самом деле, кеш :

  1. Должен оставаться в памяти до тех пор, пока синтаксический анализатор не завершит свою работу, но не дольше (не должен быть одноэлементным) при синтаксическом анализеэто просто очень специфическое действие, которое происходит редко.
  2. Должно держать все объекты в контексте и возвращать управляемые объекты из методов, отмеченных *, в противном случае попытка сохранитсядолжен произойти сбой каталога (дублированные вставки) .. *
  3. Следует использовать тот же контекст постоянства, что и 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 точкам одновременно .Например, использование другого stateful bean-компонента с расширенным контекстом персистентности будет работать для 1-го проанализированного файла, но я понятия не имею, что должно произойти со 2-м файлом на самом деле. Действительно, для первого файла ПК будетсоздается и распространяется из CatalogBuilder в Cache , который затем будет использовать тот же компьютер.Но после возврата build () ПК из CatalogBuilder должен (я думаю) быть удален и воссоздан во время последовательного анализа, хотя ПК с кэшем должен оставаться «живым»: не должно ли в этом случае создаваться исключение?Другая проблема заключается в том, что делать, когда компонент Cache пассивирован.В настоящее время я получаю исключение:

"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 Ответ

0 голосов
/ 10 февраля 2012

Конфигурация EclipseLink:

  • Вы можете указать свойство в файле конфигурации для всех объектов.

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

  • На уровне сущности с аннотацией @Cache с указанием различных атрибутов.

Общая конфигурация JPA:

  • На уровне объекта с помощью аннотации @Cacheable с атрибутом value в качестве true.

  • Задание режима поиска с помощью CacheRetrieveMode, атрибут USE для включения else BYPASS.

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

  • Настройка хранилища в кеше с помощью CacheStoreMode с атрибутом BYPASS, USE или REFRESH.

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

  • Cache интерфейс представляет общий кэш. Чтобы удалить все или некоторые кэшированные объекты, вы можете вызвать один из методов evict. Вы можете получить ссылку на интерфейс кеша от EntityManagerFactory.

...