Cache JPA Entities with EJB - jpa

[The real question is marked in bold down. Here follows an as short as possible explanation of my situation]
I have the following JPA Entities:
#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;
}
The idea is to have several Novels, belonging each to a specific Genre, for which can exist more than an edition (different publisher, year, etc). For semplicity a NovelEdition can belong to just
one Catalog, being such a Catalog represented by such a text file:
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
.....
Each entity has associated a Stateless EJB that acts as a DAO, using a Transaction Scoped EntityManager. For example:
#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
}
I am interested at when the catalog files are parsed and the corresponding entities are built (I usually read a whole batch of Catalogs at a time).
Being the parsing a String-driven procedure, I don't want to repeat actions like novelDAO.getByName("Title of Novel 1") so I would like to use a centralized cache for mappings of type String-Identifier->Entity object.
Currently I use 3 Objects:
1) The file parser, which does something like:
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) The CatalogBuilder is a Stateful EJB which uses the Cache and gets re-initialized each time a new Catalog file is parsed and gets "removed" after a catalog is persisted.
#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) Finally, the problematic bean: Cache. For CatalogBuilder I used an EXTENDED persistence context (which I need as the Parser executes several succesive transactions) together with a Stateful EJB;
but in this case I am not really sure what I need. In fact, the cache:
Should stay in memory until the parser is finished with its job,
but not longer (should not be a singleton) as the parsing is just a
very particular activity which happens rarely.
Should keep all of
the entities in context, and should return managed entities form
methods marked with *, otherwise the attempt to persist the catalog
should fail (duplicated INSERTs)..*
Should use the same
persistence context as the CatalogBuilder.
What I have now is :
#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;
}
}
But honestly I couldn't find a solution which satisfies these 3 points at the same time. For example, using another stateful bean with an extended persistence context would work for the 1st parsed file, but I have
no idea what should happen from the 2nd file on.. Indeed for the first file the PC will be created and propagated from CatalogBuilder to Cache, which will then use the same PC. But after build() returns,
the PC of CatalogBuilder should (I guess) be removed and re-created during the successive parsing, although the PC of Cache should stay "alive": shouldn't in this case an exception being thrown? Another problem is
what to do when the Cache bean is passivated. Currently I get the exception:
"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)"
Hence, I have no idea how to implement my cache. How would you solve the problem?

EclipseLink configuration :
You can have specify property in configuration file for all entites.
<property name="eclipselink.cache.shared.default" value="true"/>
At entity level with #Cache annotation, specifying different attributes.
General JPA configuration :
At entity level by using #Cacheable annotation with value attribute as true.
Specifying the retrieval mode by CacheRetrieveMode, attribute USE to enable else BYPASS.
entityManager.setProperty("javax.persistence.cache.retrieveMode", CacheRetrieveMode.USE);
Configuring the storage in cache by CacheStoreMode with attribute BYPASS, USE or REFRESH.
entityManager.setProperty("javax.persistence.cache.storeMode",CacheStoreMode.REFRESH);
Cache interface represents shared cache. To remove all or some cached entity, you can call one of the evict methods. You can get reference of cache interface from EntityManagerFactory.

Related

Update in Database not reflected in JPA Entity

I'm using EclipseLink for JPA Persistence and I'm very new to this technology. I was in a situation I had to update an entry in the table in oracle database and I did commit for this update successfully in the database but It is not reflected in JPA Entity means I can't see the data in Front end but once i restart the tomcat server, I can see the data in the front end. Someone please help me what is happening from back end.
Thanks in advance!
SDS.java
import java.util.Date;
#Entity
#Table(name="SDS")
#XmlRootElement
public class SDS {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long sdsId;
#Column
private String sdsNumber;
#Column
private String sdsName;
#Column
private String sapId;
#Column(name = "DATE_FIELD")
#Temporal(TemporalType.TIMESTAMP)
private Date sdsDate;
public SDS() {}
public Long getSdsId() {
return sdsId;
}
public void setSdsId(Long sdsId) {
this.sdsId = sdsId;
}
public String getSdsNumber() {
return sdsNumber;
}
public void setSdsNumber(String sdsNumber) {
this.sdsNumber = sdsNumber;
}
public String getSdsName() {
return sdsName;
}
public void setSdsName(String sdsName) {
this.sdsName = sdsName.substring(0, Math.min(sdsName.length(), 254));
//this.sdsName = sdsName;
}
public Date getSdsDate() {
return sdsDate;
}
public void setSdsDate(Date sdsDate) {
this.sdsDate = sdsDate;
}
}
DB Update:
update SDS set date_field='10-FEB-2017' where sdsid=1102
Update may be not reflected because when you get your Entity with EntityManager's "find" method EntityManager finds it in the persistence context, not in the database.
If the entity instance is contained in the persistence context, it is returned from there
entityManager.refresh(entity) synchronized the entity with the database for me.
entityManager.flush() did not work (even with committing everything).
entityManager.clear() or obtaining a new EntityManager from EntityManagerFactory also did not help.
And it seems that the same happens when you use "createNativeQuery" with SQL SELECT instead of the "find" method.
Oh! Look what I found.
By default EclipseLink uses a shared object cache, that caches a subset of all objects read and persisted for the persistence unit. The EclipseLink shared cache differs from the local EntityManager cache. The shared cache exists for the duration of the persistence unit (EntityManagerFactory, or server) and is shared by all EntityManagers and users of the persistence unit. The local EntityManager cache is not shared, and only exists for the duration of the EntityManager or transaction.
The shared cache can also be disabled.
property name="eclipselink.cache.shared.default" value="false"

Extended Persistence Context with ViewScoped CDI beans

I am a long time Seam user who tries to move to Java EE7, JSF2.2 and CDI now.
In Seam you tend to use EntityManagers with extended scope most of the time (in the Seam Conversation Scope). You don't get any LIEs on Ajax request etc.
I am trying to do it in a similar way with Java EE7 and CDI but somehow the injected EntityManager is only transaction scoped. When I get a ajax request in the entities that were loaded before are not managed anymore.
I am using the new javax.faces.view.ViewScoped and javax.transactional.Transactional on my CDI bean.
My Producer:
#PersistenceContext(unitName = "primary", type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Produces
#Default
#Dependent
public EntityManager getEntityManager() {
return entityManager;
}
And my CDI bean:
#Named
#ViewScoped
#Transactional
public class TestBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Inject
EntityManager entityManager;
Logger log = Logger.getLogger(TestBean.class);
private TestEntity lastTest = null;
public void testAdd(){
TestEntity test = new TestEntity();
test.setVal("Test "+System.currentTimeMillis());
entityManager.persist(test);
entityManager.flush();
log.infov("Created test entity {0}", test);
lastTest = test;
}
public void testRead(){
List<TestEntity> test = entityManager.createQuery("select t from TestEntity t").getResultList();
for(TestEntity t: test){
log.infov("Found {0} managed {1}",t,entityManager.contains(t));
}
if(lastTest!=null){
log.infov("Last Test {0} managed {1}",lastTest,entityManager.contains(lastTest));
}
}
So when I first call testAdd() via Ajax it creates a new test entity. When I then call testRead() it gets all test entities and checks that the last created test entity is still managed (which it should if it is an EntityManager with an extended persistent context). But entityManager.contains(lastTest) always returns false.
What am I doing wrong?
I believe I can't use #PersistenceContext directly in the CDI bean. So how do I get the desired (Seam like) behaviour?
When you specify that an injected EntityManager is an extended persistence context, all object instances remain managed. Extended persistence contexts can only be used within Stateful session beans.
This is according to JBOSS documentation: https://docs.jboss.org/ejb3/app-server/tutorial/extended_pc/extended.html.
Consider packaging your insert/update/delete operations into EJBs while simple read from database can be through CDI beans. But more complex operations involving multiple reads and writes as well as transaction should be within EJBs.

Spring Data Rest CrudRepository vs ReadOnlyRepository

I noticed an anomaly in the way Spring Data Rest repositories are behaving. I have two types of entities. in my application - readonly entities (for reference data like statecodes, country codes, zip codes etc.). I don't want to let the end user change these. So I implemented the following ReadOnly repository.
#NoRepositoryBean
public interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
T findOne(ID id);
Iterable<T> findAll();
}
#Repository
public interface StateRepository extends ReadOnlyRepository<State, Long> {
}
Now, all other entities have CrudRepositories associated with them because they are editable entities like addresses which reference the states and zip codes.
Here's an example.
#Repository
public interface CustomerRepository extends CrudRepository<Address, Long> {
}
I have a controller for both readonly and editable entities, with a pass-through call to the repositories.
#RestController
#RequestMapping(value = "/addresses", produces = MediaType.APPLICATION_JSON_VALUE)
public class AddressController {
#Autowired
private AddressRepository addressRepository;
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public Iterable<Address> getAllAddresses() {
return addressRepository.findAll();
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public Address getAddress(#PathVariable("id") Long id) {
return addressRepository.findOne(id);
}
}
I have an identical Controller corresponding to the State entity.
Funnily enough, the request to StateController gives me a HATEOAS json response, while the request to Address gives me a non HATEOAS json response. What gives?
My bad. My application server did not redeploy certain repositories. This is a non-issue.
So for those running into these issues, you are likely using hot-code replace feature of your IDE. Consider restarting your app and it should be a non-issue.

#ManyToOne(fetch=FetchType.LAZY) lazy loading not working

I am working with JPA 2.1 (EclipseLink 2.5.1) and JBoss 7.1.
I've define very simple JPA entities:
#Entity
#Table(name="APLICACIONES_TB")
public class Aplicacion implements Serializable {
#Id
#Column(name="COD_APLICACION_V")
private long codAplicacionV;
#Column(name="APLICACION_V")
private String aplicacionV;
#OneToMany(mappedBy="aplicacion")
private Collection<Prestacion> prestaciones;
... getters and setters
}
#Entity
#Table(name="PRESTACIONES_TB")
public class Prestacion implements Serializable {
#Id
#Column(name="COD_PRESTACIONES_V")
private String codPrestacionesV;
#Column(name="DESCRIPCION_V")
private String descripcionV;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "COD_APLICACION_V")
private Aplicacion aplicacion;
... getters and setters ...
}
I have developed a staless EJB that executes a query to obtain some "Aplicacion" entities.
#Stateless
#LocalBean
public class DocuEJB implements DocuEJBLocal
{
#PersistenceContext(name="DocuEjb", type=PersistenceContextType.TRANSACTION)
private EntityManager em;
public Prestacion getResult(String name)
{
return em.createNamedQuery("ExampleQueryName", Prestacion.class).getSingleResult();
}
}
Because I'm working with JSF 2.1 the EJB is being injected in a managed bean:
#ManagedBean(name = "ManagedBean")
#RequestScoped
public class ManagedBean
{
#EJB DocuEJB docuEjb;
public String doSomething()
{
Prestacion entity = docuEjb.getResult("egesr");
if (entity != null)
{
// It should return null because 'entity' should be detached
Aplicacion app = entity.getAplicacion();
// but 'app' entity is not null, ¿why not?
System.out.println (app.getCodAplicacionV());
}
}
}
Lazy loading is not working even when lazy loading has been defined for 'aplicacion' field on 'Prestacion' entity. The code posted before should return a NullPointerException in the next line:
System.out.println (app.getCodAplicacionV());
because 'app' entity is detached and lazy loading has been configured.
Why is not working lazy loading?
Thanks
Try to add #Transactional on doSomething(), I think that your transaction manager is not well configured.
You can see here the official spring documentation. In any case, can you add your spring configurations, so that we can better help you. :)
I don't think the behavior your encounter is abnormal or your question should state it clearly:
EJB are by default transactional
Your JSF inject an EJB, with #EJB, and I guess JBoss can create a java reference and not a proxy
The entity is being managed because the transaction is not done, it will finish when doSomething ends.
Your entity is then loaded into the EntityManager, and lazy loading works because there is a context to it.
You would call em.evict(entity) with the result your are getting, this would probably fails because the entity would not be managed any more.

JPA using #PersistenceContext

How to get the Persistence Unit Name dynamically?
For example in below example,I am hard coding unitName to application_openjpa, but I want to give it dynamically as unitName=#{unitName} so if I have different project uses the entity jar, but can use its own persistence.xml file
#PersistenceContext(unitName="application_openjpa")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
Since an application normally have limited number of persistence units, how about create two setter methods and annotate it with different unitName attribute, eg:
#PersistenceContext(unitName="testEM")
public void setTestEntityManager(EntityManager testEM) {
this.testEM = testEM;
}
#PersistenceContext(unitName="prodEM")
public void setProdEntityManager(EntityManager prodEM) {
this.prodEM = prodEM;
}
Then you can choose between testEM and prodEM dynamically ?