はじめに
概要
本記事では、Spring BootとSpring Data Couchbaseを使ったアプリケーション開発について紹介します。
イントロダクションとして、できるだけ前提知識を置かず、また徒らに複雑にならない様、動作を確認できるアプリケーションの開発までの流れを整理します。
想定対象読者
対象読者として、例えば以下の様な人を想定しています。
- 開発言語としてJavaを使っており、SpringフレームワークやSpring Bootに関心がある(または使っている)
- リレーショナルデータベース以外のデータベースを開発に使えないかと考えている
- そもそもデータベースに対して選り好みはないが(使ったことがないが)、JAVAのオブジェクトをそのまま保存・再利用できれば良いと思っている
- オブジェクトとリレーショナルデータとのマッピングに嫌気がさしている
- データベースにミリ秒以下の応答性能を求めている
アプリケーション構築手順
初期準備
データベースの準備
以下の記事を参考にしてください(Dockerだけでなく、Mac、Windows、Ubuntuへのインストーラーを使ったインストールについても触れています)。
Dockerでお手軽JSONデータベース体験 ~ Couchbase Server最新版を試す
また、(上記記事を参考に)今回作成するアプリケーション用のバケット(Couchbaseにおけるデータベース)を作成しておきます。
ここではspring_user_mgmt
という名前を用います(簡単なユーザ管理のアプリケーションを作ります)。
開発用インデックス定義
CouchbaseのWEBコンソールでバケットを作成したら、同じWEBコンソールのQueryワークスペースで、開発用のインデックスを作成しておきます。
CREATE PRIMARY INDEX `idx_spring_user_mgmt` ON `spring_user_mgmt`
注意: 本番リリースの際には、インデックスの適切な設計が重要です。ここで利用するインデックスは開発用の簡易な定義であることにご留意ください。
Springプロジェクト生成
Spring Initializrを使って、プロジェクトの雛形を作ります。
右の「Dependencies」に、Spring Data Couchbaseを追加します(他は全てデフォルトのままにしています)。
「Generate」押下によりダウンロードされたファイルを適当な場所に展開します。
依存関係の追加
pom.xmlの<dependencies>
タグの間に以下の要素を追加します。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchbase</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
データベースとの連携
データベース接続設定
抽象クラスAbstractCouchbaseConfiguration
を継承したクラス中で下記の様にCouchbaseへの接続設定を行います。
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
@Configuration
public class Config extends AbstractCouchbaseConfiguration {
@Override
public String getConnectionString() {
return "couchbase://127.0.0.1";
}
@Override
public String getUserName() {
return "Administrator";
}
@Override
public String getPassword() {
return "password";
}
@Override
public String getBucketName() {
return "spring_user_mgmt";
}
}
データベースに保存するクラスの定義
データベースに保存するクラスには@Document
アノテーションを付けます(id
と@Id
にも注目)。
package com.example.demo;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.core.mapping.Document;
@Document
public class User {
@Id
private String id;
private String firstName;
private String lastName;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Repositoryインターフェイスの定義
Springフレームワークの作法に従い、下記の様にRepository
インターフェイスを定義します。実装は自動的に生成されます。
package com.example.demo;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends CouchbaseRepository<User, String> {
Optional<User> findById(String id);
List<User> findByFirstName(String firstName);
List<User> findByEmailLike(String email);
}
Springフレームワークで、Couchbaseデータベースを使うために最低限必要な内容は、基本的に以上です。
動作確認
CommandLineRunner実装クラスの作成
上記作成した内容の動作確認のためにCommandLineRunner
実装クラスを作成します。
init()
で、User
オブジェクトを作成しデータベースへ挿入(Upsert)し、dryrun()
で、データベースからUser
オブジェクトを取得・検索した結果を出力しています。
package com.example.demo;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DemoCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
init();
dryrun();
}
@Autowired
private UserRepository userRepository;
private void init() {
User u1 = createUser("user::0001", "太郎", "佐藤", "taro.sato@demo.com");
userRepository.save(u1);
User u2 = createUser("user::0002", "次郎", "田中", "ziro.tanaka@demo.com");
userRepository.save(u2);
User u3 = createUser("user::0003", "花子", "鈴木", "hanako.suzuki@demo.com");
userRepository.save(u3);
}
private void dryrun() {
Optional<User> userById = userRepository.findById("user::0001");
System.out.println("User get = "+userById.get().getFirstName());
List<User> usersByEmail = userRepository.findByEmailLike("%@demo.com");
System.out.println( "Total # of users by email = "+usersByEmail.size() );
List<User> usersByFirstName = userRepository.findByFirstName("太郎");
System.out.println( "Total # of users by first name = "+usersByFirstName.size() );
}
public static User createUser(String id, String firstName, String lastName, String email) {
User user = new User();
user.setId(id);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setEmail(email);
return user;
}
}
コマンドライン実行
プロジェクトディレクトリで下記の様に実行します。
$ mvn spring-boot:run
起動時の出力の後、下記の様に結果が出力されるはずです。
User get = 太郎
Total # of users by email = 3
Total # of users by first name = 1
Couchbase上のデータ確認
WEBコンソールの「Documents」メニューからバケットの中身を確認した結果は以下の様な画面になります。
ここで注目したいのは、全てのデータ(ドキュメント)が"_class":"com.example.demo.User"
という要素を持っていることです。この_class
要素が暗黙に追加されることにより、User
以外のクラスを定義・保存した場合も、クラス毎のマッピングに煩わされることなく、同じバケットに異なるクラスのデータを保存することができます。
REST APIの公開
CouchbaseとSpringとの連携という意味では本質的ではありませんが、以上、作成・確認したデータ取得・検索操作をREST APIとして公開し、利用するまでを紹介します(詳細な解説は、本旨ではないためコードと実行結果のみの紹介に留めます)。
ソースコード
Controlerクラス定義
package com.example.demo;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@GetMapping("/get/{id:.+}")
public ResponseEntity<Optional<User>> findById(@PathVariable String id) {
return ResponseEntity.ok(userService.findById(id));
}
@GetMapping("/name/{name:.+}")
public ResponseEntity<List<User>> findByFirstName(@PathVariable String name) {
return ResponseEntity.ok(userService.findByFirstName(name));
}
@GetMapping("/email/{email:.+}")
public ResponseEntity<List<User>> findByEmail(@PathVariable String email) {
return ResponseEntity.ok(userService.findByEmailLike("%"+email+"%"));
}
}
Serviceインターフェイス定義
package com.example.demo;
import java.util.List;
import java.util.Optional;
public interface UserService {
Optional<User> findById(String id);
List<User> findByFirstName(String fistName);
List<User> findByEmailLike(String email);
}
Service実装定義
package com.example.demo;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserRepository repo;
@Autowired
public void setUserRepository(UserRepository repo) {
this.repo = repo;
}
@Override
public List<User> findByFirstName(String fistName) {
System.out.println(fistName);
return repo.findByFirstName(fistName);
}
@Override
public Optional<User> findById(String id) {
return repo.findById(id);
}
@Override
public List<User> findByEmailLike(String email) {
return repo.findByEmailLike(email);
}
}
実行結果
最後に
Javaで開発する場合、またSpringフレームワークを用いる場合、必ずしもSpring Data Couchbaseを使わなければならないわけではなく、CouchbaseのJava SDKを用いることも可能です。
また、Spring Data Couchbaseを使ってできることは、今回紹介した範囲に尽きるものではありません。
JSONデータベースCouchbaseを使ったJAVAアプリケーション開発のための導入として、参考にしていただける様でしたら幸いです。