Search code examples
javaspringspring-bootredisspring-data-redis

Spring Data Redis InvocationTargetException


I am currently trying my hands on Spring Data Redis in order to getting and putting my data to a redis repository. However, upon running my apps using mvn spring-boot:run, It always returns error with below message:

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.7.RELEASE:run (default-cli) on project networkprofile-bs-redis-poc: An exception occurred while running. null: InvocationTargetException: Invalid bean definition with name 'metadataRepository' defined in null: Cannot register bean definition [Root bean: class [org.springframework.data.redis.repository.support.RedisRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'metadataRepository': There is already [Root bean: class [org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound. -> [Help 1]

I have two repository interfaces in my code, MetadataRepository and ProfileRepository, and for some reason the error keeps going back and forth between those two, with the same InvocationTargetException.

MetadataRepository

package xxx.repository;

import org.springframework.data.repository.CrudRepository;

import xxx.model.redis.Metadata;

public interface MetadataRepository extends CrudRepository<Metadata, String> {}

ProfileRepository

package xxx.repository;

import org.springframework.data.repository.CrudRepository;

import xxx.model.redis.Profile;

public interface ProfileRepository extends CrudRepository<Profile, String> {
    Profile findByMsisdnAndProfile(String msisdn, String profile);
}

pom.xml

...

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

...

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    
    ...    

    <axiom.version>1.2.21</axiom.version>
    <springfox.version>2.9.2</springfox.version>
    
    ...
    
</properties>

<dependencies>
    ...
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web-services</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ojdbc8</artifactId>
        <version>12.2.0.1</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.ws.commons.axiom</groupId>
        <artifactId>axiom-api</artifactId>
        <version>${axiom.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.ws.commons.axiom</groupId>
        <artifactId>axiom-impl</artifactId>
        <version>${axiom.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    
    <!-- Swagger -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>${springfox.version}</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>${springfox.version}</version>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

I have set some Redis Configuration as below:

package xxx.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

@Configuration
@EnableRedisRepositories
public class RedisConfig {

    @Value("${xxx.redis.host}")
    private String redisHost;
    
    @Value("${xxx.redis.port}")
    private int redisPort;
    
    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisHost, redisPort);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(){
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(lettuceConnectionFactory());
        template.afterPropertiesSet();
        return template;
    }
}

I'm not quite sure on what causes this because this is different from the similar problem that throws BeanDefinitionOverrideException.


My goal is that so my apps can run without problems, so I need to pinpoint the cause of this. Perhaps there is something I've done incorrectly?


Update

I found out that spring-boot-starter-data-jdbc and spring-boot-starter-data-redis is causing the conflict in my build, resulting in InvocationTargetException. Upon excluding the JDBC dependencies, the InvocationTargetException disappears. However, doing so will break my other modules which requires JDBC connection. So I would need both the JDBC and Redis dependencies in my case.


Solution

  • I was stuck with a similar problem for almost two days, but finally figured it out. The thing is, when multiple Spring Data modules are used in a project, the Spring framework enters into strict repository configuration mode and uses different detection mechanisms underneath, to identify which repository belongs to which persistence technology (explained here).

    In your case, two Spring Data modules i.e. spring-data-jdbc and spring-data-redis are used. So, The ProfileRepository and MetadataRepository, defined in your project, could be used as repository both for spring-data-jdbc and spring-data-redis. An approach to solve this problem is to use module specific annotations. For example, to define ProfileRepository as a Repository related to Redis, you must define your Profile class as :

    @RedisHash("Profile")
    public class Profile implements Serializable {
    // fields and getters/setters as usual
    }
    

    OR, if you want to define ProfileRepository as a Repository related to JDBC, you must define your Profile class as :

    @Table("Profile")
    public class Profile implements Serializable {
    // fields and getters/setters as usual
    }