👌

Spring Bootを使ってビットコインデータを取得し、MySQLに保存する方法

に公開

Original Article

dev.to
https://dev.to/shelner/how-to-get-bitcoin-data-and-save-it-in-mysql-using-spring-boot-3g1p

参考

GMO Coin API documentation
https://api.coin.z.com/docs/en/#klines

準備

MySQL データベースとテーブルを作成します。

例:

CREATE DATABASE IF NOT EXISTS coin_db;

USE coin_db;

CREATE TABLE IF NOT EXISTS btc_klines (
    id INT AUTO_INCREMENT PRIMARY KEY,
    open_time TIMESTAMP,
    open DECIMAL(20, 8),
    high DECIMAL(20, 8),
    low DECIMAL(20, 8),
    close DECIMAL(20, 8),
    volume DECIMAL(20, 8)
);

依存関係

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20240303</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

JPA 実装のステップバイステップ

1. application.properties

これは DB 接続を構成します。

# MySQL に接続するための URL(ポート番号とデータベース名を含む)
spring.datasource.url=jdbc:mysql://localhost:3306/coin_db
# MySQL データベースのユーザー名
spring.datasource.username=your_username
# MySQL データベースのパスワード
spring.datasource.password=your_password
# エンティティ定義に基づいてテーブルを自動的に作成/更新します
spring.jpa.hibernate.ddl-auto=update

2. Kline.java (エンティティ)

import jakarta.persistence.*;
import java.math.BigDecimal;
import java.sql.Timestamp;

@Entity
@Table(name = "btc_klines")
public class Kline {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "open_time")
    private Timestamp openTime;
    
    private BigDecimal open;
    private BigDecimal high;
    private BigDecimal low;
    private BigDecimal close;
    private BigDecimal volume;
    
    // Getter と Setter
}

3. KlineRepository.java

// このインターフェースにより、基本的な CRUD 操作を手動で実装することなく実行できます
import org.springframework.data.jpa.repository.JpaRepository;

// これは `Kline` エンティティのリポジトリ(データアクセス層)です。
// save、findAll、findById、deleteById などのメソッドを継承します。
public interface KlineRepository extends JpaRepository<Kline, Long> {
    // JpaRepository<T, ID>:
    // - T はエンティティ型(Kline)です。
    // - ID は主キーの型(long)です。
}

4. KlineService.java

import org.json.JSONArray; // JSON レスポンス内の配列を解析します。
import org.json.JSONObject; // JSON オブジェクト全体を解析します。
import org.springframework.stereotype.Service; // このクラスを Spring サービスコンポーネントとしてマークします。
import java.math.BigDecimal; // 正確な小数値(例:暗号通貨の価格)に使用します
import java.net.HttpURLConnection; // HTTPリクエストの送信に使用します
import java.net.URL; // エンドポイントの定義に使用します
import java.io.BufferedReader; // レスポンスストリームを1行ずつ読み取りに使用します
import java.io.InputStreamReader; // バイトストリームを文字ストリームに変換します
import java.sql.Timestamp; // エポックからの時刻(MySQL)を保存します

// Springに、このクラスをアプリケーションコンテキストのサービスとして登録するように指示します
@Service
public class KlineService {

    // MySQLにデータを保存するためのリポジトリへの参照
    private final KlineRepository repository;
    
    // コンストラクター注入:Springはこのサービスにリポジトリを注入します
    public KlineService(KlineRepository repository) {
    this.repository = repository;
    }
    
    // このメソッドは API からデータを取得し、各レコードをデータベースに保存します。
    public void fetchAndSave() {
        try {
            // API エンドポイントを使用して URL オブジェクトを作成します。
            URL url = new URL("https://api.coin.z.com/public/v1/klines?symbol=BTC&interval=1min&date=20210417");
            // 接続を開き、HttpURLConnection にキャストしてリクエストを構成します。
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // リクエストメソッドを GET (読み取り専用) に設定します。
            conn.setRequestMethod("GET");
            
            // 入力ストリームから API レスポンスを読み取る準備をします。
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            // StringBuilder を使用して、レスポンスの各行を 1 つの文字列に追加します。
            StringBuilder jsonBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) // ストリームの最後まで読み取りを続けます。
                jsonBuilder.append(line);
                
            // レスポンス全体をJSONオブジェクトに変換します
            JSONObject response = new JSONObject(jsonBuilder.toString());
            // JSONレスポンスから「data」配列を抽出します
            JSONArray data = response.getJSONArray("data");
            
            // データ配列内の各オブジェクトを反復処理します
            for (int i = 0; i < data.length(); i++) {
                JSONObject obj = data.getJSONObject(i); // 各klineエントリを取得します
                
                Kline kline = new Kline(); // エントリの新しいインスタンスを作成します
                // 文字列エポックをタイムスタンプに変換します
                kline.setOpenTime(new Timestamp(Long.parseLong(obj.getString("openTime"))));
                
                // 金融精度のために文字列をBigDecimalに解析します
                kline.setOpen(new BigDecimal(obj.getString("open")));
                kline.setHigh(new BigDecimal(obj.getString("high")));
                kline.setLow(new BigDecimal(obj.getString("low")));
                kline.setClose(new BigDecimal(obj.getString("close")));
                kline.setVolume(new BigDecimal(obj.getString("volume")));
                
                // 各Klineレコードをデータベースに保存します
                repository.save(kline);
            }
        
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. Application.java

import org.springframework.boot.CommandLineRunner; // アプリケーションの起動後にコードを実行するために使用されます
import org.springframework.boot.SpringApplication; // アプリケーションの起動に使用されるクラス
import org.springframework.boot.autoconfigure.SpringBootApplication; // Spring Boot の自動構成を有効にします

// @Configuration、@EnableAutoConfiguration、および @aComponentScan を組み合わせます
@SpringBootApplication
public class Application implements CommandLineRunner {

    private final KlineService klineService; // データの取得と保存を行うサービスを注入します
    
    // KlineService のコンストラクター注入
    public Application(KlineService klineService) {
        this.klineService = klineService;
    }
    
    public static void main(String[] args) {
        // Spring Boot アプリを起動します
        SpringApplication.run(Application.class, args);
    }
    
    // このメソッドはアプリの起動直後に実行されます
    @Override
    public void run(String... args) {
        klineService.fetchAndSave(); // 起動時にデータの取得と保存のプロセスを 1 回トリガーします
    }
}

Discussion