It may seem an obvious mistake, but I am not able to find it. Following is my code and configuration:
Application Class
#SpringBootApplication
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
SQL Config Class
#Configuration
public class PostgreSQLConfig {
#Bean
public DataSource getDBDataSource() {
final DriverManagerDataSource driverManagerDataSource
= new DriverManagerDataSource(
"jdbc:postgresql://localhost:5432/applicationdb",
"username",
"password"
);
driverManagerDataSource.setDriverClassName("org.postgresql.Driver");
return driverManagerDataSource;
}
#Bean
public JdbcTemplate getJdbcTemplate(final DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
#Repository DAO CLASS
#Repository
public class CustomDAO {
#Autowired
private JdbcTemplate jdbcTemplate;
.
.
#Component DAO Class
#Component
public class AnotherDAO {
#Autowired
private JdbcTemplate jdbcTemplate;
.
.
The JdbcTemplate object gets populated for #Component annotated class. But the object is null for #Repository annotated class. I started spring boot with logging set to DEBUG, and couldn't find any error while creating JdbcTemplate bean. From logs, I can infer the method is called and the bean is created. The JdbcTemplate also has reference to DataSource.
If the #Repository annotation is changed to #Component the code works fine.
When using spring jdbc, can we annotate a DAO class with
#Repository?
Why the dependency injection works with #Component but fails for
#Repository?
As per the suggestion from M. Deinum, I have made following changes.
Removed PostgreSQLConfig class
Added application.properties file
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/applicationdb
spring.datasource.user=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
Properties are loaded by spring application. Following are log messages:
2017-01-06 15:03:02 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'spring.datasource.driver-class-name' in [applicationConfigurationProperties] with type [String]
2017-01-06 15:03:02 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'spring.datasource.password' in [applicationConfigurationProperties] with type [String]
2017-01-06 15:03:02 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'spring.datasource.user' in [applicationConfigurationProperties] with type [String]
2017-01-06 15:03:02 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Found key 'spring.datasource.url' in [applicationConfigurationProperties] with type [String]
I receive an exception. Following are the details:
2017-01-06 15:03:05 [main] DEBUG o.s.b.d.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.jdbc.core.JdbcTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1474)
DAO classes are marked with #Repository annotation.
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.3.RELEASE'
}
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'idea'
apply plugin: 'application'
apply plugin: 'org.springframework.boot'
def applicationVersion = '0.0.1-SNAPSHOT'
def dependencyVersions = [
slf4j: '1.7.22',
logback: '1.1.8',
postgresql: '9.4.1212',
junit: '4.12'
]
group = 'com.appl'
version = applicationVersion
mainClassName = 'com.appl.Application'
war {
baseName = 'my-custom-appl'
version = applicationVersion
}
war.dependsOn test
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
configurations.all {
exclude group: 'commons-logging', module: 'commons-logging'
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-jdbc'
compile 'org.springframework:spring-jdbc'
compile "org.slf4j:jcl-over-slf4j:${dependencyVersions.slf4j}"
compile "org.postgresql:postgresql:${dependencyVersions.postgresql}"
compile "ch.qos.logback:logback-classic:${dependencyVersions.logback}"
testCompile "junit:junit:${dependencyVersions.junit}"
testCompile 'org.springframework.boot:spring-boot-starter-test'
}
I found the (what is happening part of) solution to the problem. Methods in DAO class were marked final, and #Repository doesn't works well with final method. From logs, I can infer that #Repository tries to create a proxy for DAO class, which fails.
When #Repository is replaced with #Component the code runs fine. Again from logs, I can see that #Component doesn't create a proxy and the final status of the method is ignored.
Following are the debug logs of Spring:
When method is final and DAO Class is marked as #Repository
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Autowiring by type from bean name 'customDAO' via constructor to bean named 'getJdbcTemplate'
2017-01-08 09:19:03 [main] DEBUG c.j.l.d.CustomDAO - Received JdbcTemplate: org.springframework.jdbc.core.JdbcTemplate#3dedb4a6
2017-01-08 09:19:03 [main] INFO c.j.l.d.CustomDAO - Constructor Called
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'customDAO' to allow for resolving potential circular references
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.j.l.data.CustomDAO#19542407]
2017-01-08 09:19:03 [main] INFO o.s.aop.framework.CglibAopProxy - Unable to proxy method [public final int com.j.l.data.CustomDAO.persistEvent(java.util.List)] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public java.lang.String java.lang.Object.toString()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Found 'hashCode' method: public native int java.lang.Object.hashCode()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.springframework.aop.Advisor)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.aopalliance.aop.Advice)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isFrozen()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setTargetSource(org.springframework.aop.TargetSource)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvisor(org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvisor(int,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setExposeProxy(boolean)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isExposeProxy()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isProxyTargetClass()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract org.springframework.aop.TargetSource org.springframework.aop.framework.Advised.getTargetSource()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setPreFiltered(boolean)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvice(int,org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvice(org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isPreFiltered()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.Class[] org.springframework.aop.framework.Advised.getProxiedInterfaces()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isInterfaceProxied(java.lang.Class)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract org.springframework.aop.Advisor[] org.springframework.aop.framework.Advised.getAdvisors()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.removeAdvisor(int) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.removeAdvisor(org.springframework.aop.Advisor)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.replaceAdvisor(org.springframework.aop.Advisor,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.removeAdvice(org.aopalliance.aop.Advice)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.String org.springframework.aop.framework.Advised.toProxyConfigString()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass()
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'customDAO'
When method is not final and DAO class is marked as #Repository
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'getJdbcTemplate'
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Autowiring by type from bean name 'anotherDAO' via constructor to bean named 'getJdbcTemplate'
2017-01-08 09:19:03 [main] DEBUG c.j.l.data.AnotherDAO - Received JdbcTemplate: org.springframework.jdbc.core.JdbcTemplate#3dedb4a6
2017-01-08 09:19:03 [main] INFO c.j.l.data.AnotherDAO - Constructor Called
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'anotherDAO' to allow for resolving potential circular references
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.j.l.data.AnotherDAO#907f2b7]
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public void com.j.l.data.AnotherDAO.persistEvent(com.j.l.model.EmergencyUserResponse)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public java.lang.String java.lang.Object.toString()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Found 'hashCode' method: public native int java.lang.Object.hashCode()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.springframework.aop.Advisor)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.aopalliance.aop.Advice)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isFrozen()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setTargetSource(org.springframework.aop.TargetSource)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvisor(org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvisor(int,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setExposeProxy(boolean)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isExposeProxy()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isProxyTargetClass()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract org.springframework.aop.TargetSource org.springframework.aop.framework.Advised.getTargetSource()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setPreFiltered(boolean)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvice(int,org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvice(org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isPreFiltered()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.Class[] org.springframework.aop.framework.Advised.getProxiedInterfaces()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isInterfaceProxied(java.lang.Class)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract org.springframework.aop.Advisor[] org.springframework.aop.framework.Advised.getAdvisors()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.removeAdvisor(int) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.removeAdvisor(org.springframework.aop.Advisor)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.replaceAdvisor(org.springframework.aop.Advisor,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.removeAdvice(org.aopalliance.aop.Advice)
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.String org.springframework.aop.framework.Advised.toProxyConfigString()
2017-01-08 09:19:03 [main] DEBUG o.s.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass()
2017-01-08 09:19:03 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'anotherDAO'
When method is final / not final and DAO class is marked as #Component
2017-01-08 09:13:21 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Autowiring by type from bean name 'customDAO' via constructor to bean named 'getJdbcTemplate'
2017-01-08 09:13:21 [main] DEBUG c.j.l.d.CustomDAO - Received JdbcTemplate: org.springframework.jdbc.core.JdbcTemplate#67fe380b
2017-01-08 09:13:21 [main] INFO c.j.l.d.CustomDAO - Constructor Called
2017-01-08 09:13:21 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'customDAO' to allow for resolving potential circular references
2017-01-08 09:13:21 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2017-01-08 09:13:21 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'customDAO'
I am still unable to understand why #Repository creates a proxy and #Component skips it? Is there a way to mark method final and use #Repository on DAO class?
If required I can create a separate question for this.
Related
I am exploring spring transactions and after going through the spring docs and the following link i still have few questions. If I have a parent method with Propogation.REQUIRED which is calling 3 methods with Propogation.REQUIRES_NEW as follows:
//#Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public Contact saveContactInSteps(Contact contact, String newFirstName, String newLastName, Date bDate) throws Exception {
contact.setFirstName(newFirstName);
try {
updateContactFirstName(contact);
} catch (Exception e) {
LOG.error("Contact first name could not be saved !!");
e.printStackTrace();
throw e;
}
contact.setLastName(newLastName);
try {
updateContactLastName(contact);
} catch (Exception e) {
LOG.error("Contact Last name could not be saved !!");
e.printStackTrace();
throw e;
}
contact.setBirthDate(bDate);
updateContactBday(contact);
return contact;
}
public Contact saveById(Contact contact) {
sessionFactory.getCurrentSession().saveOrUpdate(contact);
LOG.info("Contact " + contact + " saved successfully");
return contact;
}
//#Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public Contact updateContactFirstName(Contact contact) throws Exception {
throw new Exception("Cannot update first name");
//sessionFactory.getCurrentSession().saveOrUpdate(contact);
//LOG.info("Contact First name saved successfully");
//return contact;
}
//#Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public Contact updateContactLastName(Contact contact) throws Exception {
throw new Exception("Cannot update last name");
//sessionFactory.getCurrentSession().saveOrUpdate(contact);
//LOG.info("Contact " + contact + " saved successfully");
//return contact;
}
//#Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public Contact updateContactBday(Contact contact) {
sessionFactory.getCurrentSession().saveOrUpdate(contact);
LOG.info("Contact Birth Date saved successfully");
return contact;
}
Whenever i process the parent method i.e. saveContactInSteps, even the last name is getting updated in database. I am not sure how that is happening. If i throw the exception from first child method nothing gets updated. Is it because of the fact that i am setting the attributes i.e. names and birthdate in the parent method ? Even if i am setting the attributes in the parent method, i am not letting the parent method complete as well. Does the isolation level play any part here ? I believe it's for concurrent transactions ? Is there something i am missing ?
EDIT 1:
After going through the article i did realize what i was missing. I needed AOP proxies in place which i have added as follows:
<aop:config>
<aop:pointcut
expression="execution(* org.pack.spring.transactions.ContactService.*(..))"
id="updateOperation" />
<aop:advisor pointcut-ref="updateOperation" advice-ref="saveUpdateAdvice" />
</aop:config>
<tx:advice id="saveUpdateAdvice">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRES_NEW" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
Log output is as follows:
01:23:57.535 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.550 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
01:23:57.552 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'saveUpdateAdvice'
01:23:57.552 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'saveUpdateAdvice'
01:23:57.552 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'saveUpdateAdvice' to allow for resolving potential circular references
01:23:57.553 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
01:23:57.553 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean '(inner bean)#56ac5c80'
01:23:57.553 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
01:23:57.553 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
01:23:57.557 [main] DEBUG o.s.t.i.NameMatchTransactionAttributeSource - Adding transactional method [update*] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-Exception]
01:23:57.557 [main] DEBUG o.s.t.i.NameMatchTransactionAttributeSource - Adding transactional method [save*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-Exception]
01:23:57.557 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
01:23:57.557 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
01:23:57.557 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
01:23:57.557 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
01:23:57.558 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean '(inner bean)#56ac5c80'
01:23:57.558 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Invoking afterPropertiesSet() on bean with name 'saveUpdateAdvice'
01:23:57.558 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'saveUpdateAdvice'
01:23:57.568 [main] DEBUG o.s.a.a.a.AspectJAwareAdvisorAutoProxyCreator - Creating implicit proxy for bean 'springTxContactService' with 0 common interceptors and 3 specific interceptors
01:23:57.575 [main] DEBUG o.s.aop.framework.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [org.pack.ch9.spring.transactions.hibernate.home.ContactService#598260a6]
01:23:57.654 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.save(org.pack.ch9.spring.transactions.hibernate.home.Contact)
01:23:57.654 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.saveContactInSteps' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.655 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.saveContactInSteps(org.pack.ch9.spring.transactions.hibernate.home.Contact,java.lang.String,java.lang.String,java.util.Date) throws java.lang.Exception
01:23:57.655 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.findAll' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
01:23:57.656 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public java.util.List org.pack.ch9.spring.transactions.hibernate.home.ContactService.findAll()
01:23:57.656 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.findById' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
01:23:57.656 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.findById(java.lang.Long)
01:23:57.657 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.saveById' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.657 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.saveById(org.pack.ch9.spring.transactions.hibernate.home.Contact)
01:23:57.657 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.updateContactFirstName' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.657 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.updateContactFirstName(org.pack.ch9.spring.transactions.hibernate.home.Contact) throws java.lang.Exception
01:23:57.658 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.updateContactLastName' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.658 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.updateContactLastName(org.pack.ch9.spring.transactions.hibernate.home.Contact) throws java.lang.Exception
01:23:57.658 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.updateContactBday' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.658 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.pack.ch9.spring.transactions.hibernate.home.Contact org.pack.ch9.spring.transactions.hibernate.home.ContactService.updateContactBday(org.pack.ch9.spring.transactions.hibernate.home.Contact)
01:23:57.659 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.setSessionFactory' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.660 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public void org.pack.ch9.spring.transactions.hibernate.home.ContactService.setSessionFactory(org.hibernate.SessionFactory)
01:23:57.661 [main] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'ContactService.getSessionFactory' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:57.661 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public org.hibernate.SessionFactory org.pack.ch9.spring.transactions.hibernate.home.ContactService.getSessionFactory()
01:23:57.661 [main] DEBUG o.s.aop.framework.CglibAopProxy - Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object)
01:23:57.662 [main] DEBUG o.s.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public java.lang.String java.lang.Object.toString()
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'saveUpdateAdvice'
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'sessionFactory'
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'hibernateProperties'
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor'
01:23:57.698 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor'
01:23:57.699 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionalEventListenerFactory'
01:23:57.699 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
01:23:57.746 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor#21d1b321]
01:23:57.747 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
01:23:57.750 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
01:23:57.752 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'springTxContactService'
01:23:57.755 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
01:23:57.762 [main] DEBUG o.s.o.h.HibernateTransactionManager - Creating new transaction with name [org.pack.ch9.spring.transactions.hibernate.home.ContactService.findById]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
01:23:57.962 [main] DEBUG o.s.o.h.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for Hibernate transaction
01:23:57.964 [main] DEBUG o.s.o.h.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
01:23:57.965 [main] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/springorm]
01:23:57.977 [main] DEBUG o.s.jdbc.datasource.DataSourceUtils - Setting JDBC Connection [com.mysql.jdbc.JDBC4Connection#2a685eba] read-only
01:23:57.983 [main] DEBUG o.s.o.h.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mysql.jdbc.JDBC4Connection#2a685eba]
Hibernate: select distinct contact0_.ID as ID1_0_0_, contacttel1_.ID as ID1_2_1_, hobby3_.HOBBY_ID as HOBBY_ID1_3_2_, contact0_.BIRTH_DATE as BIRTH_DA2_0_0_, contact0_.FIRST_NAME as FIRST_NA3_0_0_, contact0_.LAST_NAME as LAST_NAM4_0_0_, contact0_.VERSION as VERSION5_0_0_, contacttel1_.CONTACT_ID as CONTACT_5_2_1_, contacttel1_.TEL_NUMBER as TEL_NUMB2_2_1_, contacttel1_.TEL_TYPE as TEL_TYPE3_2_1_, contacttel1_.VERSION as VERSION4_2_1_, contacttel1_.CONTACT_ID as CONTACT_5_2_0__, contacttel1_.ID as ID1_2_0__, hobbies2_.CONTACT_ID as CONTACT_2_1_1__, hobbies2_.HOBBY_ID as HOBBY_ID1_1_1__ from contact contact0_ left outer join contact_tel_detail contacttel1_ on contact0_.ID=contacttel1_.CONTACT_ID left outer join contact_hobby_detail hobbies2_ on contact0_.ID=hobbies2_.CONTACT_ID left outer join hobby hobby3_ on hobbies2_.HOBBY_ID=hobby3_.HOBBY_ID where contact0_.ID=?
01:23:58.099 [main] DEBUG o.s.o.h.HibernateTransactionManager - Initiating transaction commit
01:23:58.101 [main] DEBUG o.s.o.h.HibernateTransactionManager - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[org.pack.ch9.spring.transactions.hibernate.home.Contact#6], EntityKey[org.pack.ch9.spring.transactions.hibernate.home.ContactTelDetail#6], EntityKey[org.pack.ch9.spring.transactions.hibernate.home.ContactTelDetail#7]],collectionKeys=[CollectionKey[org.pack.ch9.spring.transactions.hibernate.home.Contact.contactTelDetails#6], CollectionKey[org.pack.ch9.spring.transactions.hibernate.home.Contact.hobbies#6]]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
01:23:58.103 [main] DEBUG o.s.jdbc.datasource.DataSourceUtils - Resetting read-only flag of JDBC Connection [com.mysql.jdbc.JDBC4Connection#2a685eba]
01:23:58.103 [main] DEBUG o.s.o.h.HibernateTransactionManager - Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[org.pack.ch9.spring.transactions.hibernate.home.Contact#6], EntityKey[org.pack.ch9.spring.transactions.hibernate.home.ContactTelDetail#6], EntityKey[org.pack.ch9.spring.transactions.hibernate.home.ContactTelDetail#7]],collectionKeys=[CollectionKey[org.pack.ch9.spring.transactions.hibernate.home.Contact.contactTelDetails#6], CollectionKey[org.pack.ch9.spring.transactions.hibernate.home.Contact.hobbies#6]]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
01:23:58.117 [main] DEBUG o.s.o.h.HibernateTransactionManager - Creating new transaction with name [org.pack.ch9.spring.transactions.hibernate.home.ContactService.saveContactInSteps]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
01:23:58.117 [main] DEBUG o.s.o.h.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for Hibernate transaction
01:23:58.117 [main] DEBUG o.s.o.h.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
01:23:58.117 [main] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/springorm]
01:23:58.127 [main] DEBUG o.s.o.h.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mysql.jdbc.JDBC4Connection#1daf3b44]
01:23:58.127 [main] DEBUG o.s.o.h.HibernateTransactionManager - Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for Hibernate transaction
01:23:58.127 [main] DEBUG o.s.o.h.HibernateTransactionManager - Participating in existing transaction
01:23:58.127 [main] ERROR o.p.c.s.t.h.home.ContactService - Contact First name could not be saved !!
01:23:58.127 [main] ERROR o.p.c.s.t.h.home.ContactService - Contact Last name could not be saved !!
01:23:58.137 [main] INFO o.p.c.s.t.h.home.ContactService - Contact Birth Date saved successfully
01:23:58.138 [main] DEBUG o.s.o.h.HibernateTransactionManager - Initiating transaction commit
01:23:58.138 [main] DEBUG o.s.o.h.HibernateTransactionManager - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[org.pack.ch9.spring.transactions.hibernate.home.Contact#6], EntityKey[org.pack.ch9.spring.transactions.hibernate.home.ContactTelDetail#6], EntityKey[org.pack.ch9.spring.transactions.hibernate.home.ContactTelDetail#7]],collectionKeys=[CollectionKey[org.pack.ch9.spring.transactions.hibernate.home.Contact.contactTelDetails#6], CollectionKey[org.pack.ch9.spring.transactions.hibernate.home.Contact.hobbies#6]]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
Note - I have removed the #Transactional annotations from the methods as i am relying on proxy to achieve the same.
From the logs, it seems that the proxies are getting created but again as pointed out in the comment, will the 3 methods invoked from saveContactInSteps not have a transaction against them as mentioned in the documentation :
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct
Is there a way to get around this ? Is AspectJ the only solution ?
I'm giving Kotlin a whirl and was converting the test below that was running fine in Java to Kotlin. After converting the test using the IntelliJ conversion tool I tried to run it but I got this error:
22:32:19.476 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class com.test.app.web.DeveloperControllerTest]
22:32:19.486 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
22:32:19.494 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
22:32:19.517 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.test.app.web.DeveloperControllerTest] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
22:32:19.539 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither #ContextConfiguration nor #ContextHierarchy found for test class [com.test.app.web.DeveloperControllerTest], using SpringBootContextLoader
22:32:19.543 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.test.app.web.DeveloperControllerTest]: class path resource [com/test/app/web/DeveloperControllerTest-context.xml] does not exist
22:32:19.544 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.test.app.web.DeveloperControllerTest]: class path resource [com/test/app/web/DeveloperControllerTestContext.groovy] does not exist
22:32:19.544 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.test.app.web.DeveloperControllerTest]: no resource found for suffixes {-context.xml, Context.groovy}.
22:32:19.545 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.test.app.web.DeveloperControllerTest]: DeveloperControllerTest does not declare any static, non-private, non-final, nested classes annotated with #Configuration.
java.lang.StackOverflowError
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
at java.util.concurrent.ConcurrentHashMap.containsKey(ConcurrentHashMap.java:964)
at java.lang.reflect.WeakCache.containsValue(WeakCache.java:175)
at java.lang.reflect.Proxy.isProxyClass(Proxy.java:791)
at java.lang.reflect.Proxy.getInvocationHandler(Proxy.java:815)
at sun.reflect.annotation.AnnotationInvocationHandler.asOneOfUs(AnnotationInvocationHandler.java:226)
at sun.reflect.annotation.AnnotationInvocationHandler.equalsImpl(AnnotationInvocationHandler.java:201)
at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:64)
at com.sun.proxy.$Proxy13.equals(Unknown Source)
at java.util.HashMap.putVal(HashMap.java:634)
at java.util.HashMap.put(HashMap.java:611)
at java.util.HashSet.add(HashSet.java:219)
at org.springframework.boot.test.context.ImportsContextCustomizer$ContextCustomizerKey.collectElementAnnotations(ImportsContextCustomizer.java:239)
at org.springframework.boot.test.context.ImportsContextCustomizer$ContextCustomizerKey.collectClassAnnotations(ImportsContextCustomizer.java:226)
Java test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class DeveloperControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void createNewDeveloper() throws Exception {
mvc.perform(
post("/api/v1/developers")
.param("username", "boaty")
.param("email", "boaty#mcboatface.org")
.param("password", "123loveboats")
.param("passwordConfirmation", "123loveboats")
).andExpect(status().isCreated());
}
}
Converted to Kotlin:
#RunWith(SpringRunner::class)
#SpringBootTest
#AutoConfigureMockMvc
class DeveloperControllerTest {
#Autowired
lateinit var mvc: MockMvc
#Test
#Throws(Exception::class)
fun createNewDeveloper() {
mvc.perform(
post("/api/v1/developers")
.param("username", "boaty")
.param("email", "boaty#mcboatface.org")
.param("password", "123loveboats")
.param("passwordConfirmation", "123loveboats"))
.andExpect(status().isCreated)
}
}
What I've noticed with this is that if I remove the annotation AutoConfigureMockMvc Spring will boot and try to run, but it'll fail because it can't autowire MockMvc.
I'm using Kotlin 1.0.1-2 together with Spring Boot 1.4.0-M2.
try adding
#ComponentScan(basePackages = arrayOf("YourAppBasePackage"))
#SpringBootTest(classes = arrayOf(MyExampleApplication::class))
This solved the issue for me
The stack overflow is occurring when Spring Boot attempts to introspect the annotations on DeveloperControllerTest.
DeveloperControllerTest is annotated with kotlin.Metadata. Metadata is annotated with kotlin.annotation.Retention. Retention is annotated with kotlin.annotation.Target and Target is annotated with itself. Target being annotated with itself is the cause of the stack overflowing.
It is legal, although probably quite unusual, for an annotation to be used to annotate itself. Spring Boot should probably be more defensive.
Creating a unit test with MockMvc I am running into:
HttpRequestMethodNotSupportedException: Request method 'POST' not supported
Which causes the test case to fail expecting a '200' but getting a '405'. Some of the additional things you may see in the Junit are for Spring Rest Docs and can be ignored. Such as the #Rule or the .andDo() on the mockMvc Call. I have followed the following documentation Spring Rest Docs - Path Parameter and cannot seem to get it working.
Here is the Controller:
#RestController("/transferObjects")
public class TransferObjectController {
#RequestMapping(method=RequestMethod.GET, produces="application/json")
public List<TransferObject> getTransferObjects(){
// do some magic
return null;
}
#RequestMapping(value = "/{name}", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void addTransferObject(#PathVariable("name")String name){
// do magic
}
#RequestMapping(value = "/{name}", method=RequestMethod.DELETE)
#ResponseStatus(HttpStatus.OK)
public void deleteTransferObject(#PathVariable("name")String name){
// do magic
}
Here is the Junit Class:
public class TransferObjectControllerTest {
#Rule
public RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.standaloneSetup(new TransferObjectController())
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void testAddTransferObject() throws Exception {
this.mockMvc.perform(post("/transferObjects/{name}", "hi"))
.andExpect(status().isOk()).andDo(document("transferObject",
pathParameters(
parameterWithName("name").description("The name of the new Transfer Object to be created."))));
}
And here is the console while running the test:
11:20:48.148 [main] INFO o.s.w.s.m.m.a.RequestMappingHandlerAdapter - Looking for #ControllerAdvice: org.springframework.test.web.servlet.setup.StubWebApplicationContext#5ab785fe
11:20:48.205 [main] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Looking for exception mappings: org.springframework.test.web.servlet.setup.StubWebApplicationContext#5ab785fe
11:20:48.283 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Initializing servlet ''
11:20:48.307 [main] DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [servletConfigInitParams] PropertySource with lowest search precedence
11:20:48.307 [main] DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [servletContextInitParams] PropertySource with lowest search precedence
11:20:48.312 [main] DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
11:20:48.312 [main] DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
11:20:48.313 [main] DEBUG o.s.w.c.s.StandardServletEnvironment - Initialized StandardServletEnvironment with PropertySources [servletConfigInitParams,servletContextInitParams,systemProperties,systemEnvironment]
11:20:48.313 [main] INFO o.s.mock.web.MockServletContext - Initializing Spring FrameworkServlet ''
11:20:48.313 [main] INFO o.s.t.w.s.TestDispatcherServlet - FrameworkServlet '': initialization started
11:20:48.318 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Unable to locate MultipartResolver with name 'multipartResolver': no multipart request handling provided
11:20:48.318 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Using LocaleResolver [org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver#63238bf4]
11:20:48.318 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Using ThemeResolver [org.springframework.web.servlet.theme.FixedThemeResolver#32b97305]
11:20:48.319 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Using RequestToViewNameTranslator [org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator#2d2e6747]
11:20:48.319 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Using FlashMapManager [org.springframework.web.servlet.support.SessionFlashMapManager#417e7d7d]
11:20:48.319 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Published WebApplicationContext of servlet '' as ServletContext attribute with name [org.springframework.web.servlet.FrameworkServlet.CONTEXT.]
11:20:48.319 [main] INFO o.s.t.w.s.TestDispatcherServlet - FrameworkServlet '': initialization completed in 6 ms
11:20:48.319 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Servlet '' configured successfully
11:20:48.361 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - DispatcherServlet with name '' processing POST request for [/transferObjects/hi]
11:20:48.364 [main] DEBUG o.s.t.w.s.s.StandaloneMockMvcBuilder$StaticRequestMappingHandlerMapping - Looking up handler method for path /transferObjects/hi
11:20:48.368 [main] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
11:20:48.369 [main] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
11:20:48.369 [main] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
11:20:48.369 [main] WARN o.s.web.servlet.PageNotFound - Request method 'POST' not supported
11:20:48.370 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
11:20:48.370 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request
It appears I was able to fix this problem.
Taking a closer look at the console while running the test I noticed:
Mapped "{[],methods=[GET],produces=[application/json]}" onto public java.util.List<common.to.TransferObject> sf.common.controller.TransferObjectController.getTransferObjects()
Mapped "{[/{name}],methods=[POST]}" onto public void sf.common.controller.TransferObjectController.addTransferObject(java.lang.String)
Mapped "{[/{name}],methods=[DELETE]}" onto public void sf.common.controller.TransferObjectController.deleteTransferObject(java.lang.String)
this shows that it is mapping the controller request mappings to the specific RequestMapping the method holds. #RestController("/transferObjects") is not actually defining the mapping as a parent. For me to do that I must include #RequestMapping("/transferObjects") underneath the #RestController.
So by changing the parent mapping I can now use the following test case:
#Test
public void testAddTransferObject() throws Exception {
this.mockMvc.perform(post("/transferObjects/{name}", "hi"))
.andExpect(status().isOk()).andDo(document("transferObject",
pathParameters(
parameterWithName("name").description("The name of the new Transfer Object to be created."))));
}
I'm trying to create Vaadin 7.5.5 app with add-on com.vaadin.vaadin-spring.1.0.0. This is not Spring Boot app, but deploy-able war with Spring context which should bootstrap Vaadin.
The error is:
Scope 'vaadin-view' is not active for the current thread
nested exception is java.lang.IllegalStateException: No active view
Here is full error trace:
2015-09-25 21:27:33 DEBUG DefaultListableBeanFactory:448 - Creating instance of bean 'vaadinSpringApplication'
2015-09-25 21:27:33 DEBUG InjectionMetadata:72 - Registered injected element on class [com.myapp.vaadin.demo.VaadinSpringApplication]: AutowiredFieldElement for com.vaadin.spring.navigator.SpringViewProvider com.myapp.vaadin.demo.VaadinSpringApplication.viewProvider
2015-09-25 21:27:33 DEBUG InjectionMetadata:72 - Registered injected element on class [com.myapp.vaadin.demo.VaadinSpringApplication]: AutowiredFieldElement for com.myapp.vaadin.demo.DefaultView com.myapp.vaadin.demo.VaadinSpringApplication.defaultView
2015-09-25 21:27:33 DEBUG InjectionMetadata:72 - Registered injected element on class [com.myapp.vaadin.demo.VaadinSpringApplication]: AutowiredFieldElement for com.myapp.vaadin.demo.SpringLoginView com.myapp.vaadin.demo.VaadinSpringApplication.springLoginView
2015-09-25 21:27:33 DEBUG InjectionMetadata:72 - Registered injected element on class [com.myapp.vaadin.demo.VaadinSpringApplication]: AutowiredFieldElement for com.myapp.vaadin.demo.SpringMainView com.myapp.vaadin.demo.VaadinSpringApplication.springMainView
2015-09-25 21:27:33 DEBUG InjectionMetadata:86 - Processing injected element of bean 'vaadinSpringApplication': AutowiredFieldElement for com.vaadin.spring.navigator.SpringViewProvider com.myapp.vaadin.demo.VaadinSpringApplication.viewProvider
2015-09-25 21:27:33 DEBUG DefaultListableBeanFactory:250 - Returning cached instance of singleton bean 'viewProvider'
2015-09-25 21:27:33 DEBUG AutowiredAnnotationBeanPostProcessor:490 - Autowiring by type from bean name 'vaadinSpringApplication' to bean named 'viewProvider'
2015-09-25 21:27:33 DEBUG InjectionMetadata:86 - Processing injected element of bean 'vaadinSpringApplication': AutowiredFieldElement for com.myapp.vaadin.demo.DefaultView com.myapp.vaadin.demo.VaadinSpringApplication.defaultView
2015-09-25 21:27:33 DEBUG DefaultListableBeanFactory:448 - Creating instance of bean 'viewCache'
2015-09-25 21:27:33 DEBUG DefaultListableBeanFactory:250 - Returning cached instance of singleton bean 'com.vaadin.spring.VaadinConfiguration'
2015-09-25 21:27:33 DEBUG CommonAnnotationBeanPostProcessor:216 - Found destroy method on class [com.vaadin.spring.internal.DefaultViewCache]: void com.vaadin.spring.internal.DefaultViewCache.destroy()
2015-09-25 21:27:33 DEBUG CommonAnnotationBeanPostProcessor:288 - Registered destroy method on class [com.vaadin.spring.internal.DefaultViewCache]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement#5cd39ffa
2015-09-25 21:27:33 DEBUG DefaultListableBeanFactory:484 - Finished creating instance of bean 'viewCache'
Sep 25, 2015 9:27:33 PM com.vaadin.server.DefaultErrorHandler doDefault
SEVERE:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vaadinSpringApplication': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.myapp.vaadin.demo.DefaultView com.myapp.vaadin.demo.VaadinSpringApplication.defaultView; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultView': Scope 'vaadin-view' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No active view
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$2.getObject(AbstractBeanFactory.java:344)
at com.vaadin.spring.internal.BeanStore.create(BeanStore.java:71)
at com.vaadin.spring.internal.UIScopeImpl$UIBeanStore.create(UIScopeImpl.java:279)
at com.vaadin.spring.internal.BeanStore.get(BeanStore.java:62)
at com.vaadin.spring.internal.SessionLockingBeanStore.get(SessionLockingBeanStore.java:46)
at com.vaadin.spring.internal.UIScopeImpl.get(UIScopeImpl.java:81)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:339)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:219)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1057)
at com.vaadin.spring.server.SpringUIProvider.createInstance(SpringUIProvider.java:172)
at com.vaadin.server.communication.UIInitHandler.getBrowserDetailsUI(UIInitHandler.java:191)
at com.vaadin.server.communication.UIInitHandler.synchronizedHandleRequest(UIInitHandler.java:74)
at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41)
at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1408)
at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:351)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
Here is application main class:
#Theme("valo")
#SpringUI
public class VaadinSpringApplication extends UI {
#Configuration
#EnableVaadin
public static class VaadinSpringApplicationConfiguration {}
#Autowired
SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest request) {
Navigator navigator = new Navigator(this, this);
navigator.addProvider(viewProvider);
navigator.navigateTo(DefaultView.NAME);
}
}
DefaultView.java
#SpringComponent
#SpringView(name = DefaultView.NAME)
public class DefaultView extends CustomComponent implements View {
public static final String NAME = "defaultView";
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
Button button = new Button("Click Me");
setCompositionRoot(layout);
}
}
As you may see from logs, spring beans (viewCache, defaultView, springLoginView, springMainView) has been created. But once I've tried to inject it into main class - got the error "Scope 'vaadin-view' is not active for the current thread".
Please advice. Thank you.
I'm using spring-data-neo4j in a spring-boot application. I did the configuration as recommended in spring.io guides and many other places by inheriting Neo4jConfiguration class. This works when the database location is hardcoded in the provided examples. However when I want to use a placeholder for retrieving the database location from a property file it's not retrieved and I get null. Here's the code
#Configuration
#EnableNeo4jRepositories(basePackageClasses = {MyRepository.class})
public class Neo4jConfig extends Neo4jConfiguration {
#Value("${neo4j.location}")
private String neo4jDatabaseLocation;
#Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory()
.newEmbeddedDatabase(neo4jDatabaseLocation);
}
...
This normally works in any other config class but not in this one because of the Neo4jConfiguration class has some several methods marked with #Autowired. This causes circular reference and it's not initialized properly. This can be seen in the logs:
2014-09-06 20:59:45.168 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredFieldElement for private javax.validation.Validator org.springframework.data.neo4j.config.Neo4jConfiguration.validator
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setConversionService(org.springframework.core.convert.ConversionService)
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setGraphDatabaseService(org.neo4j.graphdb.GraphDatabaseService)
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredFieldElement for private java.lang.String c.m.f.Neo4jConfig.neo4jDatabaseLocation
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'c.m.f.Neo4jConfig' to allow for resolving potential circular references
2014-09-06 20:59:45.171 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Processing injected method of bean 'c.m.f.Neo4jConfig': AutowiredFieldElement for private javax.validation.Validator org.springframework.data.neo4j.config.Neo4jConfiguration.validator
2014-09-06 20:59:45.182 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Processing injected method of bean 'c.m.f.Neo4jConfig': AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setConversionService(org.springframework.core.convert.ConversionService)
2014-09-06 20:59:45.183 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Processing injected method of bean 'c.m.f.Neo4jConfig': AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setGraphDatabaseService(org.neo4j.graphdb.GraphDatabaseService)
2014-09-06 20:59:45.184 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'graphDatabaseService'
2014-09-06 20:59:45.184 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'graphDatabaseService'
2014-09-06 20:59:45.185 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2014-09-06 20:59:45.185 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2014-09-06 20:59:45.188 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning eagerly cached instance of singleton bean 'c.m.f.Neo4jConfig' that is not fully initialized yet - a consequence of a circular reference
As you can see what I'm trying to achieve here is not to hardcode the database location. Is there any workaround for this circular reference problem? Or maybe any other way of configuring it? As this is a spring-boot application, I don't have any Xml configuration and if it's possible I want to keep it that way.
Have you tried passing in the neo4jDatabaseLocation as a parameter:
#Configuration
#EnableNeo4jRepositories(basePackageClasses = {MyRepository.class})
public class Neo4jConfig extends Neo4jConfiguration {
#Bean
public GraphDatabaseService graphDatabaseService(#Value("${neo4j.location}") String neo4jDatabaseLocation) {
return new GraphDatabaseFactory()
.newEmbeddedDatabase(neo4jDatabaseLocation);
}
...
}
Proposed suggestions did not work for the spring framework version I am using. As a workaround, I defined the GraphDatabaseService bean in another configuration file which does not extend Neo4jConfiguration. Note that this Neo4jConfig class is needed regardless.