SpringData의 MongoTemplate과 MongoRepository의 차이점은 무엇입니까?


97

스프링 데이터와 mongodb를 사용하여 복잡한 쿼리를 수행 할 수있는 애플리케이션을 작성해야합니다. MongoRepository를 사용하여 시작했지만 예제를 찾거나 실제로 구문을 이해하기 위해 복잡한 쿼리로 어려움을 겪었습니다.

나는 다음과 같은 쿼리에 대해 이야기하고 있습니다.

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    List<User> findByEmailOrLastName(String email, String lastName);
}

또는 구문이 잘못 되었기 때문에 시행 착오를 거쳐 시도한 JSON 기반 쿼리 사용. mongodb 문서를 읽은 후에도 (잘못된 구문으로 인해 작동하지 않는 예제).

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    @Query("'$or':[{'firstName':{'$regex':?0,'$options':'i'}},{'lastName':{'$regex':?0,'$options':'i'}}]")
    List<User> findByEmailOrFirstnameOrLastnameLike(String searchText);
} 

모든 문서를 읽은 후에는 문서화 mongoTemplate가 훨씬 더 나은 것 같습니다 MongoRepository. 다음 문서를 참조하고 있습니다.

http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/

사용하기 더 편리하고 강력한 것이 무엇인지 말씀해 주시겠습니까? mongoTemplate또는 MongoRepository? 둘 다 똑같습니까, 아니면 둘 중 하나가 다른 것보다 더 많은 기능이 부족합니까?

답변:


130

"편리함"과 "사용하기 강력 함"은 어느 정도 상반되는 목표입니다. 리포지토리는 템플릿보다 훨씬 편리하지만 물론 후자는 실행할 항목을보다 세밀하게 제어 할 수 있습니다.

저장소 프로그래밍 모델은 여러 SpringData 모듈에서 사용할 수 있으므로 SpringData MongoDB 참조 문서 의 일반 섹션에서 더 자세한 문서를 찾을 수 있습니다 .

TL; DR

일반적으로 다음 접근 방식을 권장합니다.

  1. 저장소 추상으로 시작하고 쿼리 파생 메커니즘 또는 수동으로 정의 된 쿼리를 사용하여 간단한 쿼리를 선언하십시오.
  2. 더 복잡한 쿼리의 경우 수동으로 구현 된 메서드를 저장소에 추가합니다 (여기에 설명 된대로). 구현을 위해 MongoTemplate.

세부

귀하의 예를 들어 이것은 다음과 같습니다.

  1. 사용자 정의 코드에 대한 인터페이스를 정의하십시오.

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
  2. 이 클래스에 대한 구현을 추가하고 이름 지정 규칙을 따라 클래스를 찾을 수 있는지 확인하십시오.

    class UserRepositoryImpl implements CustomUserRepository {
    
      private final MongoOperations operations;
    
      @Autowired
      public UserRepositoryImpl(MongoOperations operations) {
    
        Assert.notNull(operations, "MongoOperations must not be null!");
        this.operations = operations;
      }
    
      public List<User> yourCustomMethod() {
        // custom implementation here
      }
    }
  3. 이제 기본 저장소 인터페이스가 사용자 정의 인터페이스를 확장하도록하면 인프라가 사용자 정의 구현을 자동으로 사용합니다.

    interface UserRepository extends CrudRepository<User, Long>, CustomUserRepository {
    
    }

이렇게하면 본질적으로 선택할 수 있습니다. 선언하기 쉬운 UserRepository모든 것은에 들어가고 , 더 잘 구현 된 모든 것은에 들어가게됩니다 CustomUserRepository. 사용자 정의 옵션은 여기 에 설명되어 있습니다 .


안녕하세요 올리버, 이것은 실제로 작동하지 않습니다. spring-data는 사용자 지정 이름에서 쿼리를 자동 생성하려고합니다. yourCustomMethod (). "your"는 도메인 클래스에서 유효한 필드가 아닙니다. 나는 매뉴얼을 따랐고 spring-data-jpa-examples에서 어떻게하고 있는지 두 번 확인했습니다. 불운. spring-data는 사용자 정의 인터페이스를 저장소 클래스로 확장하자마자 항상 자동 생성을 시도합니다. 유일한 차이점은 지금은 Iterator로 작업하고 싶지 않기 때문에 CrudRepository가 아닌 MongoRepository를 사용하고 있다는 것입니다. 힌트가 있다면 감사하겠습니다.
크리스토퍼 암스트롱

10
가장 흔한 실수는 구현 클래스의 이름을 잘못 지정하는 것입니다. 기본 저장소 인터페이스가 호출 YourRepository되면 구현 클래스의 이름을으로 지정해야 YourRepositoryImpl합니다. 그럴까요? 만약 내가 GitHub의에 샘플 프로젝트를 살펴 또는 ...처럼 받아 행복 해요 그래서
올리버 Drotbohm

5
Oliver 님, Impl 클래스의 이름이 당신이 생각한대로 잘못 명명되었습니다. 이름을 조정했는데 이제 작동하는 것처럼 보입니다. 의견을 보내 주셔서 감사합니다. 이런 식으로 다른 종류의 쿼리 옵션을 사용할 수 있다는 것은 정말 멋진 일입니다. 잘 생각했습니다!
Christopher Armstrong

이 대답은 명확하지 않습니다. 이 예제로 모든 작업을 수행 한 후에는 stackoverflow.com/a/13947263/449553 이 문제에 해당합니다 . 따라서 명명 규칙은이 예제에서 보는 것보다 더 엄격합니다.
msangel

1
# 2의 구현 클래스 이름이 잘못되었습니다. should CustomUserRepository및 not CustomerUserRepository.
Cotta

28

이 답변은 약간 지연 될 수 있지만 전체 저장소 경로를 피하는 것이 좋습니다. 실질적인 가치가있는 구현 방법은 거의 없습니다. 그것을 작동시키기 위해 당신은 문서에서 많은 도움없이 며칠과 몇 주를 보낼 수있는 자바 구성의 말도 안되는 상황에 빠진다.

대신, MongoTemplate경로 를 따라 가서 Spring 프로그래머가 직면 한 구성 악몽에서 벗어날 수있는 고유 한 데이터 액세스 레이어를 만드십시오. MongoTemplate많은 유연성이 있기 때문에 자신의 수업과 상호 작용을 편안하게 설계하는 엔지니어에게 정말 구세주입니다. 구조는 다음과 같을 수 있습니다.

  1. MongoClientFactory응용 프로그램 수준에서 실행되고 MongoClient개체를 제공 할 클래스를 만듭니다 . 이것을 Singleton으로 구현하거나 Enum Singleton을 사용하여 구현할 수 있습니다 (스레드 안전).
  2. 각 도메인 개체에 대한 데이터 액세스 개체를 상속 할 수있는 데이터 액세스 기본 클래스를 만듭니다. 기본 클래스는 특정 메서드가 모든 DB 액세스에 사용할 수있는 MongoTemplate 객체를 생성하는 메서드를 구현할 수 있습니다.
  3. 각 도메인 개체의 각 데이터 액세스 클래스는 기본 메서드를 구현하거나 기본 클래스에서 구현할 수 있습니다.
  4. 그런 다음 Controller 메서드는 필요에 따라 Data 액세스 클래스의 메서드를 호출 할 수 있습니다.

안녕하세요 @rameshpa 나는 같은 프로젝트에서 MongoTemplate 및 저장소를 모두 사용할 수 있습니다 .. 그것을 사용할 수 있습니까?
Gauranga

1
구현할 수있는 MongoTemplate은 리포지토리에서 사용하는 연결과 다른 DB 연결을 갖습니다. 원자 성은 문제가 될 수 있습니다. 당신이 순서의 요구가있는 경우 또한 나는 하나 개의 스레드에 두 개의 서로 다른 연결을 사용하는 것이 좋습니다 않을 것이다
rameshpa을

24

FWIW, 다중 스레드 환경의 업데이트 관련 :

  • MongoTemplate제공 "원자"아웃 - 오브 - 박스 작업 updateFirst , updateMulti, findAndModify, upsert... 당신은 한 번의 작업으로 문서를 수정할 수 있습니다. Update이러한 방법에 의해 사용되는 객체는 만 관련 분야를 대상으로 할 수 있습니다 .
  • MongoRepository단지 당신에게 제공 기본 CRUD 작업을 find , insert, save, delete, 포함하여 POJO로 작동하는 모든 필드를 . 이렇게하면 여러 단계 (1. 업데이트 할 문서 find, 2. 반환 된 POJO에서 관련 필드 수정, 3. 수정)에서 문서를 업데이트 save하거나를 사용하여 직접 업데이트 쿼리를 정의해야합니다 @Query.

예를 들어 여러 REST 엔드 포인트가있는 Java 백엔드와 같은 다중 스레드 환경에서는 두 개의 동시 업데이트가 서로의 변경 사항을 덮어 쓸 가능성을 줄이기 위해 단일 방법 업데이트를 사용하는 것이 좋습니다.

예 : 다음과 같은 문서가 주어 { _id: "ID1", field1: "a string", field2: 10.0 }지고 두 개의 다른 스레드가 동시에 업데이트합니다.

다음과 MongoTemplate같이 보일 것입니다.

THREAD_001                                                      THREAD_002
|                                                               |
|update(query("ID1"), Update().set("field1", "another string")) |update(query("ID1"), Update().inc("field2", 5))
|                                                               |
|                                                               |

그리고 문서의 최종 상태는 항상 { _id: "ID1", field1: "another string", field2: 15.0 }각 스레드가 DB에 한 번만 액세스 하고 지정된 필드 만 변경되기 때문입니다.

와 동일한 케이스 시나리오 MongoRepository는 다음과 같습니다.

THREAD_001                                                      THREAD_002
|                                                               |
|pojo = findById("ID1")                                         |pojo = findById("ID1")
|pojo.setField1("another string") /* field2 still 10.0 */       |pojo.setField2(pojo.getField2()+5) /* field1 still "a string" */
|save(pojo)                                                     |save(pojo)
|                                                               |
|                                                               |

최종 문서 는 DB에 마지막 으로 도달하는 작업 에 따라 { _id: "ID1", field1: "another string", field2: 10.0 }또는 (참고 : 주석 에서 제안한대로 SpringData 주석 을 사용하더라도 크게 변경되지는 않습니다. 작업 중 하나 가를 throw 하고 최종 문서는 여전히 위의 하나이며 둘 다 대신 하나의 필드 만 업데이트됩니다. ){ _id: "ID1", field1: "a string", field2: 15.0 }save
@VersionsaveOptimisticLockingFailureException

따라서 매우 정교한 POJO 모델이 있거나 어떤 이유로 든 의 사용자 지정 쿼리 기능이 필요하지 않는 한 MongoTemplate이것이 더 나은 옵션 이라고 말하고 싶습니다 MongoRepository.


좋은 점 / 예. 그러나 경쟁 조건 예제와 원하지 않는 결과는 바로 그 시나리오를 방지하기 위해 @Version을 사용하여 피할 수 있습니다.
Madbreaks

@Madbreaks 이것을 달성하는 방법에 대한 리소스를 제공 할 수 있습니까? 공식 문서는 아마?
Karthikeyan

@Version 주석에 대한 Spring 데이터 문서 : docs.spring.io/spring-data/mongodb/docs/current/reference/html/…
Karim Tawfik

1
@Madbreaks 지적 해 주셔서 감사합니다. 예, @Version두 번째 스레드가 첫 번째 스레드가 저장 한 데이터를 덮어 쓰는 것을 "방지"합니다. 업데이트를 폐기하고 OptimisticLockingFailureException대신 던진다는 의미에서 "방지" 합니다. 따라서 업데이트가 성공하려면 재시도 메커니즘을 구현해야합니다. MongoTemplate을 사용하면 전체 시나리오를 피할 수 있습니다.
walen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.