2013년 7월 20일 토요일

Spring-Data-Mongo 이용하기

NoSQL DB 중 범용성이 좋은 MongoDB 를 사용하려면 여러가지 방법이 있습니다

1. Hibernate-OGM for MongoDB
2. Spring-Data MongoDB 

1번은 hibernate-core, hibernate-search, hibernate-ogm 을 활용하여 검색 시스템을 만들어봐서, 이번에는  spring-data-mongodb 를 사용해보기로 했습니다.

예전에 spring-data-jpa 에서 repository 의 concrete class 를 안 만들고, 동적으로 생성해서 사용하는 방법을 보고, 와 정말 이렇게 되면 코딩량이 엄청 줄어들겠다 싶더군요...
그래서 이번에 spring-data-mongo 의 예제를 보고, 제 나름대로 다시 구성해 봤습니다.

spring-data 의 여러가지 모듈 사용법이 더 좋고, 생산성이 높다면, 앞으로는 이 것을 계속 사용하려고 합니다...

우선 spring-data-mongo 를 사용하기 위해  dependency에 다음을 추가합니다.

pom.xml

<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<!-- Spring Data MongoDB -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<!-- QueryDSL for MongoDB -->
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
</dependency>
<!-- QueryDSL apt -->
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
view raw pom.xml hosted with ❤ by GitHub

다음으로 도메인 모델로 Album class 를 정의합니다.

Album.java
@Document
@Getter
@Setter
public class Album extends ValueObjectBase {
@Id
private ObjectId id;
private String title;
private String artist;
private List<Track> tracks = new ArrayList<Track>();
public Album(String title, String artist) {
Guard.shouldNotBeEmpty(title, "title");
Guard.shouldNotBeEmpty(artist, "artist");
this.title = title;
this.artist = artist;
}
public void add(Track tract) {
this.tracks.add(tract);
}
@Override
public int hashCode() {
return HashTool.compute(id);
}
@Override
public Objects.ToStringHelper buildStringHelper() {
return super.buildStringHelper()
.add("id", id)
.add("title", title)
.add("artist", artist);
}
private static final long serialVersionUID = 958798390838794475L;
}
view raw Album.java hosted with ❤ by GitHub
@Document 는 org.springframework.data.mongodb.core.mapping.Document 입니다.
이 어노테이션이 정의되면, MongoDB 의 Document 로 선언한 것입니다.

다음은 Album용 Repository를 정의합니다.

AlbumRepository.java
package kr.hconnect.mongo.test.music.model;
import org.bson.types.ObjectId;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Simple repositories interface to manage {@link Album} instances.
*
* @author 배성혁 sunghyouk.bae@gmail.com
* @since 13. 7. 20. 오후 4:16
*/
@Repository
public interface AlbumRepository extends CrudRepository<Album, ObjectId>, QueryDslPredicateExecutor<Album> {
List<Album> findByTracksName(String name);
List<Album> findByTracksNameLike(String name);
List<Album> findByTracksRatingGreaterThan(Stars rating);
}


테스트용 환경 설정은 다음과 같이 합니다.
MongoConfiguration.java
package kr.hconnect.mongo.test.music;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import kr.hconnect.mongo.test.music.model.AlbumRepository;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* kr.hconnect.mongo.test.music.MongoConfiguration
*
* @author 배성혁 sunghyouk.bae@gmail.com
* @since 13. 7. 20. 오후 4:34
*/
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
/** 이게 있어야 Repository 의 Concrete Class를 생성해줍니다.*/
@EnableMongoRepositories(basePackageClasses = { AlbumRepository.class }, repositoryImplementationPostfix = "Impl")
//@ComponentScan(basePackageClasses = { AlbumRepository.class })
public class MongoConfiguration extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "musicDB";
}
@Override
public Mongo mongo() throws Exception {
return new MongoClient("localhost");
}
}

주의할 점은 Repository 들을 실제 구현한 것이 아니라, Spring 이 동적으로 구현할 수 있도록 @EnableMongoRepositories 를 선언해 줘야 한다는 것입니다.

마지막으로 테스트 코드는 다음과 같습니다.

AlbumRepositoryIntegrationTest.java
package kr.hconnect.mongo.test.music.repositories;
import kr.hconnect.mongo.test.music.AbstractIntegrationTest;
import kr.hconnect.mongo.test.music.MongoConfiguration;
import kr.hconnect.mongo.test.music.model.Album;
import kr.hconnect.mongo.test.music.model.AlbumRepository;
import kr.hconnect.mongo.test.music.model.Stars;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
/**
* kr.hconnect.mongo.test.music.repositories.AlbumRepositoryIntegrationTest
*
* @author 배성혁 sunghyouk.bae@gmail.com
* @since 13. 7. 20. 오후 4:53
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MongoConfiguration.class })
public class AlbumRepositoryIntegrationTest extends AbstractIntegrationTest {
@Autowired
AlbumRepository repository;
@Before
public void purgeRepository() {
repository.deleteAll();
super.setup();
}
@Test
public void createAlbum() throws Exception {
repository.save(albums);
assertSingleGruxAlbum(repository.findOne(bigWhiskey.getId()));
}
@Test
public void findsAlbumByConcreteTrackName() throws Exception {
repository.save(albums);
assertSingleGruxAlbum(repository.findByTracksName("Grux"));
List<Album> albums = repository.findByTracksName("Foo");
assertThat(albums.isEmpty()).isTrue();
}
@Test
public void findsAllAlbumsByTrackNameLike() throws Exception {
repository.save(albums);
assertBothAlbums(repository.findByTracksNameLike("*it*"));
}
@Test
public void findsAlbumsByTrackRating() throws Exception {
bigWhiskey.getTracks().get(4).setRating(Stars.FOUR);
repository.save(albums);
assertSingleGruxAlbum(repository.findByTracksRatingGreaterThan(Stars.THREE));
List<Album> albums = repository.findByTracksRatingGreaterThan(Stars.FOUR);
assertThat(albums.isEmpty()).isTrue();
}
private void assertSingleGruxAlbum(List<Album> albums) {
Assert.assertThat(albums, CoreMatchers.is(CoreMatchers.notNullValue()));
Assert.assertThat(albums.size(), CoreMatchers.is(1));
Assert.assertThat(albums.get(0), CoreMatchers.is(CoreMatchers.notNullValue(Album.class)));
assertSingleGruxAlbum(albums.get(0));
}
}

를 구현하면 됩니다.
나머지 코드는  Spring-Data Mongo 의 예제에 있습니다. 그 예제는 xml 로 환경설정을 하고, 몇가지 제가 필요없는 코드를 제거했습니다.

시간나면 github에 따로 분리해서 올리도록 해보겠습니다...


댓글 없음: