PersistanceContext on NonServlet class - jpa

I have the following...
public class TestServlet extends HttpServlet {
#PersistenceContext
private EntityManager em;
...
}
#Stateless
#LocalBean
public class DeviceRegistrationPersister {
#PersistenceContext(unitName="deviceReg")
private EntityManager em;
...
}
The first one gets the entity manager just fine, however, the second the entity manager is null. Can someone help with what is going on?

Related

Spring Data 2.x: adding customer behavior to all repositories

I have a web app that was built with Spring Data 1.6.1.RELEASE. It adds custom behavior too all repositories by following the example in the Spring Data' online documentation here:
1.3.2 Adding custom behavior to all repositories
https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
Basically, it extends JpaRepository with custom behavior shown below:
public interface MyRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
void sharedCustomMethod(ID id);
}
Here is the code for implementation of this new interface:
public class MyRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
private EntityManager entityManager;
public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
public void sharedCustomMethod(ID id) {
// implementation goes here
}
}
Custom repository factory bean:
public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory(entityManager);
}
private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory
//to check for QueryDslJpaRepository's which is out of scope.
return MyRepository.class;
}
}
}
Here is the declaration of the factory bean:
<repositories base-package="com.acme.repository"
factory-class="com.acme.MyRepositoryFactoryBean" />
Everything works well with Spring Data 1.6.1.RELEASE. However, I got the following exception when starting Jetty in Eclipse after I switched to Spring Data 2.3.2.RELEASE:
java.lang.IllegalStateException: No suitable constructor found on interface com.acme.repository.MyRepository to match the given arguments: [class org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation, class com.sun.proxy.$Proxy52]. Make sure you implement a constructor taking these
at org.springframework.data.repository.core.support.RepositoryFactorySupport.lambda$getTargetRepositoryViaReflection$4(RepositoryFactorySupport.java:522)
at java.util.Optional.orElseThrow(Optional.java:290)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getTargetRepositoryViaReflection(RepositoryFactorySupport.java:522)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getTargetRepositoryViaReflection(RepositoryFactorySupport.java:506)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:180)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:162)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:72)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:309)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297)
at org.springframework.data.util.Lazy.getNullable(Lazy.java:212)
at org.springframework.data.util.Lazy.get(Lazy.java:94)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:144)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:878)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:401)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:292)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103)
at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:843)
at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:533)
at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:816)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:345)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1406)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1368)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:778)
at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:262)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:522)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
at org.eclipse.jetty.server.Server.start(Server.java:427)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:105)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.server.Server.doStart(Server.java:394)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at net.sourceforge.eclipsejetty.starter.jetty9.Jetty9Adapter.start(Jetty9Adapter.java:68)
at net.sourceforge.eclipsejetty.starter.common.AbstractJettyLauncherMain.launch(AbstractJettyLauncherMain.java:84)
at net.sourceforge.eclipsejetty.starter.jetty9.Jetty9LauncherMain.main(Jetty9LauncherMain.java:42)
What can I try to fix this?
UPDATE
I added the following constructor but still have the same problem:
public MyRepositoryImpl(
JpaMetamodelEntityInformation<T, ID> entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.em = entityManager;
}
I just setup an application with 2.3.2.RELEASE and its look like it has been simplified.
Delete MyRepositoryFactoryBean
Replace <repositories base-package="com.acme.repository... with
<repositories base-package="com.acme.repository"
base-class="….MyRepositoryImpl" />
MyRepositoryImpl
public class MyRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
private EntityManager entityManager;
MyRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
// Keep the EntityManager around to used from the newly introduced
this.entityManager = entityManager;
}
public void sharedCustomMethod(ID id) {
// implementation goes here
}
}
Make sure MyRepository is outside the package base-package i.e com.acme.repository.
Note
#EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) on a class annotated #Configuration is equivalent to point 2. As long as the package of that configuration class is com.acme.repository
#NoRepositoryBean annotation on MyRepository is equivalent to point 4
Sample Spring boot app with java config for reference
https://github.com/kavi-kanap/stackoveflow-63223076
Import it as maven project to your IDE. Right click on AccessingDataJpaApplication and run, it will start, save some entities and call your custom method

Is it possible to pass EntityManager as a parameter through multiple beans?

I know i can do the following :
public class MyDao{
private EntityManager em;
public void setEm(EntityManager em){
this.em = em;
}
...
and then, using #PostConstuct to pass the EntityManager
public class MyBean{
private EntityManager em;
#Inject
private MyDao myDao;
#PostConstruct
private void init(){
myDao.setEm(em);
}
...
But due to my application's architecture restriction i cannot directly inject MyDao into MyBean, i should pass by MyBusinessDao Class, so i tried the following but i'm getting a nullPointerExeception on the value of EntityManager in MyDao :
public class MyBean{
private EntityManager em;
public MyBean(){
em = createEntityManager();
}
private EntityManager createEntityManager(){
//dynamically create the EntityManager
}
#Inject
private MyBusinessDao myBusinessDao;
#PostConstruct
private void init(){
myBusinessDao.setEm(em);
}
...
and in MyBusinessDao i inject MyDao:
public class MyBusinessDao {
private EntityManager em;
#Inject
private MyDao myDao;
#PostConstruct
private void init(){
myDao.setEm(em);
}
...
I should mention that i'm not using a J2EE container
You can implement an CDI Producer Method for providing the EntityManager via CDI injection.
#ApplicationScoped
class EntityManagerProducer {
#PersistenceContext(...)
private EntityManager em;
#Produces
#RequestScoped
public EntityManager produceEm() {
return em;
}
}
You can also inject the EntityManagerFactory and call emf.createEntityManager() in the producer method and implement an CDI-Disposer method, which closes the EntityManager before the scope is finished.
public void dispose(#Disposes EntityManager em) { ... }
If you have multiple persistence contexts, then implement a producer method for each persistence context and qualify them with a CDI-Qualifier.
I resolved it this way :
public class MyBusinessDao {
private EntityManager em;
#Inject
private MyDao myDao;
private void setEm(EntityManager em){
this.em = em;
//and here i call the setEm method of myDao also
myDao.setEm(em);
}
...

How to use different EntityManager based on condition at runtime?

My congfiguration is like below:
The abstract class:
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
// other methods create(T), edit(T), ...
The Ejb that extends the abstract class (i have many others EJBs and about 12 different persistence units):
#Stateless
public class FilesDao extends AbstractFacade<Files> {
#PersistenceContext(unitName = "myfirstPU")
private EntityManager firstEm;
#PersistenceContext(unitName = "mysecondPU")
private EntityManager secondEm;
// i have more than two persistenceUnit ...
#Override
protected EntityManager getEntityManager() {
return firstEm; // or secondEm based on condition
}
public FilesDao() {
super(Files.class);
}
public Files findByFileref(String inFileRef) {
try {
Query q = firstEm.createNamedQuery("Files.findByFileref"); // or secondEm based on condition
q.setParameter("fileref", inFileRef);
Files file = (Files) q.getSingleResult();
return file;
} catch (NoResultException e) {
return null;
}
}
I want to use FilesDao like this :
#Stateless
#LocalBean
public class FileBusiness {
#EJB
FilesDao fileDao;
public void myMethod(){
if(condition1){
//use the FileDao with the EnityManager **firstEm**
}
else if(condition2){
//use the FileDao with the EnityManager **secondtEm**
}
...
}
Is there a way to achieve that ?
I read about using CDI with produces method.
CDI-Producers are not that hard, see the following example.
The following classes are CDI-Qualifier annotations, which are used to distinguish implementations.
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR })
#Qualifier
public #interface MyfirstPUQualifier { }
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR })
#Qualifier
public #interface MysecondPUQualifier { }
The following is a CDI-Bean which injects the different EntityManagers and implements producer methods, making the two EntityManagers available to CDI, distinguished via CDI-Qualifiers
#ApplicationScoped
class MYProducer {
#PersistenceContext(unitName = "myfirstPU")
private EntityManager firstEm;
#PersistenceContext(unitName = "mysecondPU")
private EntityManager secondEm;
#Produces
#RequestScoped
#MyfirstPUQualifier
public EntityManager produceMyfirstPU(){
return firstEm;
}
#Produces
#RequestScoped
#MysecondPUQualifier
public EntityManager produceMysecondPU(){
return secondEm;
}
}
The following shows a CDI-Bean injecting both EntityManagers. This could be extracted to an abstract base class, because maybe needed by other DAOs as well.
#Stateless
public class FileDao {
#Inject
#MyfirstPUQualifier
private EntityManager emFirst;
#Inject
#MysecondPUQualifier
private EntityManager emSecond;
public void myDaoMEthod(){
final EntityManager em = getEntityManager();
...
}
private EntityManager getEntityManager(){
if(condition1){
return emFirst;
}
else if(condition2){
return emSecond;
}
}
}
No can use the FileDao bean without taking care what EntityManager to use, because shouldn't be decided in this bean anyway
#Stateless
#LocalBean
public class FileBusiness {
#EJB
FilesDao fileDao;
public void myMethod(){
// FileDao will decide what entity manager to use
fileDao.doStruff();
}
}

Should I clear PersistenceContext with Singleton/Schedule?

Let's say I have an EJB for scheduled job.
#Singleton
#Startup
public class MySchedule {
#Schedule
public void doSomething() {
// select oldest 8192 entities (MyEntity.class)
// and process them
entityManager.clear(); // should I do this?
}
#PersistenceContext
private EntityManager entityManager;
}
Is it possible those selected entities are kept in the entityManager? Should(Ought) I clear it?

Injection EJB into Java Class

How can i inject an EJB (3.0) in simple java class ?
When inject an EJB in my Java class, on execution i get a NullPointerException.
My Java Class test :
public class test {
#EJB MyEjb myEjb;
public test(){
myEjb.getUserbyId(2); // myEjb = null
}
}
My EJB
#Stateless
#LocalBean
public class MyEjb {
#PersistenceContext(unitName="jpa")
EntityManager EM;
public User getUserbyId(Integer id){
....
}
You should use InitialContext#lookup method to obtain EJB reference from an application server.
For example:
#Stateless(name="myEJB")
public class MyEJB {
public void ejbMethod() {
// business logic
}
}
public class TestEJB {
public static void main() {
MyEJB ejbRef = (MyEJB) new InitialContext().lookup("java:comp/env/myEJB");
ejbRef.ejbMethod();
}
}