1

I had a working spring boot app and I am trying to migration from Spring boot 1.3.8.RELEASE to 1.5.2.RELEASE. In my working 1.3.8.RELEASE track I was using hibernate 5 already i.e. i had the the following dependency in y pom

     <dependency> 
         <groupId>org.hibernate</groupId> 
         <artifactId>hibernate-java8</artifactId> 
         <version>${hibernate.version}</version> 
     </dependency> 

with Spring boot 1.5 this is not needed thus i change this to the default hibernate 5 dependency i.e. hibernate-core and removed the version declaration.

<dependency>
     <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
</dependency>

However i can not figure out why my test is saying:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.hibernate.SessionFactory' 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:1486) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]

I have definitely configured the session factory

@Profile("Development")
@Configuration
@EnableTransactionManagement
@Order(value=1)
public class PersistenceConfigDevelopment {

    @Bean
    public org.apache.commons.dbcp.BasicDataSource dataSourceReadWrite() {

        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/testDB");
        dataSource.setUsername("admin");
        dataSource.setPassword("veryBigSecret!");
        dataSource.setInitialSize(20);
        dataSource.setMaxActive(-1);

        return dataSource;
    }


    @Bean
    public org.springframework.orm.hibernate5.LocalSessionFactoryBean sessionFactory() {

        org.springframework.orm.hibernate5.LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(this.dataSourceReadWrite());
        sessionFactory.setPackagesToScan("org.testApp");
        sessionFactory.setHibernateProperties(hibernateProperties());

        return sessionFactory;
    }


       @Bean
       public  org.springframework.orm.hibernate5.HibernateTransactionManager  transactionManager() {
          HibernateTransactionManager txManager = new HibernateTransactionManager();
          txManager.setSessionFactory(sessionFactory().getObject());
          return txManager;
       }


       @Bean
       public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
          return new PersistenceExceptionTranslationPostProcessor();
       }



    @Bean
    public  Properties hibernateProperties() {

    return new Properties() {

        {
                setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
                setProperty("hibernate.chach.provider_class", "org.hibernate.cache.NoCacheProvider");
                setProperty("hibernate.show_sql", "true");              
                setProperty("hibernate.hbm2ddl.auto", "create-drop");

                setProperty("hibernate.cache.use_second_level_cache", "false");
                setProperty("hibernate.cache.use_query_cache", "false");

                //isolation level
                setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));

             }
          };
       }

}

here is how i start my spring boot 1.5.2 test:

@RunWith(SpringRunner.class)
@TestExecutionListeners(listeners={ 
                                    DependencyInjectionTestExecutionListener.class,
                                    DirtiesContextTestExecutionListener.class,
                                    WithSecurityContextTestExecutionListener.class 
                                    }
        )
@SpringBootTest(
        classes = {
        SecurityWebApplicationInitializerDevelopment.class, 
        SecurityConfigDevelopment.class, 
        TomcatEmbededDevelopmentProfile.class, 
        Internationalization.class, 
        MVCConfigDevelopment.class,
        PersistenceConfigDevelopment.class,
        } 

    )
@WebAppConfiguration
@ActiveProfiles(TestAppConfigurationProfiles.DEVELOPMENT_PROFILE)
@WithMockUser(username="alice",roles={"USER","ADMIN"} )
public class mainTests { 
     ......

for some reason Spring boot 1.5 does not find my session factory. But why? Do i use the wrong dependency? I have configured Hibernate5 session factory and transaction manager as a bean.

Here are part os my pom:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>

    </parent>


    <groupId>test</groupId>
    <artifactId>testApp</artifactId>
    <name>testApp</name>
    <version>1.0-SNAPSHOT</version>


    <properties>

        <hibernate.version>5.2.9.Final</hibernate.version>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
        <maven-source-plugin.version>3.0.1</maven-source-plugin.version>
        <maven-javadoc-plugin.version>2.10.4</maven-javadoc-plugin.version>
        <maven-install-plugin.version>2.5.2</maven-install-plugin.version>
        <maven-assembly-plugin.version>3.0.0</maven-assembly-plugin.version>

        <java.version>1.8</java.version>

        <start-class>org.testApp.core.Application</start-class>

    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <exclusions> 
                <exclusion> 
                    <groupId>org.apache.tomcat</groupId> 
                    <artifactId>tomcat-jdbc</artifactId> 
                </exclusion> 
            </exclusions>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
        </dependency>

        <!-- Thymeleaf -->    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>nz.net.ultraq.thymeleaf</groupId>
            <artifactId>thymeleaf-layout-dialect</artifactId>
        </dependency>

        <!-- Tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
        </dependency>

        <!-- Hibernate -->


        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>

        <!-- Apache -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
        </dependency>



        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

      <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>  
      </dependency>
      <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.3</version>
      </dependency>


      <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-neo4j</artifactId>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-gemfire</artifactId>
      </dependency>
      <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
      </dependency>
    </dependencies>   

</project>

here is how i start my App

@ComponentScan({"org.testApp.*"})
@Configuration
@EnableAutoConfiguration
@EnableWebSocket
@SpringBootApplication
public class Application extends SpringBootServletInitializer  {
...

Addition 29 March 2017

After analyzing the startup config i found the line:

2017-03-29 10:45:01.084  INFO 11022 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'sessionFactory' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=persistenceConfigDevelopment; factoryMethodName=sessionFactory; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/testApp/config/persistence/PersistenceConfigDevelopment.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration; factoryMethodName=sessionFactory; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.class]]

What i do not understand is why Neo4jDataAutoConfiguration.class is overriding my custom persistenceConfigDevelopment bean.

Moreover if I remove the @EnableAutoConfiguration and @SpringBootApplication on my main Application class then the app start and the correct session factory is being successfully instantiated but then it fails since it can not find a bean InternalResourceViewResolver, but this is expected since usually @EnableAutoConfiguration is taking care of those beans.

`Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type` 'org.springframework.web.servlet.view.InternalResourceViewResolver' available
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
Tito
  • 2,234
  • 6
  • 31
  • 65
  • The Neo4J auto configuration support adds a bean named `sessionFactory` overriding your configured bean. – M. Deinum Mar 30 '17 at 07:35
  • @M. Deinum well why does it add it if i have explicitelly configured it in my config i.e. per the PersistenceConfigDevelopment class above. This does not make any sense to me. The default behaviour should be that if a user have configured a session factory i.e. hibernate then Neo4J should not do anything. – Tito Mar 30 '17 at 08:16
  • I think this will be a problem for a lot fo spring users since spring should not override beans that have been manually configured. – Tito Mar 30 '17 at 08:18
  • The override is due to naming conflicts. There can only be a single bean named `sessionFactory` (I haven't checked the neo4j auto configuration but I suspect that creates a ne04j bean named sessionFactory). And which one wins depends on the order in which the configuration classes are loaded. – M. Deinum Mar 30 '17 at 08:21
  • As far as I know in spring there is not way to say which beans loads first, at least no way per annotation, which can tell Neo4j to load second. But that does not change the fact that the business logic of the Neo4j auto-configuration should be intelligent enough to check if the user have configured a @bean with the name sessionFactory then Neo4j should not instantiate his own bean with the same name and should back off. – Tito Mar 30 '17 at 08:28
  • It checks only by type not by name. But if you don't need neo4j then don't add it (or disable auto configuration). You can Order configuration classes and have them loaded in specific orders (yours loads first before others and hence is overridden by other configured beans). The overriding mechanism applies to all beans (and you could disable it on the `ApplicationContext` level to throw an exception instead of overriding the beans). – M. Deinum Mar 30 '17 at 08:31
  • this seems to me a feature request – Tito Mar 30 '17 at 08:31
  • well i did had the @Order(value=1) annotation there set but I still got the error so the Order did not helped. – Tito Mar 30 '17 at 08:33
  • If you want it changed register a request in GitHub for spring boot. – M. Deinum Mar 30 '17 at 08:35
  • M. Deinum thanks for your time, i will do that since i believe that makes sense. – Tito Mar 30 '17 at 08:37

1 Answers1

1

I found a solution to my problem i.e

removed anything from the pom that is referencing neo4j i.e.

  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-neo4j</artifactId>
  </dependency>

then add to the main application class the following exclude:

@SpringBootApplication(exclude = JpaRepositoriesAutoConfiguration.class)
public class Application extends SpringBootServletInitializer  {

I do not know yet where exactly the error is however i know for sure that when i remove the spring-data-neo4j all works well and all my test pass.

Tito
  • 2,234
  • 6
  • 31
  • 65