DEV Community

Shelner
Shelner

Posted on

How to get Bitcoin data and save it in MySQL using Spring Boot

Reference

GMO Coin API Documentation

Preparation

Build MySQL Database and Table;

Example:

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)
);
Enter fullscreen mode Exit fullscreen mode

Dependencies

<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>
Enter fullscreen mode Exit fullscreen mode

Step-by-Step JPA Implementation

1. application.properties

This is configures DB connection.

# URL to connect to MySQL, including port and database name
spring.datasource.url=jdbc:mysql://localhost:3306/coin_db
# Username for the MySQL database
spring.datasource.username=your_username
# Password for the MySQL database
spring.datasource.password=your_password
# Automatically Creates/updates tables based on entity definitions
spring.jpa.hibernate.ddl-auto=update
Enter fullscreen mode Exit fullscreen mode

2. Kline.java (Entity)

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;

    // Getters and Setters
}
Enter fullscreen mode Exit fullscreen mode

3. KlineRepository.java

// This interface allows us to perform basic CRUD operations without implementing them manually
import org.springframework.data.jpa.repository.JpaRepository;

// This is the repository (data access layer) for the `Kline` entity
// It inherits method like save, findAll, findById, deleteById, etc.
public interface KlineRepository extends JpaRepository<Kline, Long> {
    // JpaRepository<T, ID>:
    // - T is the Entity type (Kline)
    // - ID is the type of the primary key (long)
}
Enter fullscreen mode Exit fullscreen mode

4. KlineService.java

import org.json.JSONArray; // For parsing arrays in JSON response
import org.json.JSONObject; // For parsing the full JSON object
import org.springframework.stereotype.Service; // Marks this class as a Spring service component
import java.math.BigDecimal; // Used for precise decimal values (e.g., cryptocurrency prices)
import java.net.HttpURLConnection; // For sending HTTP requests
import java.net.URL; // For defining the endpoint
import java.io.BufferedReader; // For reading the response steam line by line
import java.io.InputStreamReader; // Converts byte stream into character stream
import java.sql.Timestamp; // Used to store time (in MySQL) from epoch

// Tells Spring to register this class as a service in the application context
@Service
public class KlineService {

    // Reference to the repository to save data into MySQL
    private final KlineRepository repository;

    // Constructor Injection: Spring injects the repository into this service
    public KlineService(KlineRepository repository) {
        this.repository = repository;
    }

    // this method fetches data from the API and saves each record into the database
    public void fetchAndSave() {
        try {
            // Create a URL object with the API endpoint
            URL url = new URL("https://api.coin.z.com/public/v1/klines?symbol=BTC&interval=1min&date=20210417");
            // Open a connection and cast it to HttpURLConnection to configure the request
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // Set the request method to GET (read-only)
            conn.setRequestMethod("GET");

            // Prepare to read the API response from the input stream
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            // Append each line of response into one string using StringBuilder
            StringBuilder jsonBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) // Keep reading until end of stream
                jsonBuilder.append(line);

            // Convert the entire response into a JSON object
            JSONObject response = new JSONObject(jsonBuilder.toString());
            // Extract the "data" array from the JSON response
            JSONArray data = response.getJSONArray("data");

            // Iterate over each object inside the data array
            for (int i = 0; i < data.length(); i++) {
                JSONObject obj = data.getJSONObject(i); // Get each kline entry

                Kline kline = new Kline(); // Create a new instance of the entry
                // Convert string epoch to Timestamp
                kline.setOpenTime(new Timestamp(Long.parseLong(obj.getString("openTime")))); 

                // Parse strings to BigDecimal for financial precision
                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")));

                // Save each Kline record to the database
                repository.save(kline);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Application.java

import org.springframework.boot.CommandLineRunner; // Used to run code after the application starts
import org.springframework.boot.SpringApplication; // Class used to launch the application
import org.springframework.boot.autoconfigure.SpringBootApplication; // Enables Spring Boot auto-configuration

// Combines @Configuration, @EnableAutoConfiguration, and @aComponentScan
@SpringBootApplication
public class Application implements CommandLineRunner {

    private final KlineService klineService; // Inject the service that fetches and saves data

    // Constructor Injection for the KlineService
    public Application(KlineService klineService) {
        this.klineService = klineService;
    }

    public static void main(String[] args) {
        // Launch the Spring Boot app
        SpringApplication.run(Application.class, args);
    }

    // This method runs immediately after the app starts
    @Override
    public void run(String... args) {
        klineService.fetchAndSave(); // Trigger the data fetch and save process once at startup
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.