I upgraded to Spring 3.1.2, Neo4J 1.8.RC1 and SpringData 2.1.0.RC3 and I run into the following issue.
Before explaining the problem, here is part of my application context:
<!-- Neo4J -->
<neo4j:config storeDirectory="target/graph.db" entityManagerFactory="entityManagerFactory"/>
<neo4j:repositories base-package="***.repository"/>
<!-- H2 -->
<jdbc:embedded-database type="H2" id="accountDataSource">
<jdbc:script location="classpath:scripts/schema.sql" separator=";"/>
</jdbc:embedded-database>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="accountDataSource" />
<property name="packagesToScan" value="***.domain" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="showSql" value="false" />
<property name="databasePlatform" value="${database.dialect}" />
</bean>
</property>
</bean>
<!-- transactions -->
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj" />
I run a very basic test :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:devinlove-core.xml")
#Transactional
public class MascotRepositoryTest {
#Autowired
private MascotRepository mascotRepository;
private Mascot mascot;
#Before
public void setup() {
mascot = new Mascot();
mascot.setName("Tux");
}
#Test
public void when_inserting_then_retrievable() {
assertThat(mascotRepository).isNotNull();
mascotRepository.save(mascot);
assertThat(mascot.getId()).isGreaterThan(0L);
}
}
And the test fails with a NPE. Why isn't mascot ID still null after the save operation?
MascotRepository is defined as follows:
public interface MascotRepository extends GraphRepository<Mascot> {}
Mascot itself is a very basic NodeEntity:
#NodeEntity
#TypeAlias("mascot")
public class Mascot {
#GraphId
private Long id;
#Indexed(indexType= FULLTEXT, indexName = "mascotSearch")
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
#Override
public String toString() {
return "Mascot{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
I don't know if this can be relevant to the issue, but one of my NodeEntity is partial and therefore is modified by AspectJ (to add basic ActiveRecord methods). Can you confirm only partial NodeEntities are modified this way and not pure Neo4J entities? If not, can it cause conflict with repositories?
If you have any ideas, thanks in advance!
Rolf
I think the right definition of the transactionManager bean is this:
<bean id="jpaTransactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="jtaTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<bean class="org.neo4j.kernel.impl.transaction.SpringTransactionManager">
<constructor-arg ref="graphDatabaseService" />
</bean>
</property>
<property name="userTransaction">
<bean class="org.neo4j.kernel.impl.transaction.UserTransactionImpl">
<constructor-arg ref="graphDatabaseService" />
</bean>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.data.neo4j.transaction.ChainedTransactionManager">
<constructor-arg>
<list>
<ref bean="jpaTransactionManager"/>
<ref bean="jtaTransactionManager"/>
</list>
</constructor-arg>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
Related
When I ran JUnit test on my service, it would successfully add the new entity to my database, but when I call the service from my Controller, it doesn't get saved to the database. What am I missing?
jpa.xml
<jpa:repositories base-package="com.mypackage.commons" />
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="database" value="MYSQL" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="jdbcDataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<!-- spring based scanning for entity classes -->
<property name="packagesToScan" value="com.mypackage" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />
<tx:annotation-driven />
UserService
#Service
public class RepositoryUserService implements UserService {
#Resource
private UserRepository userRepository;
#Transactional
#Override
public User create(User user) {
User newUser = userRepository.save(user);
return newUser;
}
}
MyController
#Controller
#Path("/users")
public class MyAPI {
#Autowired
private UserService userService;
#POST
#Path("/add")
#Consumes(MediaType.APPLICATION_JSON)
#Transactional
public Response addUser(User newUser) {
userService.create(newUser);
}
}
Set the entityManager for Transaction :
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
and the annotation driven this way :
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
try to remove #Transactional from your controller, define Propagation and flush the session
#Transactional(propagation= Propagation.REQUIRED)
#Override
public User create(User user) {
try{
return userRepository.saveAndFlush(user);
} catch(DataIntegrityViolationException e){
// handle the exception in here to find out what is wrong
}
}
I have two spring profiles: development and production.
There should be a common properties file (placed in classpath:properties/common/*.properties) which will be overridden by the profile's properties file (placed in classpath:properties/development/*.properties.
This is my context config snippet to clarify my intention:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.example.entities.*" />
<property name="hibernateProperties" ref="hibernateProperties" />
</bean>
<beans profile="development">
<context:property-placeholder location="classpath:properties/development/jdbc.properties" />
<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true" />
<property name="location" value="classpath:properties/development/hibernate.properties" />
</bean>
</beans>
<beans profile="production">
<context:property-placeholder location="classpath:properties/production/jdbc.properties" />
<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true" />
<property name="location" value="classpath:properties/production/hibernate.properties" />
</bean>
</beans>
Currently there's no common properties somewhere. How to merge a common properties file with the one in each profile for both jdbc.properties and hibernate.properties?
I've used JavaConfig for this:
#Configuration
#Profile("development")
public class DevelopmentConfig {
public #Bean String profile() {
return "development";
}
}
#Configuration
#Profile("production")
public class ProductionConfig {
public #Bean String profile() {
return "production";
}
}
public class PropertyUtils {
public static Properties getProperties(String profile, String filename) throws IOException {
Properties ret = new Properties();
ClassPathResource resource;
resource = new ClassPathResource("properties/common/" + filename + ".properties");
ret.putAll(PropertiesLoaderUtils.loadProperties(resource));
resource = new ClassPathResource("properties/" + profile + "/" + filename + ".properties");
if (resource.exists()) {
ret.putAll(PropertiesLoaderUtils.loadProperties(resource));
}
return ret;
}
}
#Configuration
public class MainConfig {
private #Autowired String profile;
// Here you can use: PropertyUtils.getProperties(profile, "jdbc"))
}
This should get you started
<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*:default.properties</value>
<value>classpath*:overrides.properties</value>
<value>file:${APP_HOME}/**/*.properties</value>
</list>
</property>
</bean>
I am new to Spring but in general I am aware of it's features so I decided to use it in one of my projects. Main problem however is with Hibernate. Before this idea of introducing spring the premise is this:
My application (not web) had to connect to a DB and gather information from it using "persistence_1.xml" with it's own set of entity classes. In other words everything related to "persistence_1.xml" was read only so that no tragedies would occur. Also "persistence_1.xml" with persistence-unit of name "p1" came from web-app dependencies. So picture is this: my app (not-web) written with the support of maven, took dependencies of the other application to access database and gather information.
And the other "persistence_2.xml" with persistence-unit name of "p2" and it's own subset of entities was created by me to store gathered and processed information into the same database.
So originally I had two entity managers one responsible for "p1" another for "p2".
I have seen some material on the internet where they show how to configure two entity managers with different dataSources but I can not figure out how to create two entity managers in SPRING using their own set of ENTITIES.
Let's say "test" is only associated with "UserEntity" and "dummy" is only associted with "DumbEntity".
Now everything get's mashed up along the way and no matter which PU name I type in in #PersistenceContext(name = "test") - it can query for any entity in database.
This is example of persistence.xml:
<persistence-unit name="test" type="RESOURCE_LOCAL">
<class>org.test.db.UserEntity</class>
</persistence-unit>
<persistence-unit name="dummy" type="RESOURCE_LOCAL">
<class>org.test.db.DumbEntity</class>
</persistence-unit>
Bean definition:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="test" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
</bean>
</property>
</bean>
<bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="dummy" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
</bean>
</property>
</bean>
<tx:annotation-driven/>
<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>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean name="userDao" class="org.test.services.UserDaoImpl" />
My UserDaro service
public class UserDaoImpl implements UserDao {
#PersistenceContext(unitName = "test")
private EntityManager entityManager;
public UserDaoImpl() {
}
public UserDaoImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public void saveUser(UserEntity user) {
entityManager.persist(user);
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public void saveUser(DumbEntity user) {
entityManager.persist(user);
}
#Override
public List<UserEntity> fetchAllUsers() {
String sql = "FROM UserEntity";
Query query = entityManager.createQuery(sql);
return query.getResultList();
}
#Override
public List<DumbEntity> fetchAllUsers2() {
String sql = "FROM DumbEntity";
Query query = entityManager.createQuery(sql);
return query.getResultList();
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}
In any way whether or not I use fetchAllUsers() or fetchAllUsers2() I get the result, but I would like that each of these would only work with entityManager that only has the knowledge about about specific entities.
I would like you to share your thoughts on this one. Thank You.
i need to use bean from spring application context not in a spring managed bean, so i do next: annotate bean with #Service annotation, so instance of bean created during spring loading.
<bean id="customRevisionListener" class="ru.csbi.registry.services.impl.envers.CustomRevisionListener" />
This instance is ApplicationContextAware, so application context is injected in this bean instance and i save it to static variable:
#Service
public class CustomRevisionListener implements EntityTrackingRevisionListener, ApplicationContextAware {
private static ApplicationContext applicationContext;
private ModelInformationService modelInformationService;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CustomRevisionListener.applicationContext = applicationContext;
}
private ModelInformationService getModelInformationService() {
if (modelInformationService == null) {
modelInformationService = applicationContext.getBean(ModelInformationService.class);
}
// TransactionProxyFactoryBean
return modelInformationService;
}
After that another instance of CustomRevisionListener created in not spring context(hibernate envers context). Here i use static variable to receive spring applicationContext
after that i'm getting beans from application context:
private ModelInformationService getModelInformationService() {
if (modelInformationService == null) {
modelInformationService = applicationContext.getBean(ModelInformationService.class);
}
the problem is that this bean has all #Autowired properties injected correctly:
#Service
public class ModelInformationServiceImpl implements ModelInformationService {
#Autowired
private EntityChangeService entityChangeService; // injected correctly
#Autowired
private PropertyService propertyService; // injected correctly
#Autowired
private ru.csbi.registry.services.reflection.HibernateDomainService hibernateService; // injected correctly
, but they are simple instances of java classes not Proxies supporting #Transactional annotation, which they are for my regular spring code:
getModelInformationService().getClass().getName() is "ru.csbi.registry.services.impl.envers.ModelInformationServiceImpl"
and must be something like
$Proxy71
How to get transaction supporting proxies, which spring genereates for example when injecting beans in #Controller, in bean not managed by spring?
i'm using next spring config:
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="lazyConnectionDataSourceProxy"/>
</bean>
<bean id="lazyConnectionDataSourceProxy" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource">
<ref local="dataSourceTarget" />
</property>
</bean>
<bean id="dataSourceTarget" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${ds.driver}" />
<property name="url" value="${ds.url}" />
<property name="username" value="${ds.user}" />
<property name="password" value="${ds.password}" />
<property name="initialSize" value="${ds.initialSize}" />
<property name="maxActive" value="${ds.maxActive}" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<!--property name="entityInterceptor">
<bean class="ru.csbi.registry.utils.audit.AuditLogInterceptor">
<property name="sessionFactory" ref="auditSessionFactory" />
</bean>
</property-->
<property name="dataSource" ref="dataSource" />
<property name="lobHandler" ref="oracleLobHandler" />
<property name="packagesToScan" value="ru.csbi.registry.domain" />
<property name="hibernateProperties">
<bean id="hibernatePropertiesFactoryBean" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>file:${realtyregistry.settings.path}/hibernate-config.properties</value>
</list>
</property>
</bean>
</property>
<property name="eventListeners">
<map>
<entry key="post-insert" value-ref="auditEventListener" />
<entry key="post-update" value-ref="auditEventListener" />
<entry key="post-delete" value-ref="auditEventListener" />
<entry key="pre-collection-update" value-ref="auditEventListener" />
<entry key="pre-collection-remove" value-ref="auditEventListener" />
<entry key="post-collection-recreate" value-ref="auditEventListener" />
</map>
</property>
</bean>
<bean id="auditEventListener" class="org.hibernate.envers.event.AuditEventListener" />
<bean id="persistenceManagerHibernate" class="ru.csbi.registry.utils.PersistenceManagerHibernate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I'm using Spring 3 and UserServiceImpl. The service does not see the Dao bean.
This is my UserDao:
#Repository
public class UserDaoImpl implements UserDao {
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private Session currentSession() {
return sessionFactory.getCurrentSession();
}
}
Then the Service bean :
public class UserServiceImpl implements UserService {
#Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public String testeoDAO() {
return userDao.funciona();
}
}
The testeoDAO method had writeen only for testing the DAO.The applicationContext
<context:annotation-config/>
<bean id="userDao" class="com.apress.usermanager.dao.hibernate.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="userService" class="com.apress.usermanager.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
autowire="byName">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.apress.usermanager"/>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.query.substitutions=true 'Y', false 'N'
hibernate.cache.use_second_level_cache=true
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
hibernate.hbm2ddl.auto=update
hibernate.use_sql_comments=true
hibernate.show_sql=true
hibernate.current_session_context_class=thread
</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="100"/>
<property name="maxWait" value="1000"/>
<property name="poolPreparedStatements" value="true"/>
<property name="defaultAutoCommit" value="true"/>
<property name="testOnBorrow" value="true"/>
<property name="validationQuery" value="select 1=1"/>
</bean>
<bean id="staticDataImporter" class="com.apress.usermanager.support.StaticDataImporter" depends-on="sessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="staticData" value="classpath:default-data.xml"/>
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
And the error :
java.lang.NullPointerException
at com.apress.usermanager.service.UserServiceImpl.testeoDAO(UserServiceImpl.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
I think you need to add
<context:component-scan base-package="com.a,com.b">
</context:component-scan>
To scan package com.a and com.b. This allows the classpath scan and add all #Component and so forth to it.
Annote UserServiceImpl with #Service.
Regards.