How to inject service to hibernate.ejb.interceptor with google guice? - jpa

I have an issue with injecting a service to predefined interceptor using google guice.
What i'm trying to do is to use emptyinterceptor to intercept changes with entities. Interceptor itself works fine, the problem is that I can't figure out how to inject a service to it. Injections themselves work fine throughout the whole application.
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="db-manager">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>test.Address</class>
<properties>
<property name="hibernate.ejb.interceptor" value="customInterceptor"/>
</properties>
</persistence-unit>
how im trying to inject
public class CustomInterceptor extends EmptyInterceptor {
private static final Logger LOG = Logger.getLogger(CustomInterceptor.class);
#Inject
private Provider<UploadedFileService> uploadedFileService;
...
}
how JpaPersistModule is initiated
public class GuiceListener extends GuiceServletContextListener {
private static final Logger LOG = Logger.getLogger(GuiceListener.class);
#Override
protected Injector getInjector() {
final ServicesModule servicesModule = new ServicesModule();
return Guice.createInjector(new JerseyServletModule() {
protected void configureServlets() {
// db-manager is the persistence-unit name in persistence.xml
JpaPersistModule jpa = new JpaPersistModule("db-manager");
...
}
}, new ServicesModule());
}
}
how services are initiated
public class ServicesModule extends AbstractModule {
#Override
protected void configure() {
bind(GenericService.class).to(GenericServiceImpl.class);
bind(AddressService.class).to(AddressServiceImpl.class);
}
}

I searched for hours and have find no real solution, so the ugly workaround I am using is to create 2 interceptors.
The first one is bound correctly by hibernate but does not have anything injected. It calls the second interceptor via some other mechanism - in the example below via a static reference to the InjectorFactory. The second interceptor is not bound to Hibernate, but like any other class, it can happily have stuff injected into it.
//The first ineterceptor has methods like this...
#Override
public synchronized void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
InjectorFactory.getInjector().getInstance(MyOtherInterceptor.class).onDelete(entity, id, state, propertyNames, types);
}
d
//The second one has the real implementation
#Inject
public MyOtherInterceptor() {
}
#Override
public synchronized void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
//Full implementation
}
//etc

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

#Autowired does not work in SimpleJpaRepository extension

When trying to override SimpleJpaRepository, adding other beans via #Autowired does not work. How can beans be injected in this case? Here is an implementation:
public class BaseDAO<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID>
implements IDAO<T, ID> {
#Autowired
private SomeBean someBean; // NULL!
}
Instances of BaseDAO are not Spring-managed beans in themselves and therefore autowiring through #Autowired will not work out-of-the-box. Dependencies therefore need to be injected into BaseDAO instances.
Step 1: Have a Spring ApplicationContext available somewhere
#Component
class SpringContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(final ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public static ApplicationContext getContext() { return CONTEXT; }
}
This will be required to autowire the dependencies for the custom repository implementation at the time of repository creation.
Step 2: Extend SimpleJpaRepository
class BaseDAO<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> {
#Autowired
private Dependency dependency;
}
Step 3: Autowire dependencies through a JpaRepositoryFactoryBean
class ExtendedJPARepositoryFactoryBean<R extends JpaRepository<T, ID>, T, ID extends Serializable>
extends JpaRepositoryFactoryBean<R, T, ID> {
private static class ExtendedJPARepositoryFactory<T, ID extends Serializable> extends JpaRepositoryFactory {
public ExtendedJPARepositoryFactory(final EntityManager entityManager) {
super(entityManager);
}
protected Class<?> getRepositoryBaseClass(final RepositoryMetadata metadata) {
return isQueryDslExecutor(metadata.getRepositoryInterface())
? QueryDSLJPARepository.class
// Let your implementation be used instead of SimpleJpaRepository.
: BaseDAO.class;
}
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
RepositoryInformation information, EntityManager entityManager) {
// Let the base class create the repository.
final SimpleJpaRepository<?, ?> repository = super.getTargetRepository(information, entityManager);
// Autowire dependencies, as needed.
SpringContext.getContext()
.getAutowireCapableBeanFactory()
.autowireBean(repository);
// Return the fully set up repository.
return repository;
}
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new ExtendedJPARepositoryFactory(entityManager);
}
}
Step 4a: Configure Spring Data JPA to use your factory bean (XML configuration)
<jpa:repositories base-package="org.example.jpa"
factory-class="org.example.jpa.ExtendedJPARepositoryFactoryBean"/>
Step 4b: Configure Spring Data JPA to use your factory bean (Java configuration)
#EnableJpaRepositories(repositoryFactoryBeanClass = ExtendedJPARepositoryFactoryBean.class)
In order to let Spring know that it needs to inject something inside your DAO you need to annotate it with #Component.
You can read more about this here: http://docs.spring.io/autorepo/docs/spring-boot/current/reference/html/using-boot-spring-beans-and-dependency-injection.html.

guice JpaPersistModule with runtime configuration

I need to share datasource with JpaPersistModule. This datasource is provided by guice injector.
Now the problem I have to build module during configuration phase, but datasource is available only in runtime.
Currently I have following code:
public class MyJpaConfigurationModule implements Module {
private Map<String, Object> jpaProperties = new HashMap<>();
private Module jpaModule = new JpaPersistModule("peristenceUnit").properties(jpaProperties);
public void configure(Binder binder) {
binder.requestInjection(this);
binder.install(jpaModule);
}
#Provides #Singleton
public DataSource provideDatasource() {
return ..... // some data source
}
#Inject
public void setJpaProperties(DataSource dataSource, PersistService persistService) {
jpaProperties.put("dataSource", dataSource);
persistService.start();
}
}
I have checked and it seems that jpa properties map is everywhere injected by reference, so my runtime changes should become visible, but what if this changes in future?
What is the correct way to resolve such conflicts?

JPA EntityManager Nullpointer Exception when splitting functionality in multiple classes (Glassfish, EclipseLink)

i know there are masses of questions concerning a similar problem, but i didn't manage to extract a solution for my problem.
I'm trying to build a RESTful webapplication that serves data, which is provided using JPA (EclipseLink 2.4) and runs on Glassfish 3.1.2.2.
For testing i set up a guestbook database in mysql and created this all-in-one solution:
// imports etc...
// Guestbook class has #Entity
#Stateless
#Path("guestbook")
public class GuestService {
#PersistenceContext
private EntityManager em;
#GET
public Response entry() {
Guestbook entry = em.find(Guestbook.class, someid);
return Response.ok(entry.getMsg()).build();
}
}
This works perfectly well, but i want to split it in to several layers like so: REST -> DAO -> Entity
Which would transform to:
#Stateless
#Path("guestbook")
public class GuestService {
private DAO dao;
#GET
public Response entry() {
Guestbook entry = dao.getEntry(someid);
return Response.ok(entry.getMsg()).build();
}
}
// ...
public class DAO {
#PersistenceContext
private EntityManager em;
public Guestbook getEntry(int someid) {
return em.find(Guestbook.class, someid);
}
}
So this would produce a nullpointer exception when accessing the em in getEntry() because it is null.
My persistence.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
<persistence-unit name="guestbook" transaction-type="JTA">
<jta-data-source>jdbc/mysqldatasource</jta-data-source>
<class>model.Guestbook</class>
</persistence-unit>
</persistence>
Thanks in advance, i'm looking forward for your suggestions!
You've abstracted the DAO from GuestService just fine, but it seems that you forgot to make the DAO a #Stateless EJB and inject it in your GuestService using #EJB annotation.
#Stateless
public class DAO {
#PersistenceContext
private EntityManager em;
...
}
#Stateless
#Path("guestbook")
public class GuestService {
#EJB
private DAO dao;
...
}
I hope it helps!

Create JPA EntityManager without persistence.xml configuration file

Is there a way to initialize the EntityManager without a persistence unit defined? Can you give all the required properties to create an entity manager? I need to create the EntityManager from the user's specified values at runtime. Updating the persistence.xml and recompiling is not an option.
Any idea on how to do this is more than welcomed!
Is there a way to initialize the EntityManager without a persistence unit defined?
You should define at least one persistence unit in the persistence.xml deployment descriptor.
Can you give all the required properties to create an Entitymanager?
The name attribute is required. The other attributes and elements are optional. (JPA specification). So this should be more or less your minimal persistence.xml file:
<persistence>
<persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
SOME_PROPERTIES
</persistence-unit>
</persistence>
In Java EE environments, the jta-data-source and non-jta-data-source elements are used to specify the global JNDI name of the JTA and/or non-JTA data source to be used by the persistence provider.
So if your target Application Server supports JTA (JBoss, Websphere, GlassFish), your persistence.xml looks like:
<persistence>
<persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
<!--GLOBAL_JNDI_GOES_HERE-->
<jta-data-source>jdbc/myDS</jta-data-source>
</persistence-unit>
</persistence>
If your target Application Server does not support JTA (Tomcat), your persistence.xml looks like:
<persistence>
<persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
<!--GLOBAL_JNDI_GOES_HERE-->
<non-jta-data-source>jdbc/myDS</non-jta-data-source>
</persistence-unit>
</persistence>
If your data source is not bound to a global JNDI (for instance, outside a Java EE container), so you would usually define JPA provider, driver, url, user and password properties. But property name depends on the JPA provider. So, for Hibernate as JPA provider, your persistence.xml file will looks like:
<persistence>
<persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>br.com.persistence.SomeClass</class>
<properties>
<property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="hibernate.connection.url" value="jdbc:derby://localhost:1527/EmpServDB;create=true"/>
<property name="hibernate.connection.username" value="APP"/>
<property name="hibernate.connection.password" value="APP"/>
</properties>
</persistence-unit>
</persistence>
Transaction Type Attribute
In general, in Java EE environments, a transaction-type of RESOURCE_LOCAL assumes that a non-JTA datasource will be provided. In a Java EE environment, if this element is not specified, the default is JTA. In a Java SE environment, if this element is not specified, a default of RESOURCE_LOCAL may be assumed.
To insure the portability of a Java SE application, it is necessary to explicitly list the managed persistence classes that are included in the persistence unit (JPA specification)
I need to create the EntityManager from the user's specified values at runtime
So use this:
Map addedOrOverridenProperties = new HashMap();
// Let's suppose we are using Hibernate as JPA provider
addedOrOverridenProperties.put("hibernate.show_sql", true);
Persistence.createEntityManagerFactory(<PERSISTENCE_UNIT_NAME_GOES_HERE>, addedOrOverridenProperties);
Yes you can without using any xml file using spring like this inside a #Configuration class (or its equivalent spring config xml):
#Bean
public LocalContainerEntityManagerFactoryBean emf(){
properties.put("javax.persistence.jdbc.driver", dbDriverClassName);
properties.put("javax.persistence.jdbc.url", dbConnectionURL);
properties.put("javax.persistence.jdbc.user", dbUser); //if needed
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); //If your using eclipse or change it to whatever you're using
emf.setPackagesToScan("com.yourpkg"); //The packages to search for Entities, line required to avoid looking into the persistence.xml
emf.setPersistenceUnitName(SysConstants.SysConfigPU);
emf.setJpaPropertyMap(properties);
emf.setLoadTimeWeaver(new ReflectiveLoadTimeWeaver()); //required unless you know what your doing
return emf;
}
Here's a solution without Spring.
Constants are taken from org.hibernate.cfg.AvailableSettings :
entityManagerFactory = new HibernatePersistenceProvider().createContainerEntityManagerFactory(
archiverPersistenceUnitInfo(),
ImmutableMap.<String, Object>builder()
.put(JPA_JDBC_DRIVER, JDBC_DRIVER)
.put(JPA_JDBC_URL, JDBC_URL)
.put(DIALECT, Oracle12cDialect.class)
.put(HBM2DDL_AUTO, CREATE)
.put(SHOW_SQL, false)
.put(QUERY_STARTUP_CHECKING, false)
.put(GENERATE_STATISTICS, false)
.put(USE_REFLECTION_OPTIMIZER, false)
.put(USE_SECOND_LEVEL_CACHE, false)
.put(USE_QUERY_CACHE, false)
.put(USE_STRUCTURED_CACHE, false)
.put(STATEMENT_BATCH_SIZE, 20)
.build());
entityManager = entityManagerFactory.createEntityManager();
And the infamous PersistenceUnitInfo
private static PersistenceUnitInfo archiverPersistenceUnitInfo() {
return new PersistenceUnitInfo() {
#Override
public String getPersistenceUnitName() {
return "ApplicationPersistenceUnit";
}
#Override
public String getPersistenceProviderClassName() {
return "org.hibernate.jpa.HibernatePersistenceProvider";
}
#Override
public PersistenceUnitTransactionType getTransactionType() {
return PersistenceUnitTransactionType.RESOURCE_LOCAL;
}
#Override
public DataSource getJtaDataSource() {
return null;
}
#Override
public DataSource getNonJtaDataSource() {
return null;
}
#Override
public List<String> getMappingFileNames() {
return Collections.emptyList();
}
#Override
public List<URL> getJarFileUrls() {
try {
return Collections.list(this.getClass()
.getClassLoader()
.getResources(""));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
#Override
public URL getPersistenceUnitRootUrl() {
return null;
}
#Override
public List<String> getManagedClassNames() {
return Collections.emptyList();
}
#Override
public boolean excludeUnlistedClasses() {
return false;
}
#Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
#Override
public ValidationMode getValidationMode() {
return null;
}
#Override
public Properties getProperties() {
return new Properties();
}
#Override
public String getPersistenceXMLSchemaVersion() {
return null;
}
#Override
public ClassLoader getClassLoader() {
return null;
}
#Override
public void addTransformer(ClassTransformer transformer) {
}
#Override
public ClassLoader getNewTempClassLoader() {
return null;
}
};
}
I was able to create an EntityManager with Hibernate and PostgreSQL purely using Java code (with a Spring configuration) the following:
#Bean
public DataSource dataSource() {
final PGSimpleDataSource dataSource = new PGSimpleDataSource();
dataSource.setDatabaseName( "mytestdb" );
dataSource.setUser( "myuser" );
dataSource.setPassword("mypass");
return dataSource;
}
#Bean
public Properties hibernateProperties(){
final Properties properties = new Properties();
properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect" );
properties.put( "hibernate.connection.driver_class", "org.postgresql.Driver" );
properties.put( "hibernate.hbm2ddl.auto", "create-drop" );
return properties;
}
#Bean
public EntityManagerFactory entityManagerFactory( DataSource dataSource, Properties hibernateProperties ){
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource( dataSource );
em.setPackagesToScan( "net.initech.domain" );
em.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
em.setJpaProperties( hibernateProperties );
em.setPersistenceUnitName( "mytestdomain" );
em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
em.afterPropertiesSet();
return em.getObject();
}
The call to LocalContainerEntityManagerFactoryBean.afterPropertiesSet() is essential since otherwise the factory never gets built, and then getObject() returns null and you are chasing after NullPointerExceptions all day long. >:-(
It then worked with the following code:
PageEntry pe = new PageEntry();
pe.setLinkName( "Google" );
pe.setLinkDestination( new URL( "http://www.google.com" ) );
EntityTransaction entTrans = entityManager.getTransaction();
entTrans.begin();
entityManager.persist( pe );
entTrans.commit();
Where my entity was this:
#Entity
#Table(name = "page_entries")
public class PageEntry {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String linkName;
private URL linkDestination;
// gets & setters omitted
}
With plain JPA, assuming that you have a PersistenceProvider implementation (e.g. Hibernate), you can use the PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) method to bootstrap an EntityManagerFactory without needing a persistence.xml.
However, it's annoying that you have to implement the PersistenceUnitInfo interface, so you are better off using Spring or Hibernate which both support bootstrapping JPA without a persistence.xml file:
this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
this.persistenceUnitInfo,
getJpaPropertyMap()
);
Where the PersistenceUnitInfo is implemented by the Spring-specific MutablePersistenceUnitInfo class.
DataNucleus JPA that I use also has a way of doing this in its docs. No need for Spring, or ugly implementation of PersistenceUnitInfo.
Simply do as follows
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jpa.JPAEntityManagerFactory;
PersistenceUnitMetaData pumd = new PersistenceUnitMetaData("dynamic-unit", "RESOURCE_LOCAL", null);
pumd.addClassName("mydomain.test.A");
pumd.setExcludeUnlistedClasses();
pumd.addProperty("javax.persistence.jdbc.url", "jdbc:h2:mem:nucleus");
pumd.addProperty("javax.persistence.jdbc.user", "sa");
pumd.addProperty("javax.persistence.jdbc.password", "");
pumd.addProperty("datanucleus.schema.autoCreateAll", "true");
EntityManagerFactory emf = new JPAEntityManagerFactory(pumd, null);