BAEL-7670: Introduction to DuckDB (#16197)
* BAEL-7670: Introduction to DuckDB * BAEL-7670: Introduction to DuckDB
This commit is contained in:
parent
e34eb87e8e
commit
856360fe1d
|
@ -0,0 +1,42 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.baeldung</groupId>
|
||||||
|
<artifactId>duckdb</artifactId>
|
||||||
|
<name>duckdb</name>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>9</source>
|
||||||
|
<target>9</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>persistence-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<duckdb.version>0.10.0</duckdb.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.duckdb</groupId>
|
||||||
|
<artifactId>duckdb_jdbc</artifactId>
|
||||||
|
<version>${duckdb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,198 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.ResultSetMetaData;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
|
||||||
|
|
||||||
|
class DuckDbAccessIntegrationTest {
|
||||||
|
|
||||||
|
private Connection conn = null;
|
||||||
|
private Statement stmt = null;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setupOnce() throws Exception {
|
||||||
|
Class.forName("org.duckdb.DuckDBDriver");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() throws SQLException {
|
||||||
|
conn = DriverManager.getConnection("jdbc:duckdb:");
|
||||||
|
stmt = conn.createStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenQueryCurrentDate_thenReturnToday() throws SQLException {
|
||||||
|
ResultSet rs = stmt.executeQuery("SELECT current_date");
|
||||||
|
Date currentDate = rs.next() ? rs.getDate(1) : null;
|
||||||
|
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
Date expectedDate = calendar.getTime();
|
||||||
|
|
||||||
|
assertThat(currentDate).isEqualTo(expectedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenReadCsv_thenReturnHeaderAndData() throws SQLException {
|
||||||
|
String filePath = getResourceAbsolutePath("/customer.csv");
|
||||||
|
String query = String.format("SELECT * FROM read_csv('%s')", filePath);
|
||||||
|
|
||||||
|
ResultSet rs = stmt.executeQuery(query);
|
||||||
|
ResultSetMetaData metadata = rs.getMetaData();
|
||||||
|
|
||||||
|
List<String> actualHeaderNames = new ArrayList<>();
|
||||||
|
for (int n=1; n<=metadata.getColumnCount(); n++) {
|
||||||
|
actualHeaderNames.add(metadata.getColumnLabel(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then
|
||||||
|
List<String> expectedHeaderNames = List.of("CustomerId", "FirstName", "LastName", "Gender");
|
||||||
|
assertThat(actualHeaderNames).isEqualTo(expectedHeaderNames);
|
||||||
|
|
||||||
|
int rowCount = 0;
|
||||||
|
while (rs.next()) {
|
||||||
|
rowCount++;
|
||||||
|
}
|
||||||
|
assertThat(rowCount).isEqualTo(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenImportByCsv_thenReturnCorrectRowCount() throws SQLException {
|
||||||
|
// When
|
||||||
|
String filePath = getResourceAbsolutePath("/customer.csv");
|
||||||
|
String query = String.format("CREATE TABLE customer AS SELECT * FROM read_csv('%s')", filePath);
|
||||||
|
stmt.executeUpdate(query);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(getTableRowCount(conn, "customer")).isEqualTo(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenImportByJson_thenReturnCorrectRowCount() throws SQLException {
|
||||||
|
// When
|
||||||
|
String filePath = getResourceAbsolutePath("/product.json");
|
||||||
|
String query = String.format("CREATE TABLE product AS SELECT * FROM read_json('%s')", filePath);
|
||||||
|
stmt.executeUpdate(query);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(getTableRowCount(conn, "product")).isEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenImportByInsert_thenReturnCorrectRowCount() throws SQLException {
|
||||||
|
// When
|
||||||
|
stmt.executeUpdate("CREATE TABLE purchase(customerId BIGINT, productId BIGINT)");
|
||||||
|
|
||||||
|
String query = "INSERT INTO purchase(customerId, productId) VALUES (?,?)";
|
||||||
|
try (PreparedStatement pStmt = conn.prepareStatement(query)) {
|
||||||
|
|
||||||
|
pStmt.setInt(1, 101);
|
||||||
|
pStmt.setInt(2, 1);
|
||||||
|
pStmt.addBatch();
|
||||||
|
|
||||||
|
pStmt.setInt(1, 101);
|
||||||
|
pStmt.setInt(2, 2);
|
||||||
|
pStmt.addBatch();
|
||||||
|
|
||||||
|
pStmt.setInt(1, 102);
|
||||||
|
pStmt.setInt(2, 2);
|
||||||
|
pStmt.addBatch();
|
||||||
|
|
||||||
|
pStmt.executeBatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(getTableRowCount(conn, "purchase")).isEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenQueryWithJoin_thenReturnCorrectCount() throws SQLException {
|
||||||
|
String customerFilePath = getResourceAbsolutePath("/customer.csv");
|
||||||
|
String productFilePath = getResourceAbsolutePath("/product.json");
|
||||||
|
whenImportByInsert_thenReturnCorrectRowCount();
|
||||||
|
|
||||||
|
String query = String.format("SELECT C.firstName, C.lastName, P.productName " +
|
||||||
|
"FROM read_csv('%s') AS C, read_json('%s') AS P, purchase S " +
|
||||||
|
"WHERE S.customerId = C.customerId " +
|
||||||
|
"AND S.productId = P.productId ",
|
||||||
|
customerFilePath, productFilePath);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
ResultSet rs = stmt.executeQuery(query);
|
||||||
|
while (rs.next()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(count).isEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenExportData_thenFileIsCreated() throws IOException, SQLException {
|
||||||
|
createPurchaseView(conn);
|
||||||
|
|
||||||
|
File tempFile = File.createTempFile("temp", "");
|
||||||
|
String exportFilePath = tempFile.getAbsolutePath();
|
||||||
|
String query = String.format("COPY purchase_view TO '%s'", exportFilePath);
|
||||||
|
stmt.executeUpdate(query);
|
||||||
|
|
||||||
|
assertThat(tempFile.length()).isGreaterThan(0);
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() throws SQLException {
|
||||||
|
stmt.close();
|
||||||
|
conn.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getResourceAbsolutePath(String name) {
|
||||||
|
return this.getClass().getResource(name).getPath().replaceFirst("/", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getTableRowCount(Connection conn, String tableName) throws SQLException {
|
||||||
|
try (Statement stmt = conn.createStatement()) {
|
||||||
|
ResultSet rs = stmt.executeQuery(String.format("SELECT COUNT(*) FROM %s", tableName));
|
||||||
|
return (rs.next()) ? rs.getInt(1) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPurchaseView(Connection conn) throws SQLException {
|
||||||
|
whenImportByCsv_thenReturnCorrectRowCount();
|
||||||
|
whenImportByJson_thenReturnCorrectRowCount();
|
||||||
|
whenImportByInsert_thenReturnCorrectRowCount();
|
||||||
|
|
||||||
|
String query = "CREATE VIEW purchase_view AS " +
|
||||||
|
"SELECT P.productName, COUNT(*) AS purchaseCount " +
|
||||||
|
"FROM customer C, product P, purchase S " +
|
||||||
|
"WHERE S.customerId = C.customerId " +
|
||||||
|
"AND S.productId = P.productId " +
|
||||||
|
"GROUP BY P.productName " +
|
||||||
|
"ORDER BY COUNT(*) DESC ";
|
||||||
|
|
||||||
|
try (Statement stmt = conn.createStatement()) {
|
||||||
|
stmt.executeUpdate(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
CustomerId,FirstName,LastName,Gender
|
||||||
|
101,John,Smith,Male
|
||||||
|
102,Sarah,Jones,Female
|
||||||
|
103,Michael,Johnson,Male
|
||||||
|
104,Emily,Davis,Female
|
||||||
|
105,David,Brown,Male
|
||||||
|
106,Emma,Williams,Female
|
||||||
|
107,Alexander,Miller,Male
|
||||||
|
108,Samantha,Anderson,Female
|
||||||
|
109,Matthew,Taylor,Male
|
||||||
|
110,Olivia,Thompson,Female
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"productId": 1,
|
||||||
|
"productName":"EZ Curl Bar",
|
||||||
|
"category": "Sports Equipment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 2,
|
||||||
|
"productName": "7' Barbell",
|
||||||
|
"category": "Sports Equipment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productId": 3,
|
||||||
|
"productName": "Single Mouthguard - Black",
|
||||||
|
"category": "Sports Equipment"
|
||||||
|
}
|
||||||
|
]
|
|
@ -24,6 +24,7 @@
|
||||||
<module>core-java-persistence-2</module>
|
<module>core-java-persistence-2</module>
|
||||||
<module>core-java-persistence-3</module>
|
<module>core-java-persistence-3</module>
|
||||||
<module>couchbase</module>
|
<module>couchbase</module>
|
||||||
|
<module>duckdb</module>
|
||||||
<module>elasticsearch</module>
|
<module>elasticsearch</module>
|
||||||
<module>flyway</module>
|
<module>flyway</module>
|
||||||
<module>flyway-repair</module>
|
<module>flyway-repair</module>
|
||||||
|
|
Loading…
Reference in New Issue