JSF and synchronized lazyloading from EntityManager - jpa

I know EntityManager is not thread-safe but how to work with this in JSF?
Environment: Tomcat with Java-SE. Not Spring or something like that. Manual transaction management.
If I have an object like (pseudo code):
#Entity
class Person {
#OneToMany(lazy=true)
List<Address> addresses;
#ManyToOne(lazy=true)
Company company;
}
Then I have a NamedBean like
#Named
#SessionScoped
class PersonBean {
EntityManager entityManager;
public PersonBean(EntityManager em) { this.entityManager = em; }
public Person getPerson() {
em.find(Person.class, 1); // load the Person with id=1
}
}
And a page like
<html>
<body>
#{personBean.person.addresses[0].street}<br/>
#{personBean.person.addresses[0].number}<br/>
#{personBean.person.addresses[0].city}<br/>
#{personBean.person.company.address.street}<br/>
</body>
</html>
In this situation I can do the em.find() in a synchronized way to keep using the EntityManager in a thread safe manner. But what about the page itself? I assume JSF will load the values in parallel?
Any suggestions? Or is there no problem?
Thank you very much for any advice.

Related

Is it possible to inject a context specific PersistenceContext?

In a jsf application data is managed injecting a PersistenceContext.
#PersistenceContext(unitName = "MyPU")
private EntityManager em;
PersistenceContext is static and choosen at compile time. Is there a way to inject a different PersistenceContext based on the user ? My idea is to enforce authorization checks on database side too, so if there is a hole in application security the user cannot access or modify restricted data.
Create some factory :
#Stateless
public class PersistenceContextFactory {
#PersistenceContext(unitName="MyPU")
private EntityManager emPU;
#PersistenceContext(unitName="MyOtherPU")
private EntityManager emOtherPU;
public EntityManager getEntityManager(User user) {
if(user.hasSomeRight()) {
return emPU;
} else {
return emOtherPU;
}
}
}

Entities are unmanaged when using Extended PersistentContext

Environment:
JDK 1.8
WildFly 10.0.0.Final
I have following #Stateful bean
#Stateful
#SessionScoped
#Local(CdiStatefulEmployeeService.class)
public class CdiStatefulEmployeeBean implements CdiStatefulEmployeeService {
#PersistenceContext(name = "employees", type = PersistenceContextType.EXTENDED)
EntityManager extendedEm;
private Employee cached;
#Override
public String service() {
cached = extendedEm.find(Employee.class, 499983);
return cached.getFirstName();
}
#Override
public String updateEntity() {
cached.setFirstName("Uri2");
//extendedEm.flush(); -- Line 1
return cached.getFirstName();
}
}
and following Servlet client
#WebServlet("/atInjectedStatefulEjbClient")
public class AtInjectedStatefulEjbClient extends HttpServlet {
#Inject
CdiStatefulEmployeeService statefulBean;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(true);
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
pw.println(statefulBean.service());
pw.println();
pw.println(statefulBean.updateEntity());
session.invalidate();
pw.flush();
pw.close();
}
}
Observation: Calling bean.updateEntity() method is not automatically saving the changes i.e. setting first name to "Uri2".
Question : Arent the entities managed across the calls in case of extended persistence context ?
Calling the flush() (i.e. uncommenting Line 1) has no effect either. Basically the entity is not managed in the updateEntity() call. I find this strange. Any thoughts ?
UPDATE:
Just to rule out the possibility, tried with same code but with following
No #SessionScoped
No #Inject (replaced with JDNI lookup inside servlet doGet())
Added #Remove method in the bean
Have servlet doGet() invoke #Remove once it is done with calling other 2 methods
Result : Same issue. Entity is not managed after first service call
Rakesh
Assuming that service method is called before updateEntity shows described behavior: Transaction attribute of service method is default, which means that, as you pointed out in your comment on updateEntity, the transaction is committed at end of service call. This commit makes the cached entity unmanaged. I suggest to annotate service method with #TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) which should keep cached entity managed and subsequent call of updateEntity finds managed entity.
Usually, with extended persistence contexts the facade EJB classes are annotated with #TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) and only EJB's methods that should commit get a different transaction attribute (see for example article Domain-driven design with Java EE 6).
Never ever inject STATEFUL (session dependent) beans into servlets (used by many users = by many sessions). If you use CMT the commit of the transaction will be executed after the call of the #Remove marked method. Where is it? It can be empty but it is critical at stateful beans.
But It would be a viable solution:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:inputText value="#{employeeMB.emplyeeName}"/>
<h:commandButton value="Submit" action="#{employeeMB.onSubmit}"/>
</h:form>
</h:body>
</html>
#ManagedBean
#SessionScoped
#Getter
#Setter
public class EmployeeMB
{
private String employeeName;
#EJB
private EmployeeSB employeeSB;
public void onSubmit()
{
employeeSB.persist( employeeName );
employeeName = "";
}
}
#Stateless
#LocalBean
public class EmployeeSB
{
#PersistenceContext
private EntityManager em;
public void persist( String employeeName_ )
{
Employee e = new Employee();
e.setName( employeeName_ );
em.persist( e );
}
}
Seems to be a bug in WildFly. All the documentation (Spec, books etc..) point out that Extended context meant to prevent entities from becoming detached when the transaction ends.
Filed Wildfly Issue : https://issues.jboss.org/browse/WFLY-6383

#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.

Transaction using JSF2, EJB3, JPA2

What is the best way to handle transactions in this environment?
I have a Transacao class, which has a collection of Transacao.
public class Transacao {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pai")
private List<Transacao> filhos;
}
I load this in JSF from a EJB, something like:
public class TransacaoBean {
#EJB
private TransacaoService transacaoService;
private void edit(Long id) {
this.transacao = transacaoService.findById(id);
}
}
although, if I want to get the collection of filhos, I have to do this:
public class TransacaoBean {
...
private void edit(Long id) {
this.transacao = transacaoService.findById(id);
log.info(this.transacao.getFilhos.size()); //this throws a LazyInitializationException
}
}
and I get an Exception.
What is the best way to have this loaded in my JSF? I'm considering creating a Filter and using USerTransaction to keep the transaction open for the request or fetching the filhos in my EJB. Is there a better solution to this, which one is better?
The fetch's default value of the #OneToMany is FetchType.LAZY.
You can set it FetchType.EAGER to use them in non-managed environment.
Or you can make another EJB or method for getting a list or just the size.
public class TransacaoService {
public Transacao findById(final long id) {
...
}
public long getFilhosSize(final long id) {
// SELECT f FROM Transacao AS t WHERE t.pai.id=:id
}
#PersistenceContext
private EntityManager entityManager;
}

Entity states between EJB and Jersey

I'm a novice.
Does Jersey and EJB hold the same EntityManager scope?
Should I have to pass the EntityManager to EJB for same persistence context?
The primary target usage is JTA.
#Stateless
class MyEJB {
public MyEntity find(Long id) {
...
}
#PersistenceContext;
EntityManager entityManager;
}
class MyResource {
#GET
#Path("/myentity/{id}");
public MyEntity get(#PathParam("id") final long id) {
final MyEntity found = myEjb.find(id);
// is found's state detached?
// should I have to reattach?
found.setDate(new Date());
return found;
}
#EJB
private MyEjb myEjb;
#PersistenceContext;
EntityManager entityManager;
}
Does Jersey and EJB hold the same EntityManager scope?
Should I have to pass the EntityManager to EJB for same persistence context?
I don't think that your wording is correct, but they can share the same EntityManager instance, and you have chosen the right way (through injection). Have a look at this chapter of the Java EE 6 Tutorial:
To obtain an EntityManager instance, inject the entity manager into the application component:
#PersistenceContext
EntityManager em;
So, once again, your approach is correct. With regards to the questions in the code comments: the fact that MyEntity is attached or detached, it depends on the implementation of the find method in your EJB. If you do the following, it will be attached:
public MyEntity find(Long id) {
return entityManager.find(MyEntity.class, id);
}
Finally, doing this way, if you have chosen JTA to use container managed transactions, the transactions will be automatically bounded with the natural boundaries of MyBean's methods. In order to have JTA transactions, you have to use this line in persistence.xml file:
<persistence-unit name="em" transaction-type="JTA">