LoginSignup
6
4

More than 3 years have passed since last update.

Cassandra×SpringBoot 奮闘記録

Last updated at Posted at 2020-01-21

この記事の概要

  • Cassandraに触れたことがなかったので、構築〜SpringBootのアプリケーションから参照・更新をすることろまでをやってみたときの記録
  • 環境はローカルPC(Mac)

参考

Cassandra: http://cassandra.apache.org/doc/latest/
SpringDataCassandra: https://spring.io/projects/spring-data-cassandra
property設定項目: https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#data-properties

Cassandraの構築

インストール

# Cassandra本体
brew install cassandra
# CQL(Cassandra Query Language)
brew install cql

起動

cassandra -f

Connection error: ('Unable to connect to any servers', {'127.0.0.1': error(61, "Tried connecting to [('127.0.0.1', 9042)]. Last error: Connection refused")})

動かん。。。
僕の場合はJAVA_HOMEがJava11のディレクトリを指しているのがいけなかったようです。。。
定義し直したら無事起動!

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/
cassandra -f

どうせならHomebrewで起動!

vi /usr/local/etc/cassandra/cassandra-env.sh
# 末尾に「export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/」を追記

brew services start cassandra

接続!

cqlsh

cqlsh localhost 9042
これと一緒

ちょっとCassandra単体で遊んでみる

cqlsh

ここから再開
こまかいオプションの説明はここでは省略。。。

KeySpaceの追加

MySQLやOracleでいうDatabaseに当たる概念

-- # keyspace追加前
DESCRIBE keyspaces
-- system_traces  system_schema  system_auth  system  system_distributed

-- # 「sample」 keyspace追加
CREATE KEYSPACE sample WITH REPLICATION = {'class':'SimpleStrategy','replication_factor':1};

-- # keyspace追加前
DESCRIBE keyspaces
-- system_schema  system_auth  system  sample  system_distributed  system_traces

ちなみに削除は

DROP KEYSPACE sample;

Databaseの移動

USE sample

テーブルの作成

ここではシンプルにKeyとValueを持つデータを想定

-- 作成
CREATE TABLE t_sample (key text PRIMARY KEY, value text);

-- 一覧確認
DESCRIBE tables;

-- テーブル定義の確認
DESCRIBE table t_sample;

ちなみに削除は

DROP TABLE t_sample;

データ操作

先ほど作成したt_sampleテーブルを使って一通り

-- 参照(空っぽ)
select * from t_sample;

-- 参照(上と同等)
select key, value from t_sample;

-- 登録
INSERT INTO t_sample (key, value) VALUES ('key1', 'value1');

-- 登録(重複)
-- クエリは成功して、updateのような動きになっている
INSERT INTO t_sample (key, value) VALUES ('key1', 'value2');

-- 更新
UPDATE t_sample SET value = 'value3' WHERE key = 'key1';

-- 削除
DELETE FROM t_sample WHERE key = 'key1';

primary違反したら弾かれるんじゃなくてupdateされるのは初体験

Springアプリケーションの構築

project作成

build.gradle
plugins {
    id 'org.springframework.boot' version '2.2.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-cassandra'
    // webfluxは動作確認用です
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    compileOnly('org.projectlombok:lombok')
    annotationProcessor('org.projectlombok:lombok')
}
application.yml
spring:
  data:
    cassandra:
      keyspace-name: sample

テーブルのモデルクラスを作成

Sample.java
import lombok.Value;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;

@Data
@Table("t_sample")
public class Sample {
    @PrimaryKey
    private String key;
    private String value;
}

登録・参照・削除をしてみる

Application.java
@Slf4j
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        //SpringApplication.run(Application.class);

        Cluster cluster = Cluster.builder()
                .addContactPoints("localhost")
                .withoutJMXReporting()  // 付けないとエラー
                .build();
        Session session = cluster.connect("sample");

        var template = new CassandraTemplate(session);
        var data = new Sample("key1", "value1");

        template.insert(data);
        // 全件検索
        var selected = template.select("SELECT * from t_sample", Sample.class);
        log.info("selected: {}", selected);
        // 更新
        data = new Sample("key1", "value2");
        template.update(data);
        // 条件付き検索
        selected = template.select(Query.query(Criteria.where("key").is("key1")), Sample.class);
        log.info("selected: {}", selected);
        // 削除(PK指定)
        template.deleteById("key1", Sample.class);
        // 件数取得
        var count = template.count(Sample.class);
        log.info("count: {}", count);

        // 接続クローズ
        session.close();
        cluster.close();
    }
}

.withoutJMXReporting() については記述しておかないと以下のエラーが発生した

Exception in thread "main" java.lang.NoClassDefFoundError: com/codahale/metrics/JmxReporter

Webアプリケーションっぽくしてみる

まずは「spring-boot-starter-data-cassandra」がどこまでBean登録してくれるのか調査

Cluster

  • org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfigurationがBean登録している
  • spring.data.cassandra配下のpropertiesを設定すれば良さそう
  • spring.data.cassandra.keyspace-name=sampleとか

Session

  • SessionというかSessionFactoryなのかな?
  • org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfigurationがBean登録している
  • こちらも同様にspring.data.cassandra配下の設定が反映されそう

CassandraTemplate

  • Session同様、org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfigurationがBean登録している

結論、、、
いきなりCassandraTemplate使うでOK!

CassandraController.java
@RequiredArgsConstructor
@RestController
public class CassandraController {
    private final CassandraTemplate template;

    // curl localhost:8080
    @GetMapping("")
    public List<Sample> all() {
        return template.select("SELECT * FROM t_sample", Sample.class);
    }

    // curl localhost:8080/key1
    @GetMapping("/{key}")
    public Sample getByKey(@PathVariable String key) {
        return template.selectOneById(key, Sample.class);
    }

    // curl localhost:8080/insert -d '{"key":"key1","value":"value1"}' -X PATCH -H 'Content-Type: application/json'
    @PatchMapping("/**")
    public Sample patch(@RequestBody Sample sample) {
        return template.insert(sample);
    }

    // curl localhost:8080/key1 -X DELETE
    @DeleteMapping("/{key}")
    public Boolean delete(@PathVariable String key) {
        return template.deleteById(key, Sample.class);
    }
}

Reactive対応

ReactiveCassandraTemplateを使うだけ!
返り値の型をFlux or Monoにするもの忘れずに!(一括でPublisherとしてもOK)

ReactiveCassandraController.java
@RequiredArgsConstructor
@RestController
@RequestMapping("/reactive")
public class ReactiveCassandraController {
    private final ReactiveCassandraTemplate template;

    // curl localhost:8080/reactive
    @GetMapping("")
    public Flux<Sample> all() {
        return template.select("SELECT * FROM t_sample", Sample.class);
    }

    // curl localhost:8080/reactive/key1
    @GetMapping("/{key}")
    public Mono<Sample> getByKey(@PathVariable String key) {
        return template.selectOneById(key, Sample.class);
    }

    // curl localhost:8080/reactive/insert -d '{"key":"key1","value":"value1"}' -X PATCH -H 'Content-Type: application/json'
    @PatchMapping("/**")
    public Mono<Sample> patch(@RequestBody Sample sample) {
        return template.insert(sample);
    }

    // curl localhost:8080/reactive/key1 -X DELETE
    @DeleteMapping("/{key}")
    public Mono<Boolean> delete(@PathVariable String key) {
        return template.deleteById(key, Sample.class);
    }

}
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4