简体   繁体   中英

How to inject and change database / data source on runtime?

Firstly I have created standard repository using JpaRepository for my entity Foo class to store it in database.

public interface FooRepository extends JpaRepository<Foo, Long> {
}

Furthermore as my second data access object I would like to use my own implementation of this simply CRUD operation interaface, which would store data in file.

public interface FooDao {
    Collection<Foo> getAll();
    Foo getById(Long id);
    void removeById(Long id);
    void update(Foo foo);
    void insert(Foo foo);
}

And separately it works fine when I have both variable manually declared like this

@Autowired
private FooRepository fooRepository;

@Autowired
private FooDao fooDao;

From services I use some kind of switch statement to identify which data source currently user chose, using helper variable:

private String datasource = "db";    // or "file"

But this solution require using if statement to determine which data access object I need to use, what duplicate code and of course is not elegant.

public Foo getOne(Long id){
    Foo result = null;

    if(datasource.equals("db"))
        result = fooRepository.findOne(id); 
    else if(datasource().equals("file"))
        result = fooDao.getById(id);            

    return result;
}

How can I change dynamically on runtime between those different and not compatible interfaces? How can I make them compatible to use them interchangeable, how properly write some kind of wrapper?

you could make your FooDao implementation implement the FooRepository interface rather than your own one. In this way you would just provide 2 different implementations for the interface and can handle them the same way in your service. They would just be two different Spring beans:

@Autowired
   private FooRepository fooRepository;

   @Autowired
   private FooRepository fooDao;

And your implementation would look like:

@Component
public class FooDaoImpl implements FooRepository {
  // your own implementations for the methods declared by FooRepository 
}

In your using class you could just use an access method to get one of this interfaces like this one:

private FooRepository getFooDataAccess() {
  return "db".equals(datasource) ? fooRepository : fooDao;
}

To avoid the conditional statements, you need to use a common interface.
The problem is that neither FooDao nor JpaRepository are good candidates to be the common interface.

JpaRepository subclasses benefits at runtime of a generated implementation, so you could not back the generated implementation to the FooDao interface.

Make the FooDao subclass implement JpaRepository makes no sense either as it forces your FooDao implementation to be coupled to JpaRepository and to implement all methods (several dozen) of that while it probably could not.
You could throw UnsupportedOperationException for the unsupported methods but it will make your code more brittle as misuses will be detected only at runtime.

So I think that in these specific conditions your actual way wrapping the two objects is a valid approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM