0
votes

so I have a project that use spring-data-neo4j and met a obscure problem. I use java config for spring-neo4j,Neo4jConfig.java:

@Configuration
@EnableNeo4jRepositories(basePackages = "org.neo4j.example.repository")
@EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration {

    @Bean
    public SessionFactory getSessionFactory() {
        // with domain entity base package(s)
        return new SessionFactory("org.neo4j.example.domain");
    }

    // needed for session in view in web-applications
    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public Session getSession() throws Exception {
        return super.getSession();
    }

}

the I hava a DAO and one implements,BeanDaoImpl.java:

@Repository
public class BeanDaoImpl implements BeanDao {
    public String getStr() {
        return "from BeanImpl";
    }
}

then I have a service use the DaoImpl,note that the autowired is BeanDaoImpl,not BeanDao:

@Service
public class MyBeanService {
    @Autowired
    private BeanDaoImpl daoImpl;


    public String getServiceString(){
        return daoImpl.getStr();
    }
}

here is my app-context.xml:

    <context:component-scan base-package="com.springconflict" />

the version is springframework 4.2.5, spring-data-neo4j 4.1.11,it seems spring-data-neo4j has compatibility with spring 4.2.5;

here is the compile error info:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.springconflict.dao.impl.BeanDaoImpl com.springconflict.service.MyBeanService.daoImpl; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.springconflict.dao.impl.BeanDaoImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

The odds are either I remove Neo4jConfig or use @Autowired BeanDao the test can pass. also, I use a common @Configuration class, the test still pass,so the problem may in Neo4jConfiguration, can someone tell me why and how to fix this? I have no access to change @Autowired BeanDaoImpl to @Autowired BeanDao in real project.

all code are in here

1

1 Answers

1
votes

TL;DR Answer: Define the following additional bean in Neo4jConfig, thus overwriting SDN 4.x's default instance of PersistenceExceptionTranslationPostProcessor.

@Bean
PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
    PersistenceExceptionTranslationPostProcessor p = new PersistenceExceptionTranslationPostProcessor();
    p.setProxyTargetClass(true);
    return p;
}

Long answer: Spring Data Neo4j uses Springs PersistenceExceptionTranslationPostProcessor to remap Neo4j OGM exceptions into Spring Data exceptions. Those processor is applied to all components of type @Repository.

So the post processor forms now a proxy around your BeanDoaImpl and thus it's basically like another class. That works fine if you use the interface, but not if you want to assign the bean to the concrete class.

Spring can be configured globally to create subclasses instead of standard Java interface based proxies (it does that by use of CGLIB), but that's not on by default and in case of Spring 4.2.x and Spring Data Neo4j 4.2.x would not matter anyway, as the post processor above was independent from that mechanism.

Replacing it with one explicitly configured solves your problem.

From an architectural point of view (or clean code wise), it would be better if you would inject the interface, not the concrete class, though.

Please let me know, if I could help you :)