I'm attempting to run some tests on my Spring web app and it seems I'm having some issues initially inserting records in a DB to use my tests with. My setup is as follows:
Unit Test Setup which extends TestBase:
#Before
public void setup() {
setupBase();
configurationDao = (ConfigurationDao)ctx.getBean("configDao");
setupReasons(configurationDao.getEm());
}
TestBase:
public class TestBase {
protected ApplicationContext ctx;
public void setupBase() {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
public static String getRandomUUIDStyleId() {
return UUID.randomUUID().toString().substring(0, 24);
}
#Transactional
public void setupReasons(EntityManager em) {
DateTime now = new DateTime();
for (Reason reason : Reasons.values()) {
ReasonEntity r = new ReasonEntity();
r.setId(reason.ordinal());
r.setReason(reason.name());
em.persist(r);
}
}
}
BaseDao which Configuration extends:
public abstract class BaseDao<T extends EntityBase> implements Dao<T> {
#PersistenceContext
protected EntityManager em;
protected Class<T> entityClass;
#Transactional
public T findById(long id) {
return em.find(entityClass, id);
}
#Transactional
public List<T> findAll() {
Query query = em.createQuery("from " + entityClass.getName());
return (List<T>) query.getResultList();
}
#Transactional
public T persistOrMerge(T entity) {
if (entity.getId() == null || entity.getId() == 0) {
//System.out.println("Persisting: "+entity.toString());
em.persist(entity);
} else {
if (!em.contains(entity)) {
//System.out.println("Merging: "+entity.toString());
return em.merge(entity);
}
}
return entity;
}
public void remove(T entity) {
em.remove(entity);
}
public EntityManager getEm() {
return em;
}
}
applicationContext.xml:
<bean id="configDao" class="ConfigurationDao" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" >
<property name="persistenceUnitName" value="TEST-pu" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
While setupReasons "appears" to be persisting it never inserts the data into the MySql table (seen through hibernate showsql property) and I can't seem to understand why. Thanks in advance for your help and if you need anything else from me I'm more than happy to provide.
Despite the fact that setupReasons() is marked #Transactional, I don't see anything to make me think a transaction is being started for it. Without a transaction, nothing will be written. You might want to read up on the Spring test framework.
Follow this tutorial to make your test classes transaction aware using spring:
http://static.springsource.org/spring/docs/2.5.x/reference/testing.html
Use p6spy to inspect what is exactly being sent to the database, including start and commit transaction statements.
p6spy | http://www.mkyong.com/hibernate/how-to-display-hibernate-sql-parameter-values-solution/
For someone who still is facing this issue, just put the annotation #TransactionConfiguration in your class and that's it!
Check it out on #TransactionConfiguration
Related
I read the spring-doc#orm-exception-translation, and add 2 beans as it says.
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean class="org.mybatis.spring.MyBatisExceptionTranslator">
<constructor-arg type="javax.sql.DataSource" ref="dataSource"/>
<constructor-arg type="boolean" value="false"/>
</bean>
Also, my dao is annotated by #Repository.
#Repository
public class AuthorDaoImpl implements AuthorDao {
private SqlSessionTemplate template;
private MultiMapper mapper;
#Autowired
public AuthorDaoImpl(SqlSessionTemplate template, MultiMapper mapper) {
this.template = template;
this.mapper = mapper;
}
#Override
public void add(Author author) {
mapper.insertAuthor(author);
}
#Override
public Author getById(int id) {
return mapper.selectAuthorById(id);
}
#Override
public Author getByName(String username) {
return mapper.selectAuthorByName(username);
}
#Override
public void update(Author author) {
mapper.updateAuthor(author);
}
#Override
public void delete(Author author) {
mapper.deleteAuthor(author);
}
}
But when I tried to add the same data, what I get is still mysql exception. Could someone give me some ideas? Thank you very much!
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'obama' for key 'username'
When I throw exception in service method I expected that transactional annotation on service will rollback save operation, but it is not working.
This is my service:
#Service
#Transactional(value = "transactionManager", rollbackFor = Exception.class)
public class OrderServiceImp implements OrderService {
#Autowired
private OrderRepository orderRepository;
#Override
public void doSomeStaff(Long orderId) {
Order order = orderRepository.findOne(orderId);
orderRepository.save(order);
throw new NullPointerException("Test transaction exeption");
}
}
In data.xml I have next configs:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<jpa:repositories base-package="com.dmitro.repositories" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/>
In dispatcher-servlet.xml I declared scan:
<context:component-scan base-package="com.dmitro.service" />
I am using spring-data-jpa 1.8.0.RELEASE. Please help!
#Transactional(value = "transactionManagerForServiceLayer", rollbackFor = Exception.class)
This is the culprit. You should not have different transaction manager for service and repository. To fix it, you need to replace transactionManagerForServiceLayer here to transactionManager and then rollback will work.
Try to throw the Exception in OrderRepositoryImpl to see if it works
public class OrderRepositoryImpl implements OrderRepository {
#Override
public void save() {
throw new SomeRunTimeException();
}
}
public class OrderServiceImp implements OrderService {
#Override
public void doSomeStaff(Long orderId) {
Order order = orderRepository.findOne(orderId);
orderRepository.save(order);
}
}
The DataSource AutoCommit is ON (or True). Turn it OFF (or false).
Problem was in configuration, because I declared services and transaction manager in different spring contexts: transaction manager was in root context and services was in child dispatcher-servlet.xml context.
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 years ago.
I currently developing an application using Spring Data Neo4j. This is the first time I use Spring framework.
However, the autowired annotation is not working, I always get null. Here is the codes:
public class CreateDiaryTransaction extends Transaction {
#Autowired
DirayRepository repository;
#Override
public Object perform(Map<String, Object> parameters) {
// TODO Auto-generated method stub
Diary diary = (Diary) DiaryFactory.getInstance().create(parameters);
repository.save(diary);
return diary.toJsonRepre();
}
}
Then I test it in a unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/applicationContext.xml" })
public class CreateDiaryTransactionTest {
#Test
public void test() {
Transaction transaction = new CreateDiaryTransaction();
Map<String, Object> parameters = new HashMap<>();
parameters.put("title", "Test");
parameters.put("weather", "sun");
parameters.put("mood", "happy");
parameters.put("html", "link");
parameters.put("images", new ArrayList<Image>());
try {
transaction.execute(parameters);
} catch (Exception e) {
fail("Exception occur");
}
}
}
When I runs this, I got null object exception. However, when I place the repository directly in the unit test, it works fine. Here is another test that works:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/applicationContext.xml" })
public class DiaryRepositoryTest {
#Autowired
DirayRepository repository;
#Test
#Transactional
public void testSaveDiary() {
Diary diary = (Diary) DiaryFactory.getInstance().create(
new HashMap<String, Object>());
repository.save(diary);
Diary retrivedDiary = repository.findOne(diary.getGraphId());
assertEquals(diary, retrivedDiary);
}
}
And here is my application context:
<context:annotation-config />
<neo4j:config storeDirectory="target/data/pamela"
base-package="com.bond.pamela.domain" />
<neo4j:repositories base-package="com.bond.pamela.persistence" />
<tx:annotation-driven mode="aspectj" />
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean
class="com.bond.pamela.domain.valueObject.converter.ImageToJsonConverter" />
<bean
class="com.bond.pamela.domain.valueObject.converter.JsonToImageConverter" />
</set>
</property>
</bean>
I guess I somehow put the repository outside the spring context, but how can I get it wirte? Thx
You need following in your application context
<context:component-scan base-package="packagename.package" />
Here package name is package in which you have all your classes with annotations.
Have a look at this post for more :
Spring MVC: Difference between context:annotation-config vs context:component-scan
Make sure you have written #Repository or #Component annotation on the implementation of DirayRepository and you have written component-scan in
applicationContext.xml
sample code for DirayRepository
#Repository
public class DirayRepositoryImpl implements DirayRepository {
void save() { ...}
}
I am using xml configuration in my Spring Application. Now i would like to convert the existing classes to use annotation(like #service, #Repository etc) instead of xml configuration.
Business Logic(Irrelevant for this question, just for understanding):
Sevice connnects to Americas database and find the skus(products) and deactivates the skus.
Sevice connnects to EMEA database and find the skus(products) and deactivates the skus.
Here is the sample code.
/* Service code, which has 2 instances of SkuDAO, one connecting to US database and one connecting to EMEA database */
public class DeactivationService {
private static final Logger LOG = Logger.getLogger(DeactivationService.class);
private SkuDAO amerdao; //Dependency Injection Amer
private SkuDAO emeadao; //Dependency Injection EMEA
public DeactivationService(SkuDAO amerdao,SkuDAO emeadao) {
this.amerdao=amerdao;
this.emeadao=emeadao;
}
/*
* Step 1: find inactive sku in americas skudao1.find()
* Step 2: find inactive sku in emea skudao2.find()
* Step 3: deactivate sku in americas
* Step 4: deactivate sku in emea
*/
public void deactivateSku() {
List<Sku> totalList = new ArrayList<Sku>();
List<Sku> amerList = amerdao.find();
List<Sku> emeaList = emeadao.find();
amerdao.deactivate(amerList);
emeaList.deactivate(emeaList);
}
}
/* DAO interface */
public interface SkuDAO {
public List<Sku> find();
public void deactivate(List<Sku>);
}
/* DAO Implementation
Here one constructor in which DataSource is injected
*/
public class SkuDAOImpl implements SkuDAO {
private DataSource datasource; //Dependency injection
private JdbcTemplate jdbcTemplate;
public SkuDAOImpl(DataSource datasource) {
this.datasource=datasource;
}
public List<Sku> find() {
//some processing to find the sku, purposely left empty as it is a sample code
}
public void deactivate(List<Sku>) {
//some processing to deactivate the sku, purposely left empty as it is a sample code
}
}
Spring Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd">
<context:property-placeholder location="file:${dbconfiguration}"/>
<bean id="AmericasDataSource" class="dell.harmony.data.HarmonyBasicDataSource" destroy-method="close" >
<property name="url"><value>${HarmonyAmericasDb.url}</value></property>
<property name="driverClassName"><value>${HarmonyAmericasDb.driverClassName}</value></property>
<property name="username"><value>${HarmonyAmericasDb.username}</value></property>
<property name="password"><value>${HarmonyAmericasDb.password}</value></property>
<property name="initialSize"><value>${HarmonyAmericasDb.initialSize}</value></property>
<property name="maxActive"><value>${HarmonyAmericasDb.maxActive}</value></property>
<property name="maxWait"><value>${HarmonyAmericasDb.maxWait}</value></property>
<property name="maxIdle"><value>${HarmonyAmericasDb.maxIdle}</value></property>
<property name="minIdle"><value>${HarmonyAmericasDb.minIdle}</value></property>
<property name="removeAbandoned"><value>${HarmonyAmericasDb.removeAbandoned}</value></property>
<property name="removeAbandonedTimeout"><value>${HarmonyAmericasDb.removeAbandonedTimeout}</value></property>
</bean>
<bean id="EMEADataSource" class="dell.harmony.data.HarmonyBasicDataSource" destroy-method="close" >
<property name="url"><value>${HarmonyEMEADb.url}</value></property>
<property name="driverClassName"><value>${HarmonyEMEADb.driverClassName}</value></property>
<property name="username"><value>${HarmonyEMEADb.username}</value></property>
<property name="password"><value>${HarmonyEMEADb.password}</value></property>
<property name="initialSize"><value>${HarmonyEMEADb.initialSize}</value></property>
<property name="maxActive"><value>${HarmonyEMEADb.maxActive}</value></property>
<property name="maxWait"><value>${HarmonyEMEADb.maxWait}</value></property>
<property name="maxIdle"><value>${HarmonyEMEADb.maxIdle}</value></property>
<property name="minIdle"><value>${HarmonyEMEADb.minIdle}</value></property>
<property name="removeAbandoned"><value>${HarmonyEMEADb.removeAbandoned}</value></property>
<property name="removeAbandonedTimeout"><value>${HarmonyEMEADb.removeAbandonedTimeout}</value></property>
</bean>
**<!-- Sku Deactivation -->**
<bean id="SkuAmerDao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="AmericasDataSource"/></constructor-arg>
</bean>
<bean id="SkuEMEADao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="EMEADataSource"/></constructor-arg>
</bean>
<bean id="ServiceManager" class="dell.harmony.service.skudeactivation.service.DeactivationService">
<constructor-arg index="0"><ref bean="SkuAmerDao"/></constructor-arg>
<constructor-arg index="1"><ref bean="SkuEMEADao"/></constructor-arg>
</bean>
</beans>
Now i want to convert the above classes to highlighted inside xml("Sku Deactivation") , into annodation.
My code for convertion is as follows:
#Service
public class DeactivationService {
private static final Logger LOG = Logger.getLogger(DeactivationService.class);
private SkuDAO amerdao; //Dependency Injection Amer
private SkuDAO emeadao; //Dependency Injection EMEA
#Autowired(required=true)
public DeactivationService( #Qualifier("SkuAmerDao") SkuDAO amerdao, #Qualifier("SkuEMEADao") SkuDAO emeadao) {
this.amerdao=amerdao;
this.emeadao=emeadao;
}
}
In the above constructor, now 'amerdao' instance, should be injected with AmericasDataSource and 'emeadao' with EMEADataSource, how to do that?
Please note, i dont have a setter in the SkuDAOImpl. Also there is only one datasource instance inside the SkuDAOImpl.
can you given sample code of SkuDAOImpl with annodation.
Any suggestion, to improve the coding from service to dao , if it can be done in a better way. (Not required to answer this)
EDITED NOW: just to be clear with question 1, I would like to remove the below two lines in Spring xml and use annotation instead my DeactivationService. Is it possible?
<bean id="SkuAmerDao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="AmericasDataSource"/></constructor-arg>
</bean>
<bean id="SkuEMEADao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="EMEADataSource"/></constructor-arg>
</bean>
What about:
#Service
public class DeactivationService {
private static final Logger LOG = Logger.getLogger(DeactivationService.class);
#Autowired
#Qualifier("SkuAmerDao")
private SkuDAO amerdao; //Dependency Injection Amer
#Autowired
#Qualifier("SkuEMEADao")
private SkuDAO emeadao; //Dependency Injection EMEA
// no constructor needed.
}
public abstract class BaseDao implements SkuDAO {
private final JdbcTemplate jdbcTemplate;
protected BaseDao() {
this.jdbcTemplate = new JdbcTemplate(getDataSource());
}
protected abstract DataSource getDataSource();
public List<Sku> find() {
//some processing to find the sku, purposely left empty as it is a sample code
}
public void deactivate(List<Sku>) {
//some processing to deactivate the sku, purposely left empty as it is a sample code
}
}
#Repository("SkuAmerDao")
public class SkuAmerDAOImpl extends BaseDao {
#Autowired
#Qualifier("AmericasDataSource")
private DataSource datasource; //Dependency injection
#Override
protected DataSource getDatasource() {
return dataSource;
}
}
#Repository("SkuEMEADao")
public class SkuEMEADAOImpl extends BaseDao {
#Autowired
#Qualifier("EMEADataSource")
private DataSource datasource; //Dependency injection
#Override
protected DataSource getDatasource() {
return dataSource;
}
}
Always the same principle:
class is made a bean by an annotation #Service, #Component, #Repository (those annotations can take the name of the bean as value)
injection of dependency is made on fields with #Autowired and if there are more than one corresponding bean (in your case you have two DataSources), add a #Qualifier to specify which one.
Full documentation here.
I ran into an issue that can only be explained with my fundamental lack of understanding of Spring's IoC container facilities and context setup, so I would ask for clarification regarding this.
Just for reference, an application I am maintaing has the following stack of technologies:
Java 1.6
Spring 2.5.6
RichFaces 3.3.1-GA UI
Spring framework is used for bean management with Spring JDBC module used for DAO support
Maven is used as build manager
JUnit 4.4 is now introduced as test engine
I am retroactively (sic!) writing JUnit tests for the application and what suprised me is that I wasn't able to inject a bean into a test class by using setter injection without resorting to #Autowire notation.
Let me provide set up an example and accompanying configuration files.
The test class TypeTest is really simple:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest {
#Autowired
private IType type;
#Test
public void testFindAllTypes() {
List<Type> result;
try {
result = type.findAlltTypes();
assertNotNull(result);
} catch (Exception e) {
e.printStackTrace();
fail("Exception caught with " + e.getMessage());
}
}
}
Its context is defined in TestStackOverflowExample-context.xml:
<context:property-placeholder location="classpath:testContext.properties" />
<context:annotation-config />
<tx:annotation-driven />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.connection.driver.class}" />
<property name="url" value="${db.connection.url}" />
<property name="username" value="${db.connection.username}" />
<property name="password" value="${db.connection.password}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="beanDAO" class="com.example.BeanDAOImpl">
<property name="ds" ref="dataSource"></property>
<property name="beanDAOTwo" ref="beanDAOTwo"></property>
</bean>
<bean id="beanDAOTwo" class="com.example.BeanDAOTwoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="type" class="com.example.TypeImpl">
<property name="beanDAO" ref="beanDAO"></property>
</bean>
TestContext.properties is in classpath and contains only db-specific data needed for datasource.
This works like a charm but my question is - why doesn't it work when I try to manually wire beans and perform setter injection as in:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest {
private IType type;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
What am I missing here? What part of configuration is wrong here? When I try to manually inject beans via setters, test fails because this part
result = type.findAlltTypes();
is resolved as null in runtime. I've, of course, consulted the Spring reference manual and tried various combinations of XML configuration; all I could conclude is that Spring was unable to inject beans because it somehow fails to properly dereference Spring Test Context reference but by using #Autowired this happens "automagically" and I really can't see why is that because JavaDoc of both Autowired annotation and its PostProcessor class doesn't mention this.
Also worth adding is the fact that #Autowired is used in application only here. Elsewhere only manual wiring is performed, so this also brings forth question - why is it working there and not here, in my test? What part of DI configuration am I missing? How does #Autowired get reference of Spring Context?
EDIT:
I've also tried this but with same results:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
Any other ideas, perhaps?
EDIT2:
I've found a way without resorting to writing own TestContextListener or BeanPostProcessor. It is suprisingly simple and it turns out that I was on the right track with my last edit:
1) Constructor-based context resolving:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
type = ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
2) By implementing ApplicationContextAware interface:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
type = (Type) ctx.getBean("type");
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
Both of these approaches properly instanced beans.
If you take a look at the source of org.springframework.test.context.support.DependencyInjectionTestExecutionListener, you will see the following method (formatted and commented for clarity):
protected void injectDependencies(final TestContext testContext)
throws Exception {
Object bean = testContext.getTestInstance();
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext()
.getAutowireCapableBeanFactory();
beanFactory.autowireBeanProperties(bean,
AutowireCapableBeanFactory.AUTOWIRE_NO,
// no autowiring!!!!!!!!
false
);
beanFactory.initializeBean(bean, testContext.getTestClass().getName());
// but here, bean post processors are run
testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}
So the test object is a bean without auto-wiring. However, #AutoWired, #Resource etc, don't use the autowiring mechanism, they use BeanPostProcessor. And so the dependencies are injected if and only if the annotations are used (or if you register some other BeanPostProcessor that does it).
(The above code is from Spring 3.0.x, but I bet it was the same in 2.5.x)