실제로 ORM에 익숙한 사람이라면 foreignKey를 하나의 객체로 넣는 것이 익숙한 사람이 많을 것이다.
아래의 예시를 보자.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(nullable = false, length = 100)
private String title;
@ColumnDefault("0")
private int count;
@ManyToOne
@JoinColumn(name="userId");
private User user;
@CreationTimestamp
private Timestamp createDate;
}
// [출처] Spring boot JPA로 foreign key주기|작성자 YH
이때 Board는 게시판이고, 해당 게시판은 User를 참조하고 있다. 특히 User의 userId로 참조키로 활용했다는 지점을 볼 수 있다.
만일 해당 게시판을 RDB에 넣고 싶다면 아래와 같은 코드로 작성하곤 한다.
String userId = "ihateMongo"
User user = new User(userId);
Board board = new Board("잠이 온다....자고 싶다...")
board.setUser(user);
board.setBoardId("게시판");
boardrepository.save(board);
Board board = boardrepository.findById("게시판");
System.out.println(board.getUser().getUserId);
# 출력값 : "ihateMongo"
이때 위의 Entity에 설정한 userId 값을 @JoinColumn으로 설정했기 때문에 User객체가 아닌, userId로 RDB에 저장되게 된다.
하지만 spring에서 가져올 때는 getUser를 하는 것만으로도 참조된 User 객체의 모든 값을 가져올 수 있기 때문에 굉장히 사용하기에 유리하다고 볼 수 있다. (굳이 Join을 직접 사용하지 않을 수 있다는 점에서 강력하다!)
그렇다면 MongoDB는?
결론부터 말하자면 select의 과정에서는 JPA처럼 가져올 수 있지만, create의 과정에서는 일일히 참조하는 키를 넣어줘야 하기 때문에 같은 방식으로 할 수 없다. 다만 MongoTemplate를 통해서 쿼리를 직접 활용하여 select문을 작성해야 하기 때문에 불편한 것은 매한가지다..
하지만 이전 게시물에서 무조건 lookup를 담을 수 있는 Dto를 설정해줘야 했는데, 사실 보니 꼭 그렇지 않아도 되었다는 점에서 큰 발견이다.
@Document
@Getter
@Setter
@NoArgsConstructor
public class Cafe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
@Column(unique = true, nullable = false, length = 20)
private String cafeName;
@Column(nullable = false)
private GeoJsonPoint coordinate;
@DBRef
private List<CafeOperatingTime> cafeOperatingTimeList;
@DBRef
private List<Reserved> reservedList;
@DBRef
private List<Matching> matchingList;
@DBRef
private List<Table> tableList;
@DBRef
private List<TableClass> tableClassList;
}
만일 @DBRef를 활용할 경우, 해당 column으로 직접 set() 함수를 활용하지 않는 이상 MongoDB에 실제로 들어가지 않는다!
아래의 예시를 통해서 setReserved()를 통해서 save 한 것과, 보통의 경우로 save한 것인데 DB 에 저장되는 방식이 다른 것을 알 수 있다.
다만 가져올 때에는 List<> 형태로 배출하기 때문에 꼭 List<foreingKey>가 되는 칼럼으로 받아야 한다.
// Cafe에서 활용한 함수
@Override
public Cafe findByIdwithOperatingTime(String cafeId) {
Criteria criteria = Criteria.where("id").is(cafeId);
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.lookup("cafeOperatingTime", "cafeId", "id", "cafeOperatingTime"),
Aggregation.project("cafeId", "cafeName", "cafeImg")
.andExpression("cafeOperatingTime").as("cafeOperatingTimeList")
);
Cafe cafe = mongoTemplate.aggregate(aggregation, Cafe.class, Cafe.class).getUniqueMappedResult();
return cafe;
}
// Reserved 에서 활용한 함수
@Override
public Reserved findNearestReserved(String userId, LocalDateTime time) {
Criteria criteria = Criteria.where("userId").is(userId).and("reservedStatus").is("VALID").and("reservedEndTime").gte(time);
AggregationOperation match = Aggregation.match(criteria);
AggregationOperation lookupTableClass = Aggregation.lookup("tableClass", "id", "tableClassId", "tableClass");
AggregationOperation lookupCafe = Aggregation.lookup("cafe", "id", "cafeId", "cafe");
AggregationOperation project = Aggregation.project("id", "userId", "cafeId", "tableId", "reservedStartTime", "reservedEndTime", "reservedStatus")
.andExpression("cafe").arrayElementAt(0).as("cafe");
AggregationOperation sort = Aggregation.sort(Sort.Direction.ASC, "reservedStartTime");
AggregationOperation limit = Aggregation.limit(1);
Aggregation aggregation = Aggregation.newAggregation(
match,
lookupTableClass,
lookupCafe,
project,
sort,
limit
);
Reserved reserved = mongoTemplate.aggregate(aggregation, Reserved.class,Reserved.class).getUniqueMappedResult();
return reserved;
}
이렇게 되는 경우를 보았다. 중요하게 보아야 할 부분은 아무래도 arrayElementAt(0)에 해당하는 부분인데, 이 부분은 cafe라는 객체를 불러올 수는 있지만 이것이 배열의 형태로 나타난다는 것이 중요하다.
데이터를 어떻게 가져오는가?
참조하는 Document : 참조당하는 Document
1:1 일때 -> [[객체]] (이때, pk는 앞쪽에 있음)
1:다 일때 -> [[객체]]
다:1 일때 -> [객체]
로 받게 된다.
즉, pk가 참조당하는 Document에 존재할 경우에는 @DBref의 칼럼은 객체로만 받을 수 있고, pk가 참조하는 쪽에 있다면 List<객체>로 받으면 된다는 것이다.
하지만 어느 경우에던 List가 쌓여 있는 것은 동일하기 때문에 arrayElementAt(0) 함수가 필요하다.
결론
물론 참조하는 key를 실제 column에 함께 넣어주어야 한다는 점에서
"save(CREATE)"하는 과정에서는 JPA와 확연히 다르게 불편함이 있지만,
"find, get(SELECT)"의 과정에서는 JPA와 동일하게 객체 자체를 가져올 수 있다는 점이 증명되었다!
'MongoDB' 카테고리의 다른 글
MongoDB 에서 lookup을 활용해보자! - with spring boot (0) | 2023.07.02 |
---|---|
MongoDB 의 MongoTemplate 활용하기 - JPA 위에 덧씌우기 (0) | 2023.07.01 |