2016-07-12 31 views
7

Mam projekt, który wykorzystuje dane źródłowe (MongoDB w tym przypadku) do interakcji z wieloma bazami danych o tym samym schemacie. Oznacza to, że każda baza danych wykorzystuje te same jednostki i klasy repozytoriów. Tak więc, na przykład:Dostosowywanie nazw komponentów repozytorium danych Spring do użycia z wieloma źródłami danych

public class Thing { 
    private String id; 
    private String name; 
    private String type; 
    // etc... 
} 

public interface ThingRepository extends PagingAndSortingRepository<Thing, String> { 
    List<Thing> findByName(String name); 
} 

@Configuration 
@EnableMongoRepositories(basePackageClasses = { ThingRepository.class }) 
public MongoConfig extends AbstractMongoConfiguration { 
    // Standard mongo config 
} 

Działa to dobrze jeśli Łączę się w jednej bazie danych, ale robi się bardziej skomplikowana, gdy chcę połączyć się z więcej niż jednej bazy danych w tym samym czasie:

@Configuration 
@EnableMongoRepositories(basePackageClasses = { ThingRepository.class }, 
    mongoTemplateRef = "mongoTemplateOne") 
public MongoConfigOne extends AbstractMongoConfiguration { 

    @Override 
    @Bean(name = "mongoTemplateOne") 
    public MongoTemplate mongoTemplate() throws Exception { 
     return new MongoTemplate(this.mongo(), "db_one"); 
    } 

    // Remaining standard mongo config 

} 

@Configuration 
@EnableMongoRepositories(basePackageClasses = { ThingRepository.class }, 
    mongoTemplateRef = "mongoTemplateTwo") 
public MongoConfigTwo extends AbstractMongoConfiguration { 

    @Override 
    @Bean(name = "mongoTemplateTwo") 
    public MongoTemplate mongoTemplate() throws Exception { 
     return new MongoTemplate(this.mongo(), "db_two"); 
    } 

    // Remaining standard mongo config 

} 

Mogę utworzyć wiele instancji tego samego repozytorium, używając różnych instancji MongoTemplate, ale nie znam prawidłowego sposobu odwoływania się do nich i ich wstrzykiwania. Chciałbym, aby móc wprowadzić poszczególne instancje repozytorium do różnych kontrolerów, tak:

@Controller 
@RequestMapping("/things/one/") 
public class ThingOneController { 
    @Resource private ThingRepository thingRepositoryOne; 
    ... 
} 

@Controller 
@RequestMapping("/things/two/") 
public class ThingTwoController { 
    @Resource private ThingRepository thingRepositoryTwo; 
    ... 
} 

to konfiguracja jak to możliwe? Czy mogę w jakiś sposób kontrolować nazwy komponentów inicjowanych interfejsów, aby można było do nich odwoływać się za pomocą @Resource lub @Autowired?

Dodatkowe pytanie: czy można to zrobić również w fabryce niestandardowego repozytorium?

+0

Może być konieczne ręczne utworzenie instancji repozytorium przy użyciu fabryki, po czym można nadać im nazwy za pomocą zwykłych technik '@ Bean'. – chrylis

+0

@chrylis: Czy możesz podać przykład tego jako odpowiedź? Nie jestem pewien, jak wyglądałby najlepszy sposób tworzenia komponentów komponentu bean produktu i repozytorium. – woemler

+0

Występowałem tylko przy ręcznym tworzeniu i nie mogę dać ci dobrej rady w tej kwestii. Prawdopodobnie Oliver Gierke będzie za kilka godzin. – chrylis

Odpowiedz

8

W ten sposób można osiągnąć, że:

Tworzenie repozytorium interfejsu (z @NoRepositoryBean będziemy podłączać go Nas):

@NoRepositoryBean 
public interface ModelMongoRepository extends MongoRepository<Model, String> { 
}  

Następnie w klasie konfiguracji instancji 2 fasole repozytorium za pomocą MongoRepositoryFactoryBean. Oba repozytoria używają tego samego interfejsu Wiosna repozytorium danych, ale będziemy przypisywać im różne MongoOperations (tj Szczegóły bazy):

@Configuration 
@EnableMongoRepositories 
public class MongoConfiguration { 

    @Bean 
    @Qualifier("one") 
    public ModelMongoRepository modelMongoRepositoryOne() throws DataAccessException, Exception { 
     MongoRepositoryFactoryBean<ModelMongoRepository, Model, String> myFactory = new MongoRepositoryFactoryBean<ModelMongoRepository, Model, String>(); 
     myFactory.setRepositoryInterface(ModelMongoRepository.class); 
     myFactory.setMongoOperations(createMongoOperations("hostname1", 21979, "dbName1", "username1", "password1")); 
     myFactory.afterPropertiesSet(); 
     return myFactory.getObject(); 
    } 

    @Bean 
    @Qualifier("two") 
    public ModelMongoRepository modelMongoRepositoryTwo() throws DataAccessException, Exception { 
     MongoRepositoryFactoryBean<ModelMongoRepository, Model, String> myFactory = new MongoRepositoryFactoryBean<ModelMongoRepository, Model, String>(); 
     myFactory.setRepositoryInterface(ModelMongoRepository.class); 
     myFactory.setMongoOperations(createMongoOperations("hostname2", 21990, "dbName2", "username2", "password2")); 
     myFactory.afterPropertiesSet(); 
     return myFactory.getObject(); 
    } 

    private MongoOperations createMongoOperations(String hostname, int port, String dbName, String user, String pwd) throws DataAccessException, Exception { 
     MongoCredential mongoCredentials = MongoCredential.createScramSha1Credential(user, dbName, pwd.toCharArray()); 
     MongoClient mongoClient = new MongoClient(new ServerAddress(hostname, port), Arrays.asList(mongoCredentials)); 
     Mongo mongo = new SimpleMongoDbFactory(mongoClient, dbName).getDb().getMongo(); 
     return new MongoTemplate(mongo, dbName); 
    } 
    //or this one if you have a connection string 
    private MongoOperations createMongoOperations(String dbConnection) throws DataAccessException, Exception { 
     MongoClientURI mongoClientURI = new MongoClientURI(dbConnection); 
     MongoClient mongoClient = new MongoClient(mongoClientURI); 
     Mongo mongo = new SimpleMongoDbFactory(mongoClient, mongoClientURI.getDatabase()).getDb().getMongo(); 
     return new MongoTemplate(mongo, mongoClientURI.getDatabase()); 
    } 
} 

masz teraz 2 fasola z odrębnych kwalifikacyjnych, każdy skonfigurowany dla różnych baz danych i przy użyciu ten sam model.

i obecnie można je wstrzykiwać przy użyciu tych samych określenia:

@Autowired 
@Qualifier("one") 
private ModelMongoRepository mongoRepositoryOne; 

@Autowired 
@Qualifier("two") 
private ModelMongoRepository mongoRepositoryTwo; 

dla uproszczenia, ja ciężko kodowane wartości w klasie konfiguracji, ale można wstrzykiwać je z właściwościami w application.properties/yml .

EDIT, aby odpowiedzieć na komentarze:

Oto modyfikacja jeśli chcesz utworzyć niestandardową implementację bez utraty korzyści z wiosennych interfejs danych repozytoriów. Specyfikacja mówi:

Często konieczne jest dostarczenie niestandardowej implementacji dla kilku metod repozytorium .W repozytoriach Spring Data można łatwo uzyskać kod niestandardowego repozytorium i zintegrować go z generyczną funkcją analizy abstrakcji i kwerendy CRUD . Aby wzbogacić repozytorium o niestandardową funkcjonalność, najpierw należy zdefiniować interfejs i implementację dla niestandardowej funkcjonalności w postaci . Użyj interfejsu z repozytorium , który podałeś, aby rozszerzyć niestandardowy interfejs. Najważniejszym bitem, który można znaleźć dla klasy, jest przyrostek Impl nazwy w porównaniu do interfejsu rdzenia repozytorium (patrz poniżej).

Utwórz nowy interfejs, który ma technicznie nic wspólnego z danymi wiosennych, stary dobry interfejs:

public interface CustomMethodsRepository { 
    public void getById(Model model){ 
} 

mieć swoje repozytorium interfejs rozszerza to nowy interfejs:

@NoRepositoryBean 
public interface ModelMongoRepository extends MongoRepository<Model, String>, CustomMethodsRepository { 
} 

Następnie stwórz swoją klasę implementacji, która tylko implementuje twój interfejs danych bez sprężyn:

public class ModelMongoRepositoryImpl implements CustomModelMongoRepository { 
    private MongoOperations mongoOperations; 

    public ModelMongoRepositoryImpl(MongoOperations mongoOperations) { 
     this.mongoOperations = mongoOperations; 
    } 
    public void getById(Model model){ 
     System.out.println("test"); 
    } 
} 

zmienić konfigurację Java, aby dodać myFactory.setCustomImplementation(new ModelMongoRepositoryImpl());:

@Bean 
@Qualifier("one") 
public ModelMongoRepository modelMongoRepositoryOne() throws DataAccessException, Exception { 
    MongoRepositoryFactoryBean<ModelMongoRepository, Model, String> myFactory = new MongoRepositoryFactoryBean<ModelMongoRepository, Model, String>(); 
    MongoOperations mongoOperations = createMongoOperations("hostname1", 21979, "dbName1", "usdername1", "password1"); 
    myFactory.setCustomImplementation(new ModelMongoRepositoryImpl(mongoOperations)); 
    myFactory.setRepositoryInterface(ModelMongoRepository.class); 
    myFactory.setMongoOperations(mongoOperations); 

    myFactory.afterPropertiesSet(); 
    return myFactory.getObject(); 
} 

Jeżeli nie zostało okablowanie repozytorium ręcznie poprzez config Java, to realizacja musiałaby być nazwany ModelMongoRepositoryImpl dopasować interfejs ModelMongoRepository +"Impl". I będzie to obsługiwane automatycznie wiosną.

+0

Co mam zrobić w przypadkach, w których dodałem niestandardowe metody, takie jak w przypadku 'ThingRepository.findByName()'? A może mam niestandardowe zaimplementowane metody w klasie 'ThingRepositoryImpl'? Czy muszę ręcznie zaimplementować je w mojej konkretnej klasie, że Spring będzie wstrzykiwać 'MongoTemplate'? Jeśli tak, to jaki jest sens nawet przy użyciu fabryki repozytoriów Spring Data? – woemler

+0

Widzę, co masz na myśli, będę aktualizować kod, aby korzystać z interfejsu + MongoRepositoryFactoryBean – alexbt

+0

Dzięki, wygląda na to, że to zadziała, ale teraz jestem zawieszony na tym, jak wstrzyknąć 'MongoTemplates' do' ThingRepositoryImpl' powinienem chcesz użyć niestandardowego narzędzia 'findByName (..)' lub innej metody. Myśli? – woemler

0

Dla ogólnego @Repository można po prostu dodać (value="someDao") nazwać utworzony Bean, jeśli MongoRepository rozciąga Repository to powinno działać.

+0

To mi nie pomaga, ponieważ tworzone jest więcej niż jedno wystąpienie każdego repozytorium. Użycie '@Repository (name =" myRepository ")' spowoduje powstanie dwóch komponentów o tej samej nazwie. Chcę się upewnić, że obiekty utworzone przez komponent bean repozytorium są nazwane inaczej i programowo. – woemler

+0

Możesz to zrobić @Autowire @Qualifier ("mongoTemplateOne") – Gandalf

+0

Nie próbuję wstrzykiwać poszczególnych obiektów 'MongoTemplate', próbuję wstrzyknąć repozytoriom, które są używane do tworzenia. W podanym przykładzie, w jaki sposób wstawiłbyś "ThingRepository" utworzony ze źródła danych? – woemler