I have a multi-module Spring application where each module (jar) contains its own persistence.xml.
In jar 1
Persistence.xml
<persistence-unit name="pu_one" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
EntityOne.java
#Entity
public class EntityOne {
}
EntityOneRepository.class
interface EntityOneRepository extends JpaRepository<EntityOne, Long> {
}
In jar 2:
persistence.xml
<persistence-unit name="pu_two" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
EntityTwo.java
#Entity
public class EntityTwo {
}
EntityTwoRepository.class
interface EntityTwoRepository extends JpaRepository<EntityTwo, Long> {
}
Then I use a org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager to put them all together.
My issue is with regards to the JpaRepositories as they are using the default persistence unit, as defined in the configuration of the DefaultPersistenceUnitManager.
Question. How to I instruct each of the repositories to use the correct persistence unit?
I ended up of using
org.springframework.data.jpa.support.MergingPersistenceUnitManager
Instead of the
org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager
Then all of the entities in different jars will be merged into one persistence unit. The trick was to name all persistence units the same.
However I would prefer to inform each repository what persistence unit to use, this works for now.
Related
I have this orm.xml file to register AuditingEntityListener globally for all entities.
// orm.xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
version="2.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener
class="org.springframework.data.jpa.domain.support.AuditingEntityListener" />
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
I understand we could use abstract mapped superclass with annotations. But I don't want my entity classes to be extending class. I'm modeling entity classes using kotlin data classes.
#MappedSuperClass
#EntityListeners(AuditingEntityListener.class)
public abstract class AbstractEntity {}
Is there a way to register this entity listener without using orm.xml with java config?
You can register EntityListeners on the EventListenerRegistry.
Just unwrap the SessoinFactory like:
SessionFactoryImplementor sessionFactory = entityManagerFactory.unwrap(
SessionFactoryImplementor.class );
sessionFactory
.getServiceRegistry()
.getService( EventListenerRegistry.class )
.prependListeners( EventType.LOAD, new SecuredLoadEntityListener() );
Spring / HibernateJpaVendorAdapter under JPA 2.1 is not the executing schema generation of Entity model / Metadata info.
I have added two code snippets and the persistence.xml below.
(Checkout my JPA 2.1 properties about schema generation... they seem to be okay. (?))
When I re-run my webapplication (restart of Tomcat) the scripts files are not created on my filesystem. (I even did a complete file search on my filesystem whether they were saved on a different place than expected. But no sign of these files on my filesystem).
What is wrong with the code / persistence.xml below? I checked with the javadocs of Spring's HibernateJpaVendor class but I didn't see anything which could point me to the cause.
Question: What's wrong here... I have been breaking my head on this one?
Remark 1: However, when I move my JPA 2.1 schema generation properties from HibernateJpaVendor to persistence.xml the files are generated successfully. (These properties have been put in comments in my persistence.xml to reflect the problem I am describing above.)
Remark 2: I am using Hibernate 5.1.0.Final and Spring framework 4.2.5.RELEASE
Versions in POM:
<!-- hibernate - validator JSR303-->
<hibernate-validator.version>5.1.3.Final</hibernate-validator.version>
<!-- hibernate -->
<hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version>
<hibernate.version>5.1.0.Final</hibernate.version>
<!-- spring framework -->
<spring.framework.version>4.2.5.RELEASE</spring.framework.version>
Properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/ob?autoReconnect=true
jdbc.username=<user>
jdbc.password=<password>
jdbc.db=obi
jpa.database=MYSQL
hbm2ddl=auto
jpa.showSql=true
jpa.generateDdl=false
jpa.persistenceUnit=obPU
hibernate.hbm2ddl.auto=create-update
hibernate.archive.autodetection=class
c3p0.acquire_increment=5
c3p0.idle_test_period=100
c3p0.max_size=100
c3p0.max_statements=0
c3p0.min_size=10
Persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="obPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.company.ob.domain.model.Obi</class>
<class>com.company.ob.domain.model.Con</class>
<class>com.company.ob.domain.model.ImageMeta</class>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<!--<properties>-->
<!--<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>-->
<!--<property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>-->
<!--<property name="javax.persistence.schema-generation.scripts.create-target" value="obituaries-create.sql"/>-->
<!--<property name="javax.persistence.schema-generation.scripts.drop-target" value="obituaries-drop.sql"/>-->
<!--<property name="javax.persistence.sql-load-script-source" value="obituaries-insert.sql"/>-->
<!--</properties>-->
</persistence-unit>
Code snippets from my Spring Java #Configuration file. My cofiguration is something like this example but not identical (to give an idea/picture):
#Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaAdapter) {
LocalContainerEntityManagerFactoryBean fb = new LocalContainerEntityManagerFactoryBean();
fb.setDataSource(dataSource);
fb.setJpaVendorAdapter(jpaAdapter);
fb.setPersistenceUnitName(persUnit);
fb.setPackagesToScan("com.company.ob.domain.model"); //if set info persistence is not needed. Scan based on Spring and not on JPA
fb.afterPropertiesSet();
return fb.getObject();
}
#Bean
public JpaVendorAdapter jpaAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.valueOf(database));
adapter.setShowSql(Boolean.valueOf(showSql));
// not setting to true because not possible as of Hibernate v5 if using JPA 2.1 schema generation (see javadocs Hibernate)
//adapter.setGenerateDdl(Boolean.valueOf(genDdl));
//adapter.getJpaPropertyMap().put("hibernate.hbm2ddl.auto", hbm2ddl);
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.database.action", "drop-and-create");
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.scripts.action", "drop-and-create");
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.scripts.create-target", "ob-create.sql");
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.scripts.drop-target", "ob-drop.sql");
adapter.getJpaPropertyMap().put("javax.persistence.database-product-name", "MySQL");
// adapter.getJpaPropertyMap().put("javax.persistence.sql-load-script-source", "ob-insert.sql");
return adapter;
}
Why post construct does not get called without putting bean in applicationContext.xml
Here is my class which contains #PostConstruct annotation.
package org.stalwartz.config;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Below is my applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr/spring-dwr-3.0.xsd">
<dwr:annotation-config />
<dwr:annotation-scan base-package="org.stalwartz" scanDataTransferObject="true" scanRemoteProxy="true" />
<dwr:url-mapping />
<!-- <bean id="proeprtyLoader" class="org.stalwartz.config.PropertyLoader"></bean> -->
<dwr:controller id="dwrController" debug="false">
<dwr:config-param name="activeReverseAjaxEnabled" value="true" />
</dwr:controller>
<context:annotation-config>
<context:component-scan base-package="org.stalwartz" annotation-config="true"></context:component-scan>
</context:annotation-config>
<mvc:annotation-driven />
...
...
...
</beans>
Looks simple, but it does not work without uncommenting bean declaration.
In Spring environment initialization callback method (the one annotated by #PostConstruct) make sense only on spring-managed-beans. To make instance(s) of your PropertyLoader class managed, you must do one of the following:
Explicitly register your class in context configuration (as you did)
<bean id="proeprtyLoader" class="org.stalwartz.config.PropertyLoader"></bean>
Let component scanning do the work (as you nearly did), but classes must be annotated by one of #Component, #Repository, #Service, #Controller.
Note from Spring documentation: The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>.
Because putting bean in applicationContext.xml you are adding bean to Spring container, which has interceptor for this annotation. When Spring inject beans it checks #PostConstruct annotation, between others.
When you call simple new PropertyLoader() JVM will not search for the #PostConstruct annotation.
From doc of #PostConstruct annotation:
The PostConstruct annotation is used on a method that needs to be executed
after dependency injection is done to perform any initialization. This
method MUST be invoked before the class is put into service. This
annotation MUST be supported on all classes that support dependency
injection. The method annotated with PostConstruct MUST be invoked even
if the class does not request any resources to be injected.
Singleton is a scope annotation. It can be used to declare 'singletone' scope for a particular bean, but not instantiate it. See this article.
If you want to instantiate your class as singleton you can try Spring Service annotation.
#Service
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Also, you can replace annotation-config tag with component-scan. Here is a good article about differences of annotation-config and component-scan tags.
you are using #Singleton from javax.inject package which is not picked up as bean by spring container. Change it to :
package org.stalwartz.config;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
and the spring will auto detect PropertyLoader and will include it in Spring container as bean via the #Component annotation and this bean will be with singleton scope
by default a bean is singleton scoped in Spring, and #PostConstruct is usually used for service beans and service beans must scoped prototype and here because you need multiple objects for that particular class, Spring will provide you singleton instance.
also by doing this spring will attempt multiple times to find this service bean and finally throws below exception:
java.lang.NoClassDefFoundError: Could not initialize class org.springframework.beans.factory.BeanCreationException
so try like this in annotation way:
package org.stalwartz.config;
import javax.annotation.PostConstruct;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("prototype") //you have to make it prototype explicitly
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Now every thing is good, and work fine for you.
add this dependency to pom.xml
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
I've been banging my head on the wall for the past couple of days trying to get this to work but I've not been able to.
I wrote a JPA EntityManagerProvider recently using EJB:
EntityManagerProviderBean.java
#Stateless
public class EntityManagerProviderBean {
#PersistenceContext(unitName="PU1")
private EntityManager entityManager1;
#PersistenceContext(unitName="PU2")
private EntityManager entityManager2;
public EntityManager getEntityManager() {
return (...) ? entityManager1: entityManager2;
}
}
And then of course I can inject the EJB wherever needed like this:
UserFacade.java
#Stateless
public class UserFacade {
#EJB
private EntityManagerProviderBean emProvider;
private EntityManager em = emProvider.getEntityManager();
...
}
Now I'm trying to do something similar using Spring, using annotations, and without doing anything in XML. I can't seem to figure out a way to inject the EntityManager. Anything that I do leads to a NullPointerException. For example, I tried to inject the EntityManager manually without relying on my EntityManagerProviderBean, like this:
UserFacadeSpring.java
public class UserFacadeSpring {
#PersistenceContext(unitName="PU1")
private EntityManager em;
...
}
But this gives me a NullPointerException. So the EntityManager is not being injected at all and I'm not sure what's wrong.
So two questions basically:
How can I inject the EntityManager using Spring?
How can I use my existing EntityManagerProviderBean EJB in Spring? What modifications do I need to make?
Any help in this matter will be greatly appreciated. As you can tell I'm a complete noob to Spring. I tried to read the guide but everything's flying over my head at the moment. I actually did try to do something half-baked but it didn't work either (I either get NullPointerException or BeanNotFoundException, I must have used every combination of #Component, #Bean, #Autowired annotations I think!):
EntityManagerProviderSpring.java
#Component
public class EntityManagerProviderSpring {
#PersistenceContext(unitName="PU1")
private EntityManager entityManager1;
#PersistenceContext(unitName="PU2")
private EntityManager entityManager2;
#Bean
public EntityManager getEntityManager() {
return (...) ? entityManager1: entityManager2;
}
}
Main.java
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(EntityManagerProviderSpring.class);
EntityManagerProviderSpring emProvider = context.getBean(EntityManagerProviderSpring.class);
EntityManager em = emProvider.getEntityManager();
...
}
Thanks!
EDIT: I'm using Hibernate with JPA and the following is my persistence.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>...</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/db1?zeroDateTimeBehavior=convertToNull"/>
<property name="javax.persistence.jdbc.user" value="pu1_username"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.password" value="pu1_pwd"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
</properties>
</persistence-unit>
<persistence-unit name="PU2" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
</persistence>
To have the exact same functionality on dependency injection level, it is impossible to map stateless ejb bean to a spring bean.
EJB #Singleton -> Spring #Component (or #Service or #Controller or #Repository)
EJB #Stateful -> Spring #Component#Scope("prototype") (or #Service#Scope("prototype") or #Controller#Scope("prototype") or #Repository#Scope("prototype"))
EJB #Stateless -> DOES NOT EXIST IN SPRING.
Stateless beans are very handy for this multiple persistence contexts... However, since Spring doesn't offer stateless bean. You have to use custom Spring setup to apply multiple persistence context. It won't work with only persistence.xml like EJB, unfortunately :(.
So to answer your questions:
Read Spring doc... Plenty of examples everywhere. Shortly, In Spring 4 Java Config you can use #EnableTransactionManagement, a transaction manager and an entity manager factory per persistence context.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/ejb.html. This is how you do it. Not a nice way since using jndi, but it is a solution...
If I want to declare a bean using Spring 3's Java-based configuration, I can do this:
#Configuration
public class MyConfiguration {
#Bean
public MyRepository myRepository() {
return new MyJpaRepository();
}
}
But, since I can't use the #Repository annotation in this context, how do get Spring to perform exception translation?
Declare your MyJpaRepository class as a repository:
#Repository
public class MyJpaRepository {
...
}
And make sure you have your annotations discoverable by setting up the component-scan element in your Spring configuration:
<context:component-scan base-package="org.example.repository"/>
Since you do not want your repository included in the annotation scan per your comments, filter it out either by excluding all #Repository annotations or your particular class(es) or package. There is an example of this in the documentation:
<context:component-scan base-package="org.example.repository">
<!-- use one or the other of these excludes, or both if you *really* want to -->
<context:exclude-filter type="regex" expression="*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
The Spring 3.0 documentation describes this configuration in more detail in section 3.10.
By configuring your class as a Repository, it will be designated as one when you pull it out as a Bean in your Configuration class.