10

I Have two repository interfaces that connect : MongoDB and CouchBase :

public interface UserRepositoryMongo extends MongoRepository<User, Long> {
}
public interface UserRepositoryCouch extends  CouchbasePagingAndSortingRepository<User, Long> {
}

Is there a way to interchangeably @Autowire these repositories into UserService on a condition? The condition will be inside property file : application.properties.



**Note :**

*These repositories can have custom methods too.*
Nilanka Manoj
  • 3,527
  • 4
  • 17
  • 48

2 Answers2

16

We can use either ConditionalOnProperty or ConditionalOnExpression to switch between two different repository implementation.

  1. If we want to control the autowiring with simple property presence/absence or property value, then ConditionalOnProperty can be used.

  2. If complex evaluation is required, then we can use ConditionalOnExpression.

ConditionalOnProperty (presence/absence of a property)

@Qualifier("specificRepo")
@ConditionalOnProperty("mongo.url")
public interface UserRepositoryMongo extends MongoRepository<User, Long>{
}

@Qualifier("specificRepo")   
@ConditionalOnProperty("couch.url")
public interface UserRepositoryCouch extends  CouchbasePagingAndSortingRepository<User, Long>{
}

ConditionalOnProperty (based on value)

@ConditionalOnProperty("repo.url", havingValue="mongo", matchIfMissing = true) //this will be default implementation if no value is matching
public interface UserRepositoryMongo extends MongoRepository<User, Long> {
}

@ConditionalOnProperty("repo.url", havingValue="couch")
public interface UserRepositoryCouch extends  CouchbasePagingAndSortingRepository<User, Long> {
}

ConditionalOnExpression

@ConditionalOnExpression("#{'${repository.url}'.contains('couch')}")
public interface UserRepositoryCouch extends  CouchbasePagingAndSortingRepository<User, Long> {
}

UPDATE

Use CrudRepository/Repository type to inject based on your requirement.

public class DemoService {

    @Autowired
    @Qualifier("specificRepo")
    private CrudRepository repository;
}

Based on bean created, either UserRepositoryMongo or UserRepositoryCouch will be autowired. Make sure only one bean is instantiated to avoid ambiguity error.

Kumar V
  • 1,570
  • 1
  • 12
  • 19
  • so, how I autowire this into service? – Nilanka Manoj Jun 01 '20 at 02:55
  • updated the answer to include injection logic as well. If you need pagination functionality, `PagingAndSortingRepository` can be used instead of `CrudRepository` – Kumar V Jun 01 '20 at 03:14
  • The issue is that I have other repositories in the package, This type of autowire will cause errors. – Nilanka Manoj Jun 01 '20 at 03:17
  • Add a marker interface if you need to use only between two of them and you can use that interface type for injection. – Kumar V Jun 01 '20 at 03:19
  • updated first code snippet to show what I meant in the above comment. – Kumar V Jun 01 '20 at 03:22
  • I tried SpecificRepository one, But it seems like, cannot use couch, mongo repo methods when autowired. – Nilanka Manoj Jun 01 '20 at 03:26
  • eg : repository.save not found – Nilanka Manoj Jun 01 '20 at 03:28
  • Understood. We can try with Qualifiers to limit the repository implementation that qualifies for CrudRepository to only two of them. Updated my answer. Hopefully other implementation should not have issues as they would have used specific Repository implementation. – Kumar V Jun 01 '20 at 03:42
  • Other approach I could think about, let `SpecificRepository` extend both `MongoRepository` & `CouchRepository` and change your custom repository implementations (`UserRepositoryMongo & UserRepositoryCouch`) to extend from `SpecificRepository` so that we can use `SpecificRepository` for injection. (although it's not efficient, it would still solve your problem) – Kumar V Jun 01 '20 at 03:51
  • Is it possible to resolve custom methods using @qualifier? – Nilanka Manoj Jun 01 '20 at 03:54
  • if you need support for custom methods, then the approach in my previous comment would help. – Kumar V Jun 01 '20 at 04:11
7

Another attempt to solve this problem.

Based on the property, the autowired customRepository will be an instance of SimpleMongoRepository or SimpleCouchRepository.

    public interface CustomRepository extends CrudRepository<User, Long> {
        User findByLastName(String lastName); //custom methods
    }

    @Qualifier("specificRepo")
    @ConditionalOnProperty("mongo.url")
    public interface UserRepositoryMongo extends MongoRepository<User, Long>, CustomRepository{
    }

    @Qualifier("specificRepo")   
    @ConditionalOnProperty("couch.url")
    public interface UserRepositoryCouch extends  CouchbasePagingAndSortingRepository<User, Long>, CustomRepository{
    }

    public class UserService {

        @Autowired
        @Qualifier("specificRepo")
        private CustomRepository repository;
    }
Kumar V
  • 1,570
  • 1
  • 12
  • 19