I know there are plenty of "could not initialize proxy - no Session" questions but I did not found any answer to my problem.
So the problem is, when I delegate fetchLazy method, it throws an above mentioned exception. Here is a simplified version of my service class:
Service
public abstract class Service<S extends Service<S,E>, E extends Entity> {
#PersistenceContext private EntityManager entityManager;
// person = personService.fetchLazy(person, Person::getCompany); OK
public E fetchLazy(E entity, Function<E,?> proxyMapper) {
E attachedEntity = entityManager.find(entity.getClass(), entity.getId());
Object proxy = proxyMapper.apply(attachedEntity);
if (!Hibernate.isInitialized(proxy)) { Hibernate.initialize(proxy); }
return attachedEntity;
}
// person = personService.fetch(person).lazy(Person::getCompany); EXCEPTION
public FetchBuilder fetch(E entity) { return new FetchBuilder((S) this, entity); }
public class FetchBuilder {
private final S service; private final E entity;
LazyFetchBuilder(E e, S s) { this.entity = e; this.service = s; }
public E lazy(E entity, Function<E,?> proxyMapper) {
return service.fetchLazy(entity, proxyMapper); // DELEGATE
}
}
}
PersonService
#Stateless
public class PersonService extends Service<PersonService,Person> { ... }
PersonBean
#Named #ViewScoped
public class PersonBean implements Serializable {
#EJB private PersonService personService;
#PostConstruct public void init() {
person = personService.getById(id);
person = personService.fetchLazy(person, Person::getCompany); // OK
person = personService.fetch(person).lazy(Person::getCompany); // EXCEPTION
}
}
I will assume this service is a Java EE or Spring transactional service. Declarative transactions are proxy-based. When you get an instance of a service using dependency injection and call a transactional method, you in fact call the method of a transactional proxy that wraps the service:
client ----------> transactional proxy -----------> service
- start the transaction
- call the service
- commit
-return the value returned by the service
When you call fetchLazy(), everything works fine:
a transaction is started,
then the entity is found using the entity manager and its company proxy is initialized,
then the transaction is committed
then you get the entity with its initialized company.
When you call fetch(), here's what happens
a transaction is started,
a FetchBuilder is constructed
then the transaction is committed
then you get the FetchBuilder
This transaction is actually useless, since you never use the entity manager.
Now what happens when you call fetch() on the returned builder? It calls fetchLazy on the service instance variable of the FetchBuilder. This service is an instance of the actual service instance, not an instance of the proxy that wraps the service instance, since you initialized it with this, from the service instance itself. You're thus bypassing the proxy, and these is thus no transaction wrapping the call to find() and the intialization of the company.
Related
I have a Spring Boot 2.3 REST application with a standard architecture (controller -> service -> repository). For auditing purposes, I inserted a thin layer (some kind of a Mediator), so that I persist all the requests to some specific service method regardless they are successfully persisted or an exception is thrown and the transaction is rollbacked. Example:
#Component
public class Mediator {
private final Service service;
private final AuditService auditService;
public Mediator(Service service, AuditService auditService) {
this.service = service;
this.auditService = auditService;
}
#Transactional
public void saveReport(Report report) {
try {
service.saveReport(report);
auditService.saveReport(report);
} catch (Exception exception) {
auditService.saveReport(report, exception);
throw exception;
}
}
}
Thus I encountered a weird situation: if I place the #Transactional on the Mediator's method (example above), all the operations in the JPA Repositories are successfully persisted. If I place the #Transactional on the ServiceImpl method instead (example below) and no longer on the Mediator, one of the delete queries is not ran, although the rest of the queries are executed just fine. Suppose my ServiceImpl looks something like:
#Service
public class ServiceImpl implements Service {
private final RepositoryA repositoryA;
private final RepositoryB repositoryB;
public ServiceImpl(RepositoryA repositoryA, RepositoryB repositoryB) {
this.repositoryA = repositoryA;
this.repositoryB = repositoryB;
}
#Transactional
public void saveReport(Report report) {
repositoryA.save(report.getA());
repositoryB.save(report.getB());
repositoryB.delete(report.getSomethingElse());
}
}
The only visible difference between the two approaches with respect to the Transactions is that in the first scenario, I have this for each Mediator call:
o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(909894553<open>)] for JPA transaction
while in the second scenario, I have this:
tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
I guess there is a difference between annotating directly a bean's method with #Transactional (what I do in the Mediator) and annotating a bean's (that is the implementation of the interface injected) method with the same thing (what we usually do by annotating a ServiceImpl method), but I am not sure and cannot spot the reason for this weird behaviour. Does anyone have an idea why this happens?
I guess that this difference in behavior is due to Spring OpenSessionInView which is enabled by default.
You must set in application.yml
spring:
jpa:
open-in-view: false
Please see OSIV
Ok I have a scenario here, and I want a standard solution, basically I have exposed EJBsession3 beans as my webservice on the server side and they further call the EJBsession3 bean to execute DAO methods. see below is the sample Code.
//This is a EJB3 session bean and I am using container managed jta transactions
#Stateless
public class datatypes implements datatypesRemote {
#PersistenceContext(unitName = "EJBtest")
EntityManager entityManager;
public List<Datatype> retrieveAllDatatypes(int targetSystemId,EntityManager ent)
throws RuntimeException {
String q = "SELECT oneDatatype from " + Datatype.class.getName()
+ " oneDatatype "
+ "where oneDatatype.targetSystem.targetSystemId = "
+ targetSystemId;
//entityManager=ent;
Query query = entityManager.createQuery(q);
System.out.println("Query retrieveAll" + q);
#SuppressWarnings("unchecked")
List<Datatype> templates = query.getResultList();
return templates;
}
}
The class above is basically my DAO class that will handle queries now below is my web services class
#WebService(serviceName="backend")
#Stateless
public class backEndForm implements backEndFormRemote {
//#PersistenceContext(name = "EJBtest")
//EntityManager entityManager;
private datatypesRemote data= new datatypes();
public List<Datatype> retrieveAllDatatypes(int id){
//entityManager.clear();
data=new datatypes();
return data.retrieveAllDatatypes(id,null);
}
}
Now the Issue is like this my web client calls my web service method and that method further calls the DAO methods that will fetch the data from data base and return to the web service and web service will return data to the requesting client
But when I execute my SQL query the entity manager is null I don't know why so a non standard solution I developed is to pass the entity manager from the web services class to the DAO class, as you can see in the sample code which is commented out.
So my question, is there any other Standard way to do it ? why the entity manager is null in the second ejb3 bean and not in the first ejb3 bean ?
Injection does not happen when you create objects with the new operator. You need to let your container create the datatypes bean and inject it into your backEndForm:
#WebService(serviceName="backend")
#Stateless
public class backEndForm implements backEndFormRemote {
#EJB
private datatypesRemote data;
public List<Datatype> retrieveAllDatatypes(int id){
return data.retrieveAllDatatypes(id,null);
}
}
I have an application which has a #Remote #Singleton EJB which injects a #RequestScoped entity manager produced by CDI. Another application on the same server (wildfly 9)/JVM will use this EJB to get a result fetched from the entity manager.
The first invocation of the EJB will return the expected result. It produces the entity manager, fetches the data and disposes the entity manager again when the invocation returns. Each subsequent invocation of that EJB will throw an error because of a closed entity manager. No produce / dispose for a new entity manager is made.
Is this the expected bahavior? Do I have an error in my code?
IFrameworkResourceManager framework = _applicationContext.getFrameworkResourceManager();
final User resolvedUser = framework.resolveUser(username, domain);
// ...
final Rights resolvedRights = framework.resolveRights(resolvedUser.getGuid(), applicationId);
// ...
This piece of code is executed in a CDI producer which again is executed as soon as a new http session is created for an user. Nothing changes if I call getFramworkResourceManager again before invoking resolveRights.
public IFrameworkResourceManager getFrameworkResourceManager() {
return IFrameworkResourceManager frm = (IFrameworkResourceManager) ctx
.lookup("java:global/WebFramework/WebFrameworkImpl!my.package.IWebFramework");
}
It doesn't matter if I use a direct JNDI lookup or #EJB injection. The returned instance is reported (toString()) as Proxy for remote EJB StatelessEJBLocator for "/WebFramework/WebFrameworkImpl", view is interface my.package.IWebFramework, affinity is None
#LocalBean
#Singleton
public class WebFrameworkImpl implements IWebFramework, Serializable {
#Inject
private EntityManager _entityManager;
#Override
public User resolveUser(String username, String domain) {
System.out.println(_entityManager + " || " + _entityManager.isOpen());
// execute query using QueryDSL and the injected entityManager
}
#Override
public Rights resolveRights(String guidUser, int applicationId) {
System.out.println(_entityManager + " || " + _entityManager.isOpen());
// execute query using QueryDSL and the injected entityManager
}
}
#Remote
public interface IWebFramework extends IFrameworkResourceManager {
// some methods...
}
public interface IFrameworkResourceManager {
public User resolveUser(String username, String domain);
public Rights resolveRights(String guidUser, int applicationId);
}
Sysout of resolveUser: org.hibernate.jpa.internal.EntityManagerImpl#379e882b || true
Sysout of resolveRights: org.hibernate.jpa.internal.EntityManagerImpl#379e882b || false
Edit 20.11.2015 13:43: Persistence unit is of type RESOURCE_LOCAL. Additionally all #ResourceScoped beans are affected. #PostConstruct and #PreDestroy are only invoked for the first EJB invocation. Each subsequent invocation uses the previous instance of the resource scoped bean which is not correct.
Edit 20.11.2015 13:55: Everything works as expected if the EJB is invoked from within the same application that provides the EJB. This behavior only appears for invocations from other applications.
Edit 20.11.2015 15:24: JBoss AS 7.1.3.Final, Wildfly 9.0.0.Final and Wildfly 10.0.0.CR4 are all effected. But according to the CDI spec (1.0 to 1.2) chapter 6.7.4 this should work. I've filled a bug report (WFLY-5716).
When using RESOURCE_LOCAL, you shoud create youe EntityManager from EntityManagerFacgtory and handle it by yourself, like:
private EntityManagerFactory factory = Persistence.createEntityManagerFactory("unit-name");
public void someMethod(){
EntityManager em = emf.createEntityManager();
EntityTransaction tx = null;
try {
tx = em.getTransaction();
tx.begin();
// do some work
tx.commit();
}
catch (RuntimeException e) {
if ( tx != null && tx.isActive() )
tx.rollback();
throw e; // or display error message
}
finally {
em.close();
}
}
Bugfix for this wired behavior is already merged in the WELD repository:
WFLY-5716
WELD-2069
Is it secure to pass a Injected EntityManager created on an EJB, to a method that will return an Object, and after, persist that Object on a Web Session for web clients use it?
Like in this example:
the EJB
#Stateless(mappedName = "MyService")
#LocalBean
public class MyService implements MyServiceLocal {
#PersistenceContext(unitName="primary")
private EntityManager em;
/**
* Default constructor.
*/
public MyService() {
}
#Override
public Service newServiceX(User user) {
return new ServiceX(user,em); // here, passing the EntityManager
}
}
After, I persist this Service in a web client (using struts):
The base action
public class YAction extends ActionSupport implements SessionAware{
#Inject
private MyServiceLocal service;
public String execute(){
Service x = service.newServiceX();
persistInCookie("ServiceX",x);
}
public void persistInCookie(String, Object){
// persist
}
}
And after, using another Action:
// another Action that
class XAction{
public String useService(){
getService().doSomething();
}
protected Service getService(){
Service service = (Service) getSessionMap().get("ServiceX");
return service;
}
}
the POJO class ServiceX using the EntityManager:
public class ServiceX extends Service{
EntityManager em;
public ServiceX(User user, EntityManager em){
this.em = em;
}
public void doSomething(){
// do something with the EntityManager passed by the EJB
}
}
First, the action that would be call is the Y action to persist the Service on the Session, next, the X action will return the Service persisted on the Session and try to use it.
I believe that the EJB Stateless Session Bean can close My EntityManager and this ServiceX POJO class can't use it. This can happen? I found similar question HERE, but in this question, the EntityManager is passed to a helper class. In my case is different because I want to persist this Object on a session cookie, and use later.
I don't think It is a good idea to store a EntityManager in SessionMap. What is more, I don't even think that it is a good idea to perform EntityManager operations outside the EJB container.
Have read about transaction-boundaries in JPA?
By default, EJB container is using CMT (Container Managed Transactions). In this case, container uses entitymanager-per-request pattern which means that the transaction begins and ends when one of the business methods of MyService starts and ends (transaction is committed or rollbacked in case of RuntimeException). For whole transaction time, EntityManager is connected with the same PersistenceContext. After the transaction is ended the container closes EntityManager which means that the EntityManager is disconnected with recent PersistenceContext:
// transaction begins
Service x = service.newServiceX();
// transaction ends
This might be crucial if you were going to do some update/insert operations outside the transaction.
Now, when you call EntityManager operation (like find) outside the transaction, for every each operation the EntityManager will create new PersistentContext. This may cause some issues, as two entities that represent the same record will be treated as different entities:
// each operation occurs in a separate persistence context, and returns
// a new detached instance
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 != mag1);
Some more articles to read:
Persistent Context
Transactions and Concurrency
Entity Lifecycle Management
I am using jboss 5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634). And need to get business interface of the bean. That is necessary for transaction management.
So I have:
#Local
public interface MyBeanInterface {
void transactionalMethod();
}
#Stateless
public class MyBean implements MyBeanInterface {
#Resource
private SessionContext context;
private int aState;
public void someMethod() {
aState = 42;
context.getBusinessObject(MyBeanInterface.class).transactionalMethod();
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void transactionalMethod() {
System.out.println(aState); // 0!!!!!
}
}
For some reason I do not get the same bean, but new bean is created. That is disastrous as transactionalMethod needs the state variable value to execute correctly.
What am I doing wrong, or that is a bug of jboss? By the way there is a bug which affects ability to get business object via bean's class: https://issues.jboss.org/browse/EJBTHREE-2126. Not sure however if it relates to my issue.
The best solution is this:
#Stateless
public class MyBean implements MyBeanInterface {
#Resource private TransactionManager tm;
private int aState;
public void someMethod() {
aState = 42;
Transaction transaction = tm.suspend();
transactionalMethod();
tm.resume(transaction);
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void transactionalMethod() {
System.out.println(aState); // 0!!!!!
}
}
When you call a published interface method from the same istance, passing by ejb context, the resource is:
If it is #Stateless, a new instance is created.
If it is #Stateful, a new session is created for the first call, then other call are same as #Singleton.
If it is #Singleton, the caller waits for the resource to be freed, in case it calls itself, a deadlock is created. If the method is annotated with #Read, calling yourself does not create any deadlocks.
I don't have time to see if the syntax is perfect but you could try:
InitialContext jndiContext = new InitialContext();
Object ref = jndiContext.lookup("projname/MyBeanInterface/local");
MyBeanInterfaceLocal m = (MyBeanInterfaceLocal) ref;
However I saw that you have a SessionContext field, so maybe for you the code should be a little bit different. Maybe it would be:
Object ref = SessionContext.lookup("projname/MyBeanInterface/local");
MyBeanInterfaceLocal m = (MyBeanInterfaceLocal) ref;
Let me know if this helps!