Apache Geodeが1.0.0-incubatingとしてリリースされていますが、合わせてSpring Data Geodeも
1.0.0.INCUBATING-RELEASEになっています。
とはいえ、Spring Data Geode用のページはなくて、相変わらずSpring Data Gemfireな
わけですが。
Spring Data for Pivotal GemFire
Spring Data Geodeは、Spring Data GemfireのGitHubリポジトリ内で、別ブランチとして
管理されています。
GitHub - spring-projects/spring-data-gemfire at apache-geode
ドキュメント(Spring Data Gemfire)はこちら。
Spring Data GemFire Reference Guide
今回、Apache Geodeが1.0.0-incubatingになって初めてSpring Data Geode含めて扱うので、
まずは軽く試してみたいと思います。
本エントリとしてのお題は、
- Apache GeodeはClient/Server Modeで動かす
- Spring Data GeodeをSpring Bootを含めて起動できる環境を作る
- Spring Data Geodeで、GemfireRepositoryを使ってみる(Queryは含まない)
とします。要するに、Hello Worldレベルですね。
めっちゃハマりましたが。
では、いってみたいと思います。
準備
アプリケーション側(Maven)
Mavenの依存関係などは、こんな感じ。
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.boot.version>1.4.4.RELEASE</spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-geode</artifactId> <version>1.0.0.INCUBATING-RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Spring Bootは、1.4.4.RELEASEとしました。最新の1.5.1.RELEASEではありません。
また、Spring Data Geodeにはstarterがないので、直接Spring Data Geodeを依存関係に足しています。
Apache GeodeのLocator/Serverの準備
LocatorとServerをそれぞれ起動しておきます。今回は、ひとつずつ起動しておくものとします。
Cacheの設定は、以下のようにしておきました。
cache.xml
<?xml version="1.0" encoding="UTF-8"?> <cache xmlns="http://geode.apache.org/schema/cache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd" version="1.0"> <region name="myRegion" refid="PARTITION_REDUNDANT"> </region> </cache>
Locator、Server、そしてRegion。
gfsh>list members Name | Id -------------------- | -------------------------------------------------------- locator-geodelocator | 172.19.0.2(locator-geodelocator:35:locator)<ec><v0>:1024 server-1fcdc39cf249 | 172.19.0.3(server-1fcdc39cf249:154)<v3>:1024 gfsh>list regions List of regions --------------- myRegion
アプリケーションの作成
では、Spring Data Geodeで作成するアプリケーションを作っていきます。
最初に、Apache Geodeに保存するエンティティを作成。お題は書籍とします。
src/main/java/org/littlewings/geode/spring/Book.java
package org.littlewings.geode.spring; import java.io.Serializable; import org.springframework.data.annotation.Id; public class Book implements Serializable { private static final long serialVersionUID = 1L; @Id private String isbn; private String title; private Integer price; public Book(String isbn, String title, Integer price) { this.isbn = isbn; this.title = title; this.price = price; } public Book() { } // getter/setterは省略 }
Serializableを実装し、Spring Dataの@Idを付与している以外はふつうのJavaBeansです。
次に、このBookクラスを使うRepositoryを作成。今回は、GemfireRepositoryインターフェースを継承しているだけで、
追加メソッドは作成しません。また、@Regionでエンティティの保存先のRegionを指定しておきます。
src/main/java/org/littlewings/geode/spring/BookRepository.java
package org.littlewings.geode.spring; import org.springframework.data.gemfire.mapping.Region; import org.springframework.data.gemfire.repository.GemfireRepository; @Region("myRegion") public interface BookRepository extends GemfireRepository<Book, String> { }
Spring Data Geodeの設定。@EnableGemfireRepositoriesアノテーションを付与するとともに、ClientCacheとRegionの
Bean定義を行います。
src/main/java/org/littlewings/geode/spring/GeodeConfig.java
package org.littlewings.geode.spring; import org.apache.geode.cache.client.ClientCache; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.data.gemfire.client.ClientCacheFactoryBean; import org.springframework.data.gemfire.client.ClientRegionFactoryBean; import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; @Configuration @EnableGemfireRepositories public class GeodeConfig { @Bean public ClientCacheFactoryBean geodeCache() throws Exception { ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean(); clientCacheFactory.setCacheXml(new ClassPathResource("client-cache.xml")); clientCacheFactory.afterPropertiesSet(); return clientCacheFactory; } @Bean public ClientRegionFactoryBean<String, Book> region(ClientCache cache) throws Exception { ClientRegionFactoryBean<String, Book> clientRegionFactory = new ClientRegionFactoryBean<>(); clientRegionFactory.setCache(cache); clientRegionFactory.setRegionName("myRegion"); clientRegionFactory.afterPropertiesSet(); return clientRegionFactory; } }
Cacheの設定は、Cache XMLで指定するようにしました。
clientCacheFactory.setCacheXml(new ClassPathResource("client-cache.xml"));
また、RegionのBean定義はしておかないと、RepositoryがRegionを参照できなくてエラーになります…。
Cache XMLを使用しているからといって、Region自体のBeanの定義は省略できず行っておく必要があります、と。
ところで、Apache Geodeは1.0.0-incubatingになるに伴いパッケージ名が「org.apache.geode」になったの
ですが、Spring Data Geodeは未だにいろいろとGemfireなので混乱しますね?
Spring Boot有効化のためのクラス。特にすることはありません。
src/main/java/org/littlewings/geode/spring/App.java
package org.littlewings.geode.spring; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication public class App { }
ClientCacheの設定
先ほどBean定義で参照していた、Cache XMLは以下のように定義しました。
src/test/resources/client-cache.xml
<?xml version="1.0" encoding="UTF-8"?> <client-cache xmlns="http://geode.apache.org/schema/cache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd" version="1.0"> <pool name="client-pool" subscription-enabled="true"> <locator host="localhost" port="10334"/> </pool> <region name="myRegion" refid="PROXY"> <region-attributes pool-name="client-pool"/> </region> </client-cache>
単純にServerに接続しにいくRegion定義です。
テストコード
では、テストコードを書いて確認してみます。
テストコードの雛形としては、以下のようなものを用意。
src/test/java/org/littlewings/geode/spring/SimpleDataGeodeTest.java
package org.littlewings.geode.spring; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.apache.geode.cache.client.ServerOperationException; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @RunWith(SpringRunner.class) @SpringBootTest public class SimpleDataGeodeTest { @Autowired BookRepository bookRepository; // ここに、テストを書く! }
作成したRepositoryは、@Autowiredでインジェクションしています。
まずは基本的なCRUD。
@Test public void gettingStarted() { Book book = new Book("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4320); // 保存 bookRepository.save(book); // 1件取得 Book foundBook = bookRepository.findOne("978-4798142470"); assertThat(foundBook.getTitle()) .isEqualTo("Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発"); assertThat(foundBook.getPrice()) .isEqualTo(4320); // 削除 bookRepository.delete("978-4798142470"); // 確認 assertThat(bookRepository.findOne("978-4798142470")) .isNull(); assertThat(bookRepository.count()) .isZero(); }
findAllなどの、Iterableを使うもの。
@Test public void iterable() { List<Book> books = Arrays.asList( new Book("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4320), new Book("978-4774182179", "[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ", 4104), new Book("978-4777519699", "はじめてのSpring Boot―スプリング・フレームワークで簡単Javaアプリ開発", 2700) ); // 複数保存 bookRepository.save(books); // 全件保存 Iterable<Book> foundBooks = bookRepository.findAll(); List<Book> foundBooksAsList = (List<Book>) foundBooks; assertThat(foundBooksAsList) .hasSize(3); assertThat(foundBooksAsList.stream().map(b -> b.getIsbn()).collect(Collectors.toList())) .containsAll(books.stream().map(b -> b.getIsbn()).collect(Collectors.toList())); // 削除 bookRepository.delete(books); // 確認 assertThat(bookRepository.count()) .isZero(); }
deleteAllはサポートしていなかったりします。
@Test public void deleteAllUnsupported() { assertThatThrownBy(() -> bookRepository.deleteAll()) .isInstanceOf(DataAccessResourceFailureException.class) .hasCauseInstanceOf(ServerOperationException.class); // さらにcauseを見るとUnsupportedOperationException }
とりあえず、OKそうですね。
ハマったこと
それはもうハマりました。
まあ、ハマったといっても1点だけで、Spring Bootを1.5.1.RELEASEでSpring Data Geodeを合わせようとするとRepositoryにうまく
DIできなくなるようで、こんな感じでコケてしまいます…。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'region' defined in class path resource [org/littlewings/geode/spring/GeodeConfig.class]: Unsatisfied dependency expressed through method 'region' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bookRepository': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
Spring Bootのバージョンを下げるとうまく動くのですが、それに気づくまでにかなり時間がかかりました…。
なお、原因自体はちゃんと追えてません…。
というか
Spring Data GeodeをSpring Bootのstarterに加えて欲しいんですけど、どうやらそうもいかない様子。
うーん。
ホント、Spring Data Geodeって今後どうなるんでしょ…。
まとめ
とりあえず触りだけですが、Spring Data Geodeの1.0.0.INCUBATING-RELEASEを試してみました。
Spring BootとSpring Data GeodeのRepositoryの関連にだいぶハマりましたが、最低限動いたので…とは言いきりにくい
ところがあります。
SpringのCache Abstractionとも、そのうち組み合わせてみようかな?(こっちは、Spring Boot 1.5.1.RELEASEで
動いて欲しい)
あと、Queryとかも試しておきたいですね。
まずはこんなところで。