ref. https://stackoverflow.com/questions/17008947/whats-the-difference-between-spring-datas-mongotemplate-and-mongorepository
스택 오버플로우의 답변을 번역함
repository는 template보다 편리하지만 template은 실행할 질의에 대해 더 세부적인 제어가 가능하다.
여러 스프링 데이터 모듈에 대해 repository 프로그래밍 모델을 사용할 수 있기때문에 spring-data-mongodb reference에 대한 자세한 내용을 확인 할 수 있다.
reference에서는 다음과 같이 설명한다.
JpaRepository나 MongoRepository와 같은 persistence 기술에 대한 특성화된 추상화를 제공한다. 이러한 인터페이스는 CrudRepository를 확장하고 CrudRepository와 같은 다소 일반적인 persistence 기술-불가지론(agnostic)의 인터페이스 외에도 기본적인 persistence 기술의 기능을 사용한다.
일반적으로 다음과 같은 접근법을 권장한다.
✔️코드 예시
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에 들어간다.
MongoTemplate은 "원자성"을 즉시 사용할 수 있는 연산자인 updateFirst
, updateMulti
, findAndModify
, upsert
... 등 단일 작업에서 문서를 수정할 수 있는 기능들을 제공한다. 또한 이런 메소드에 사용되는 업데이트 개체를 사용해 관련 필드만 대상으로 지정할 수 있다.
MongoRepository는 모든 필드를 포함하는 POJO와 함께 작동하는 기본적인 CRUD 작업만 제공한다. 이렇게하면 여러 단계로 문서를 업데이트하거나 (1. 업데이트 할 문서 찾기, 2. 반환된 POJO에서 관련 필드를 수정한 다음 3. 저장) @Query
를 사용해 직접 업데이트 쿼리를 정의해야 한다.
여러 REST 엔드포인트가 있는 Java 백엔드 같은 다중 스레드 환경에서는 동시에 일어나는 두 개의 업데이트가 서로 다른 변경 사항을 덮어쓸 가능성을 줄이기 위해 단일 메소드 업데이트를 사용해야한다. (MongoTemplate의 update기능은 변화할 필드만 지정해서 변경할 수 있다.)
예를 들어 { _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))
| |
| |
field1
과 field2
에 각각 업데이트가 되기 때문에 결과는 항상 { _id: "ID1", field1: "another string", field2: 15.0 }
이다.
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)
| |
| |
결과적으로 { _id: "ID1", field1: "another string", field2: 10.0 }
혹은 { _id: "ID1", field1: "a string", field2: 15.0 }
로 변경될 것이다. (save는 lock되지 않기 때문에 두 스레드 모두 수정전 데이터인 { _id: "ID1", field1: "a string", field2: 10.0 }
를 find 해온다.)