Hi, setelah kita membuat connection, menyiapkan database schema dengan menggunakan flyway database migration. Sekarang kita akan mengambil data dari table yang ada di database. Tapi sebelum itu ada beberapa yang harus kita siapkan karena disini saya menggunakan Design pattern. Nah temen-temen mungkin binggung apa itu design pattern ya khan?…

Ok design pattern in software engineering simple wordnya itu solusi untuk meminimalisir duplication (menulis hal yang sama dengan satu tujuan). tapi gak usah di pusingin dulu perlahan-lahan dengan pengalan menulis koding dan memahami isinya pasti nanti akan paham dengan sendirinya.

Back to topic, seperti yang saya katakan tadi saya mau membuat sebuah interface dulu sebagai template dengan nama CrudRepository yang saya simpan di package com.maryanto.dimas.bootcamp.dao seperti berikut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.maryanto.dimas.bootcamp.dao;

import java.sql.SQLException;
import java.util.List;
import java.util.Optional;

public interface CrudRepository<T, ID> {

    public T save(T value) throws SQLException;

    public T update(T value) throws SQLException;

    public Boolean removeById(ID value) throws SQLException;

    public Optional<T> findById(ID value) throws SQLException;

    public List<T> findAll() throws SQLException;

    public List<T> findAll(Long start, Long limit, Long orderIndex, String orderDirection, T param) throws SQLException;
}

Ok setelah itu kita buat kelas baru dengan nama ExampleTable yang di simpan di package com.maryanto.dimas.bootcamp.entity seperti berikut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.maryanto.dimas.bootcamp.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExampleTable {

    private String id;
    private String name;
    private Date createdDate;
    private Timestamp createdTime;
    private Boolean active;
    private Long counter;
    private BigDecimal currency;
    private String description;
    private Float floating;
}

Jadi class ExampleTable tersebut merepresentasikan columns yang ada pada table example_table pada database bootcamp. ok tahap selanjutnya adalah kita membuat repository / Data Access Object atau singkatannya DAO. buat kelas dengan nama ExampleTableDao pada package com.maryanto.dimas.dao seperti berikut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.maryanto.dimas.bootcamp.dao;

import com.maryanto.dimas.bootcamp.entity.ExampleTable;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class ExampleTableDao implements CrudRepository<ExampleTable, String> {

    private Connection connection;

    public ExampleTableDao(Connection connection) {
        this.connection = connection;
    }

    @Override
    public List<ExampleTable> findAll() throws SQLException {
        List<ExampleTable> list = new ArrayList<>();
        //language=PostgreSQL
        String query = "select id           as id,\n" +
                "       name         as name,\n" +
                "       created_date as createdDate,\n" +
                "       created_time as createdTime,\n" +
                "       is_active    as active,\n" +
                "       counter      as counter,\n" +
                "       currency     as currency,\n" +
                "       description  as description,\n" +
                "       floating     as floating\n" +
                "from example_table";
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(query);
        while (resultSet.next()) {
            ExampleTable data = new ExampleTable(
                    resultSet.getString("id"),
                    resultSet.getString("name"),
                    resultSet.getDate("createdDate"),
                    resultSet.getTimestamp("createdTime"),
                    resultSet.getObject("active", Boolean.class),
                    resultSet.getLong("counter"),
                    resultSet.getBigDecimal("currency"),
                    resultSet.getString("description"),
                    resultSet.getFloat("floating")
            );
            list.add(data);
        }

        resultSet.close();
        statement.close();
        return list;
    }

    // other method goes here
}

Nah jadi dari sample koding diatas, itu kita melakukan execute query sebagai berikut:

select  id           as id,
        name         as name,
        created_date as createdDate,
        created_time as createdTime,
        is_active    as active,
        counter      as counter,
        currency     as currency,
        description  as description,
        floating     as floating
from example_table

yang di simpan dalam kelas String kemudian di execute menggunakan API class java.sql.Statement. perintah menggunakan API class Statement biasanya digunakan untuk query yang tidak membutuhkan parameter / argument contohnya query seperti berikut:

select  id           as id,
        name         as name,
        created_date as createdDate,
        created_time as createdTime,
        is_active    as active,
        counter      as counter,
        currency     as currency,
        description  as description,
        floating     as floating
from example_table 
where id = '001'

Nah dengan query tersebut klo misalnya nilai id = '001' bernilai statis itu tidak jadi masalah, tetapi jika nilainya bisa ber-ubah-ubah sebaiknya jangan menggunakan API class Statement. Terkadang saya menemukan koding yang klo di tulis di dengan menggunakan JDBC seperti berikut:

import java.sql.*;

public class ExampleYangSalah  {

    public ExampleTable findById(String id) throws SQLException {
        // ini adalah contoh yang salah
        String query = "select id           as id,\n" +
                "       name         as name,\n" +
                "       created_date as createdDate,\n" +
                "       created_time as createdTime,\n" +
                "       is_active    as active,\n" +
                "       counter      as counter,\n" +
                "       currency     as currency,\n" +
                "       description  as description,\n" +
                "       floating     as floating\n" +
                "from example_table\n" +
                "where id = '" + id + "'";
        }
    }
}

Nah kenapa saya bilang salah, karena ini bisa kena serangan yang namanya SQL Injection. buat yang belum paham silahkan baca di sini. Akan lebih baik jika kita menggunakan object PreparedStatement.

Ok dan yang terakhir sekarang kita buat unit testing untuk method findAll() tersebut dengan cara membuat class di folder src/test/java/ dengan nama TestIntegrationExampleTable dalam package com.maryanto.dimas.bootcamp seperti berikut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.maryanto.dimas.bootcamp;

import com.maryanto.dimas.bootcamp.config.DatasourceConfig;
import com.maryanto.dimas.bootcamp.dao.ExampleTableDao;
import com.maryanto.dimas.bootcamp.entity.ExampleTable;
import junit.framework.TestCase;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

@Slf4j
public class TestIntegrationExampleTable extends TestCase {

    private DataSource dataSource;

    private ExampleTableDao dao;

    @Override
    protected void setUp() throws Exception {
        this.dataSource = new DatasourceConfig().getDataSource();
    }

    @Test
    public void testFindAllData() {
        try (Connection connection = this.dataSource.getConnection()) {
            this.dao = new ExampleTableDao(connection);
            List<ExampleTable> list = this.dao.findAll();
            assertEquals("jumlah data example table", list.size(), 5);
        } catch (SQLException ex) {
            log.error("can't fetch data", ex);
        }
    }
}

Kemudian jalankan perintah test seperti berikut:

mvn clean test

dan berikut hasilnya:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.maryanto.dimas.bootcamp.TestConnection
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
[main] INFO com.maryanto.dimas.bootcamp.TestConnection - status connected
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.364 sec
Running com.maryanto.dimas.bootcamp.TestIntegrationExampleTable
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.077 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.642 s
[INFO] Finished at: 2020-04-20T19:00:17+07:00
[INFO] ------------------------------------------------------------------------