I have a Maven project with a Hibernate JPA persistence layer that I would like to incorporate into some Spring applications (one a web application, and one a command line tool), but I am unsure how to configure this. The persistence module includes model classes, DAO classes, and the persistence.xml file. I have configured Spring's EntityManagerFactory to use the existing persistence.xml, but instantiating the DAO classes always results in a NullPointerException, as they seem to be unable to locate the persistence unit by name. Is it possible to use Hibernate JPA classes created outside Spring's context in a Spring application?
persistence.xml
<persistence-unit name="myapp" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.archive.autodetection" value="class, hbm" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
application-context.xml
<bean id="allProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="singleton" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:config/db.properties</value>
<value>classpath:config/log4j.properties</value>
</list>
</property>
</bean>
<context:component-scan base-package="com.company.app" />
<context:annotation-config />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="persistenceUnitName" value="myapp"/>
<property name="dataSource" ref="datasource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL" />
<property name="databasePlatform" value="${hibernate.dialect}" />
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="datasource" />
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
</bean>
UserDao.java
public class UserDao extends AbstractDao<User> {
public UserDao() {
super("myapp");
}
// CRUD methods
AbstractDao.java
public abstract class AbstractDao<T extends DataObject> {
private EntityManagerFactory emf;
private final static Logger log = LoggerFactory.getLogger(AbstractDao.class);
private final Map<String, Map<String, String>> connectionDictionaries =
new HashMap<String, Map<String, String>>();
public AbstractDao(String dataSourceName) {
try {
setUpDataSource(dataSourceName);
} catch (IOException e) {
log.error("Unable to load: ", AbstractDataConstants.DB_PROPERTIES_FILE);
e.printStackTrace();
}
}
...
// Other methods
Related
I have a problem with my spring boot configuration in xml I created this configuration :
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="false"/>
<property name="database" value="ORACLE"/>
</bean>
<bean id="dataSource" primary="true" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#localhost:20300:test"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="entityManagerFactory" primary="true" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<!-- spring based scanning for entity classes-->
<property name="packagesToScan" value="model.entity"/>
<property name="persistenceUnitName" value="msPersistenceUnit" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="controllerService"
class="...impl.ControllerServiceImpl">
<property name="entityManager" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" >
<constructor-arg index="0" ref="entityManagerFactory" />
</bean>
And I had the java code like this :
public void setEntityManager(final HibernateEntityManagerFactory entityManager) {
final RepositoryFactorySupport factorySupport = new JpaRepositoryFactory(entityManager.createEntityManager());
controlRepository = factorySupport.getRepository(ObjControlRepository.class);
}
when i'm using find method it's ok, but when I'm doing a save, there are not exception but the value it's not insert.
Thank your for your help.
[Edit]
To save I'm using :
/**
* The Interface ObjControlRepository.
*/
public interface ObjControlRepository extends CrudRepository<ObjControl, String> {
}
And I'm calling the method like that :
controlRepository.save(newValue);
You should try to explicitly set the hibernate.hbm2ddl.auto variable to auto. This will prevent your application to release previous data when you start your application.
You can learn more about the hibernate.hbm2ddl.auto variable here.
You should use transactions and commit the transaction in order to save an entity.
So Use #Transactionl annotation on your service layer, like this:
#Transactional
public class ControllerServiceImpl {
...
}
As ControllerServiceImpl is already declared as a bean in spring configuration file, Spring will take care about committing the transaction once you save an entity.
I am trying to deploy my Spring boot project on external Tomcat server. I followed all given advices here http://docs.spring.io/spring-boot/docs/1.1.4.RELEASE/reference/htmlsingle/#build-tool-plugins-maven-packaging, however i am a bit confused as i deployed it on the server and everything is ok, but i am getting 404 error every time i access any of pages. Also, i add to application.properties
server.context-path=/bqp
so i use url's like this http://128.0.169.5:8082/bqp/
UPDATED:
my spring-config.xml:
<context:component-scan base-package="com.bionic" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="persistenceUnitName" value="com.bionic.quizzes" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="HSQL" />
<property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/quizzes" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
Also, I could easy deploy it by main method of Spring boot. But I should deploy it on remote server.
Application.class:
#SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Try put this code on your servlet.xml
<bean class=
"org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/bqp/"/>
<property name="suffix" value=".jsp"/> //if you are using .jsp
</bean>
I am having an issue creating xa datasources in spring 4.0.
I have setup my datasources in weblogic using an xa driver.
I then added added jndi-lookups for the data sources in spring:
<jee:jndi-lookup id="dataSourceOne" jndi-name="/jdbc/XAONE" resource-ref="true" />
<jee:jndi-lookup id="dataSourceTwo" jndi-name="/jdbc/XATWO" resource-ref="true" />
I have then created the configuration for the entity managers:
<bean id="emfone" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceOne" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="packagesToScan" value="..." />
</bean>
<bean id="emftwo"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceTwo" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="packagesToScan" value="..." />
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
</bean>
After this I have my transaction manager configured:
<tx:annotation-driven />
<tx:jta-transaction-manager />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emfone" />
<qualifier value="tmOne"/>
</bean>
<bean id="docTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emftwo" />
<qualifier value="tmTwo"/>
</bean>
I have a service class method annotated with #Transactional that calls a DAO. The DAO uses both entity managers for persisting data.
When the DAO tries to persist using the emftwo a no transaction in progress error is thrown. Does anybody know where I am going wrong?
Thanks,
I have a Spring application, i want to change the data source dynamically,ie. when input a DS URL, the Spring beans and all dependency will get updated automatically.I know this is somewhat strange, but anyway i want to achieve that.
My Spring configuration as following:
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.serverName}" />
<property name="portNumber" value="${jdbc.portNumber}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="databaseName" value="${jdbc.databaseName}" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="majorDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="majorDataSource"/>
<property name="configLocation" value="classpath:sqlmap-config.xml"/>
</bean>
The questions are:
The JDBC URL is stored in properties, which could be changed runtime.
Once the URL is changed, i need to re-create the data source and maybe the dependent objects. I could not figure out how to do it elegantly in Spring?
I have known that Spring did could dynamically route data source based on one key, but the data source URL is predefined in Spring and will not change runtime. It is not my case.
You can use spring's AbstractRoutingDataSource by extending it and overriding the method determineCurrentLookupKey() that should return the key referencing the datasource's spring bean to be used.
Take a look at this blog article on spring source's blog which will show you an example of how to use that feature.
Basically to answer your questions, what you will need to do is to define the two datasources as different spring bean in your XML config. There is no need to create one dynamically, spring will load both, and use one or the other dynamically depending on your criteria in the determineCurrentLookupKey() method.
This would lead to something like:
XML config
<!-- first data source -->
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.major.serverName}" />
<property name="portNumber" value="${jdbc.major.portNumber}" />
<property name="user" value="${jdbc.major.username}" />
<property name="password" value="${jdbc.major.password}" />
<property name="databaseName" value="${jdbc.major.databaseName}" />
</bean>
<!-- second data source -->
<bean id="minorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.minor.serverName}" />
<property name="portNumber" value="${jdbc.minor.portNumber}" />
<property name="user" value="${jdbc.minor.username}" />
<property name="password" value="${jdbc.minor.password}" />
<property name="databaseName" value="${jdbc.minor.databaseName}" />
</bean>
<!-- facade data source -->
<bean id="dataSource" class="blog.datasource.CustomerRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="MINOR" value-ref="minorDataSource"/>
<entry key="MAJOR" value-ref="majorDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="majorDataSource"/>
</bean>
<!-- wiring up -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:sqlmap-config.xml"/>
</bean>
Java
public class MyRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
// get the current url
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if (request.getRequestURL().toString().endsWith("/minor"))
return "MINOR";
else
return "MAJOR";
}
}
I am not sure if this is the correct Way of doing this but what you can do is something like this.
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="dummydata" />
<property name="portNumber" value="dummydata" />
<property name="user" value="dummydata" />
<property name="password" value="dummydata" />
<property name="databaseName" value="dummydata" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="majorDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
and in Java Class
public class TestTransaction {
#Autowired
private DataSourceTransactionManager manager;
private PlatformTransactionManager transactionManager;
public testExecution(DataSource ds) {
manager.setDataSource(ds);
transactionManager = manager;
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
jdbcTemplate.update();
transactionManager.commit(status);
} catch (Exception ex) {
transactionManager.rollback(status);
}
}
}
Please suggest if this Approach could Work as i am Still new to Spring
I have two entity managers in my applicationContext.xml which corresponds to two different databases. I can easily query database1 with entityManager1, but when I try to access database2 with entityManager2, I am not getting any results. I am using Spring+Hibernate+JPA.
Here is my ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans default-autowire="byName"
xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="INFORMIX" />
<property name="showSql" value="true" />
</bean>
</property>
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
<property name="persistenceUnitName" value="PU1" />
</bean>
<bean id="entityManagerFactory2"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource2" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="INFORMIX" />
<property name="showSql" value="true" />
</bean>
</property>
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
<property name="persistenceUnitName" value="PU2" />
</bean>
<!-- Data Sources -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
<property name="url"
value="jdbc:db2://HOST_NAME:PORT_NO/DB_NAME:INFORMIXSERVER=SERVER_NAME;DELIMIDENT=y;" />
<property name="username" value="username" />
<property name="password" value="password" />
<property name="minIdle" value="2" />
</bean>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
<property name="url"
value="jdbc:db2://HOST_NAME:PORT_NO/DB_NAME2:INFORMIXSERVER=SERVER_NAME;DELIMIDENT=y;" />
<property name="username" value="username" />
<property name="password" value="password" />
<property name="minIdle" value="2" />
</bean>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
lazy-init="false">
<property name="targetObject" ref="dataSource" />
<property name="targetMethod" value="addConnectionProperty" />
<property name="arguments">
<list>
<value>characterEncoding</value>
<value>UTF-8</value>
</list>
</property>
</bean>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
lazy-init="false">
<property name="targetObject" ref="dataSource2" />
<property name="targetMethod" value="addConnectionProperty" />
<property name="arguments">
<list>
<value>characterEncoding</value>
<value>UTF-8</value>
</list>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
scope="prototype">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="persistenceUnitManager"
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence.xml</value>
<value>classpath*:META-INF/persistence2.xml</value>
</list>
</property>
<property name="dataSources">
<map>
<entry key="localDataSource" value-ref="dataSource" />
<entry key="dataSource2" value-ref="dataSource2" />
</map>
</property>
<property name="defaultDataSource" ref="dataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory2" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:annotation-driven transaction-manager="transactionManager2" />
<!-- MORE Action and DAO beans -->
</beans>
This is my service layer code which works fine with enityManager1:
#Transactional
public class StatesDAO implements IStatesDAO {
private EntityManager em;
#PersistenceContext(unitName = "PU1")
public void setEntityManager(EntityManager em) {
this.em = em;
}
private EntityManager getEntityManager() {
return em;
}
#SuppressWarnings("unchecked")
public List<States> findAll() {
logger.info("finding all States instances");
try {
final String queryString = "select model from States model";
Query query = getEntityManager().createQuery(queryString);
return query.getResultList();
} catch (RuntimeException re) {
throw re;
}
}
}
My two persitence.xml files look like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
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_1_0.xsd">
<persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.jpa.entity.States</class>
</persistence-unit>
</persistence>
and
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
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_1_0.xsd">
<persistence-unit name="PU2" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.jpa.other.entity.States</class>
</persistence-unit>
</persistence>
If I change my service layer (as shown below), I get no results. Basically the size of the list is zero:
#Transactional
public class StatesDAO implements IStatesDAO {
private EntityManager em;
#PersistenceContext(unitName = "PU2")
public void setEntityManager(EntityManager em) {
this.em = em;
}
private EntityManager getEntityManager() {
return em;
}
#SuppressWarnings("unchecked")
public List<com.jpa.other.entity.States> findAll() {
logger.info("finding all States instances");
try {
final String queryString = "select model from States model";
Query query = getEntityManager().createQuery(queryString);
return query.getResultList();
} catch (RuntimeException re) {
throw re;
}
}
}
So basically you can see is that I have two entities(States) with exactly same structure and in order to differentiate from each other I have put them into separate packages
According to my knowledge I am not doing anything crazy here but still it doesn't seem to be working. How is this problem caused and how can I solve this?
Follow-up: One thing I forgot to mention is that even though there are two different databases but the database server name is same. I don't know if this could be a useful information.So thought of sharing it.
This is the exception I am getting now:
16:24:44,732 INFO [STDOUT] Hibernate: select state0_.state as col_0_0_ from states state0_
16:24:44,753 WARN [JDBCExceptionReporter] SQL Warning: 36106, SQLState: 01I01
16:24:44,753 WARN [JDBCExceptionReporter] IDS SQL Warning: SQLCODE=36106, SQLSTATE=01I01, SQLERRMC=0;819;informix;;IDS/NT32;1;1;0;819;0;, DRIVER=4.7.85
I've hit the same exact issue, but with multiple Hibernate session factories: 2 DBs with the same structure, I didn't want to have 2 identical sets of DAOs, etc. While my experience was with Hibernate, I suspect you could use the same solution: Spring's AbstractRoutingDataSource. It allows you to configure your app to determine at runtime which data source to use, based on a value set on the ThreadLocal. See http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/ for an introduction. What ends up happening is that the dataSource ref in your factory will point not at a hard-coded dataSource bean, but at the AbstractRoutingDataSource. To set the toggle per-thread, use an #Aspect to determine which DB to hit.