복붙노트

[SPRING] 여러 데이터 소스에 사용할 Spring 데이터 저장소 bean 이름 사용자 정의

SPRING

여러 데이터 소스에 사용할 Spring 데이터 저장소 bean 이름 사용자 정의

Spring 데이터 (이 인스턴스에서는 MongoDB)를 사용하여 동일한 스키마로 여러 데이터베이스와 상호 작용하는 프로젝트가 있습니다. 이것이 의미하는 바는 각 데이터베이스가 동일한 엔티티 및 저장소 클래스를 사용한다는 것입니다. 예를 들면 다음과 같습니다.

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
}

단일 데이터베이스에 연결하는 경우에는 정상적으로 작동하지만 동시에 여러 데이터베이스에 연결할 때 상황이 더 복잡해집니다.

@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

}

다른 MongoTemplate 인스턴스를 사용하여 같은 저장소의 인스턴스를 여러 개 만들 수 있지만 인스턴스를 참조하고 주입하는 올바른 방법을 모르겠습니다. 개별 저장소 인스턴스를 다른 컨트롤러에 삽입하려면 다음과 같이 할 수 있어야합니다.

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

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

이 같은 구성이 가능합니까? @Resource 또는 @Autowired를 사용하여 인스턴스화 할 수 있도록 인스턴스화 된 인터페이스의 bean 이름을 어떻게 든 제어 할 수 있습니까?

보너스 질문 : 맞춤형 저장소 팩토리를 사용하여이 작업을 수행 할 수 있습니까?

해결법

  1. ==============================

    1.@NoRepositoryBean을 사용하여 저장소 인터페이스를 작성하십시오.

    @NoRepositoryBean을 사용하여 저장소 인터페이스를 작성하십시오.

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

    그런 다음 @Configuration 클래스에서 MongoRepositoryFactoryBean을 사용하여 2 개의 저장소 빈을 인스턴스화한다. 두 저장소 모두 동일한 Spring Data Repository 인터페이스를 반환하지만 다른 MongoOperations를 할당합니다 (예 : 데이터베이스 세부 정보).

    @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());
        }
    }
    

    별개의 @Qualifier 이름을 가진 두 개의 bean이 있으며 각각 다른 데이터베이스에 대해 구성되고 동일한 모델을 사용합니다.

    @Qualifier를 사용하여 주입 할 수 있습니다.

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

    간단히하기 위해 구성 클래스의 값을 하드 코딩했지만 application.properties /yml의 특성에서 값을 주입 할 수 있습니다.

    스프링 데이터 인터페이스 저장소의 이점을 잃지 않으면 서 사용자 정의 구현을 작성하려는 경우 다음과 같이 수정합니다. 사양은 이것을 말한다 :

    기술적으로 스프링 데이터와는 아무런 관련이없는 새로운 인터페이스를 만드십시오. 오래된 인터페이스입니다.

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

    저장소 인터페이스가이 새로운 인터페이스를 확장하게하십시오 :

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

    그런 다음 비 스프링 데이터 인터페이스 만 구현하는 구현 클래스를 만듭니다.

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

    Java 구성을 변경하여 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();
    }
    

    Java 구성을 통해 수동으로 저장소를 연결하지 않은 경우이 구현은 ModelMongoRepository + "Impl"인터페이스와 일치하도록 ModelMongoRepositoryImpl 이름을 지정해야합니다. 그리고 그것은 봄에 의해 자동으로 처리 될 것입니다.

  2. ==============================

    2.일반적인 @Repository의 경우 생성 된 Bean의 이름을 지정하기 위해 (value = "someDao")를 추가 할 수 있습니다. MongoRepository가 저장소를 확장하면이 작업이 작동합니다.

    일반적인 @Repository의 경우 생성 된 Bean의 이름을 지정하기 위해 (value = "someDao")를 추가 할 수 있습니다. MongoRepository가 저장소를 확장하면이 작업이 작동합니다.

  3. from https://stackoverflow.com/questions/38337453/customizing-spring-data-repository-bean-names-for-use-with-multiple-data-sources by cc-by-sa and MIT license