クラウドの普及に伴い、ビッグデータやキーバリュー型データの格納など、ますます活用の機会が広がりつつあるNoSQLデータベース。 第3回は代表的なNoSQLプロダクトであるAmazon DynamoDBやApache Cassandra、Amazon ElastiCacheへアクセスするSpringアプリケーションを開発する方法について、わかりやすく解説します。
本連載では、以下の様なステップで進めています。
前回、Spring Data Cassandraを使ったアプリケーション実装(1) に引き続き、 今回はSpring Data Cassandraを使ってデータベースアクセスする処理等を実装していきます。
アプリケーションコンポーネントの実装に移ります。データ追加・更新処理で画面から受け取るリクエストパラメータクラスを以下の様に作成しています。
リクエストパラメータクラス
package org.debugroom.mynavi.sample.spring.data.cassandra.app.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class SampleModel implements Serializable {
private String samplePartitionColumn;
private String sampleClusterColumn;
private String sampleText;
}
Controllerでは、以下5種類のリクエストを受け取り、ロジックを実行して結果をテンプレートに渡す処理を実装します。
package org.debugroom.mynavi.sample.spring.data.cassandra.app.web;
import org.debugroom.mynavi.sample.spring.data.cassandra.app.model.SampleModel;
import org.debugroom.mynavi.sample.spring.data.cassandra.app.model.SampleModelMapper;
import org.debugroom.mynavi.sample.spring.data.cassandra.domain.service.SampleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class SampleController {
@Autowired
SampleService sampleService;
@RequestMapping(method = RequestMethod.GET, value="findAll")
public String findAll(Model model){
model.addAttribute("mynaviSampleTables", sampleService.getMynaviSampleTables());
return "findAll";
}
@RequestMapping(method = RequestMethod.GET, value="findOne")
public String findOne(SampleModel sampleModel, Model model){
model.addAttribute("mynaviSampleTable", sampleService.getMynaviSampleTable(
SampleModelMapper.mapToMynaviSampleTableKey(sampleModel)));
return "findOne";
}
@RequestMapping(method = RequestMethod.POST, value="add")
public String add(SampleModel sampleModel, Model model){
model.addAttribute("mynaviSampleTable",
sampleService.addMynaviSampleTable(SampleModelMapper.map(sampleModel)));
return "add";
}
@RequestMapping(method = RequestMethod.POST, value = "update")
public String update(SampleModel sampleModel, Model model){
model.addAttribute("mynaviSampleTable",
sampleService.updateMynaviSampleTable(SampleModelMapper.map(sampleModel)));
return "update";
}
@RequestMapping(method = RequestMethod.POST, value = "delete")
public String delete(SampleModel sampleModel, Model model){
model.addAttribute("mynaviSampleTable",
sampleService.deleteMynaviSampleTable(
SampleModelMapper.mapToMynaviSampleTableKey(sampleModel)));
return "delete";
}
}
また、Serviceを呼び出す際にリクエストパラメータオブジェクトをServiceのインプットオブジェクトとなっているCassandraのテーブルクラスやプライマリキークラスへ変換するマッパーをインターフェースで実装します。
package org.debugroom.mynavi.sample.spring.data.cassandra.app.model;
import org.debugroom.mynavi.sample.spring.data.cassandra.domain.model.entity.MynaviSampleTable;
import org.debugroom.mynavi.sample.spring.data.cassandra.domain.model.entity.MynaviSampleTableKey;
public interface SampleModelMapper {
public static MynaviSampleTable map(SampleModel sampleModel){
return MynaviSampleTable.builder()
.mynaviSampleTableKey(MynaviSampleTableKey.builder()
.partitionColumn(sampleModel.getSamplePartitionColumn())
.clusterColumn(sampleModel.getSampleClusterColumn()).build())
.sampleText(sampleModel.getSampleText())
.build();
}
public static MynaviSampleTableKey mapToMynaviSampleTableKey(SampleModel sampleModel){
return MynaviSampleTableKey.builder()
.partitionColumn(sampleModel.getSamplePartitionColumn())
.clusterColumn(sampleModel.getSampleClusterColumn())
.build();
}
}
注釈
Java8からstaticメソッドであれば、インターフェースでも実装できる様になっています。
Serviceクラスでは、以下の通り、CRUD処理をRepositoryを通して実行する処理を実装します。
package org.debugroom.mynavi.sample.spring.data.cassandra.domain.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SampleServiceImpl implements SampleService{
@Autowired
SampleRepository sampleRepository;
@Override
public MynaviSampleTable getMynaviSampleTable(MynaviSampleTableKey mynaviSampleTableKey) {
return sampleRepository.findById(mynaviSampleTableKey).get();
}
@Override
public List<MynaviSampleTable> getMynaviSampleTables() {
List<MynaviSampleTable> mynaviSampleTables = new ArrayList<>();
sampleRepository.findAll().iterator().forEachRemaining(mynaviSampleTables::add);
return mynaviSampleTables;
}
@Override
public MynaviSampleTable addMynaviSampleTable(MynaviSampleTable mynaviSampleTable) {
mynaviSampleTable.setMynaviSampleTableKey(MynaviSampleTableKey.builder()
.partitionColumn(UUID.randomUUID().toString())
.clusterColumn("1")
.build());
return sampleRepository.save(mynaviSampleTable);
}
@Override
public MynaviSampleTable updateMynaviSampleTable(MynaviSampleTable mynaviSampleTable) {
return sampleRepository.save(mynaviSampleTable);
}
@Override
public MynaviSampleTable deleteMynaviSampleTable(MynaviSampleTableKey mynaviSampleTableKey) {
MynaviSampleTable mynaviSampleTable = sampleRepository.findById(mynaviSampleTableKey).get();
sampleRepository.deleteById(mynaviSampleTableKey);
return mynaviSampleTable;
}
}
ここで、CassandraへアクセスするコンポーネントであるSampleRepositoryインターフェースは、以下の様な要領で実装しておく必要があります。
package org.debugroom.mynavi.sample.spring.data.cassandra.domain.repository;
import org.springframework.data.repository.CrudRepository;
import org.debugroom.mynavi.sample.spring.data.cassandra.domain.model.entity.MynaviSampleTable;
import org.debugroom.mynavi.sample.spring.data.cassandra.domain.model.entity.MynaviSampleTableKey;
public interface SampleRepository extends CrudRepository<MynaviSampleTable, MynaviSampleTableKey>{
}
注釈
特に実装クラスも作成せずインターフェースだけで、findAllやsaveメソッドが実行できる理由は、Spring Data Cassandraが、GenericDAOパターンに基づく実装クラスを提供しているためです。 GenericDAOパターンとはJavaの型パラメータを利用した実装で、CRUD処理をJavaのジェネリクス機構を使って実装しておき、テーブルの種類に応じて、 型パラメータを設定することで、汎用的なCRUD共通処理を実装したDAO(DatabaseAccessObject)を作成しておくパターンです。 Spring Data Cassandraに限らず、Spring Data JPAなど類似一連のプロダクトでは、同様にインターフェースを作成するだけで、 基本的なCRUDはほぼ実装せずにデータベースアクセス処理を行うことが可能です。
型パラメータとして指定する、Cassandraのテーブルクラスは以下の要領に則って作成します。
package org.debugroom.mynavi.sample.spring.data.cassandra.domain.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.cassandra.core.mapping.Column;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Table("mynavi_sample_table")
public class MynaviSampleTable implements Serializable {
@PrimaryKey("mynaviSampleTableKey")
private MynaviSampleTableKey mynaviSampleTableKey;
@Column("sample_text")
private String sampleText;
}
package org.debugroom.mynavi.sample.spring.data.cassandra.domain.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@PrimaryKeyClass
public class MynaviSampleTableKey implements Serializable {
@PrimaryKeyColumn(name="partition_column", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String partitionColumn;
@PrimaryKeyColumn(name="cluster_column", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
private String clusterColumn;
}
これでアプリケーションが作成しました。SpringBoot起動クラスを実行し、アプリケーションを実行しましょう。「http://localhost:8080/index.html」へブラウザからアクセスすると以下の様な画面が表示され、以下の5つのサービスが実行できます。
このように、CassandraへCRUD処理するアプリケーションをSpring Data Cassandraを用いて簡単に実装することができます。 Spring Data DynamoDBを使ったアプリケーション実装(2) と比較しても分かる通り、 Amazon DynamoDBやApache CassandraにSpring Dataを使ってアクセスするアプリケーションは、 簡単なCRUD処理であれば、かなり似通ったモデルで実装することが可能です。 実際のアプリケーションでは様々ユースケースに応じて、データモデルを検討しておく必要がありますが、その辺りの実装方法は応用編などで触れればと思います。 また、今回はローカル環境への構築しか行っていませんが、Dockerコンテナイメージを使って、AWS EC2やECS上へCassandraクラスタ環境を 構築することができます。CloudFormationなど基盤環境構築を自動化するサービスと組み合わせた方法も合わせ、ゆくゆくご紹介できればと思いますが、 次回以降は、CP型データベースの代表的プロダクトであるRedis及び、AWSのマネージドサービスとして提供されているAmazon ElastiCacheへ アクセスするアプリケーションをSpring Session及びSpring Data Redisを用いて実装する例をご紹介します。
川畑 光平(KAWABATA Kohei)
某システムインテグレータにて、金融機関システム業務アプリケーション開発・システム基盤担当を経て、現在はソフトウェア開発自動化関連の研究開発・推進に従事。
Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professional等の資格を持ち、アプリケーション基盤・クラウドなど様々な開発プロジェクト支援にも携わる。
2019 APN AWS Top Engineers & Ambassadors 選出。
本連載記事の内容に対するご意見・ご質問は Facebook まで。