Skip to content

Commit 856360f

Browse files
authored
BAEL-7670: Introduction to DuckDB (#16197)
* BAEL-7670: Introduction to DuckDB * BAEL-7670: Introduction to DuckDB
1 parent e34eb87 commit 856360f

5 files changed

Lines changed: 269 additions & 0 deletions

File tree

persistence-modules/duckdb/pom.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>org.baeldung</groupId>
7+
<artifactId>duckdb</artifactId>
8+
<name>duckdb</name>
9+
<build>
10+
<plugins>
11+
<plugin>
12+
<groupId>org.apache.maven.plugins</groupId>
13+
<artifactId>maven-compiler-plugin</artifactId>
14+
<configuration>
15+
<source>9</source>
16+
<target>9</target>
17+
</configuration>
18+
</plugin>
19+
</plugins>
20+
</build>
21+
<version>0.0.1-SNAPSHOT</version>
22+
23+
<parent>
24+
<groupId>com.baeldung</groupId>
25+
<artifactId>persistence-modules</artifactId>
26+
<version>1.0.0-SNAPSHOT</version>
27+
</parent>
28+
29+
<properties>
30+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
31+
<duckdb.version>0.10.0</duckdb.version>
32+
</properties>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>org.duckdb</groupId>
37+
<artifactId>duckdb_jdbc</artifactId>
38+
<version>${duckdb.version}</version>
39+
</dependency>
40+
</dependencies>
41+
42+
</project>
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package com.baeldung;
2+
3+
import org.junit.jupiter.api.AfterEach;
4+
import org.junit.jupiter.api.BeforeAll;
5+
import org.junit.jupiter.api.BeforeEach;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.io.File;
9+
import java.io.IOException;
10+
import java.sql.Connection;
11+
import java.sql.DriverManager;
12+
import java.sql.PreparedStatement;
13+
import java.sql.ResultSet;
14+
import java.sql.ResultSetMetaData;
15+
import java.sql.SQLException;
16+
import java.sql.Statement;
17+
import java.util.ArrayList;
18+
import java.util.Calendar;
19+
import java.util.Date;
20+
import java.util.List;
21+
22+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
23+
24+
25+
class DuckDbAccessIntegrationTest {
26+
27+
private Connection conn = null;
28+
private Statement stmt = null;
29+
30+
@BeforeAll
31+
static void setupOnce() throws Exception {
32+
Class.forName("org.duckdb.DuckDBDriver");
33+
}
34+
35+
@BeforeEach
36+
void setup() throws SQLException {
37+
conn = DriverManager.getConnection("jdbc:duckdb:");
38+
stmt = conn.createStatement();
39+
}
40+
41+
@Test
42+
void whenQueryCurrentDate_thenReturnToday() throws SQLException {
43+
ResultSet rs = stmt.executeQuery("SELECT current_date");
44+
Date currentDate = rs.next() ? rs.getDate(1) : null;
45+
46+
Calendar calendar = Calendar.getInstance();
47+
calendar.set(Calendar.HOUR_OF_DAY, 0);
48+
calendar.set(Calendar.MINUTE, 0);
49+
calendar.set(Calendar.SECOND, 0);
50+
calendar.set(Calendar.MILLISECOND, 0);
51+
Date expectedDate = calendar.getTime();
52+
53+
assertThat(currentDate).isEqualTo(expectedDate);
54+
}
55+
56+
@Test
57+
void whenReadCsv_thenReturnHeaderAndData() throws SQLException {
58+
String filePath = getResourceAbsolutePath("/customer.csv");
59+
String query = String.format("SELECT * FROM read_csv('%s')", filePath);
60+
61+
ResultSet rs = stmt.executeQuery(query);
62+
ResultSetMetaData metadata = rs.getMetaData();
63+
64+
List<String> actualHeaderNames = new ArrayList<>();
65+
for (int n=1; n<=metadata.getColumnCount(); n++) {
66+
actualHeaderNames.add(metadata.getColumnLabel(n));
67+
}
68+
69+
// Then
70+
List<String> expectedHeaderNames = List.of("CustomerId", "FirstName", "LastName", "Gender");
71+
assertThat(actualHeaderNames).isEqualTo(expectedHeaderNames);
72+
73+
int rowCount = 0;
74+
while (rs.next()) {
75+
rowCount++;
76+
}
77+
assertThat(rowCount).isEqualTo(10);
78+
}
79+
80+
@Test
81+
void whenImportByCsv_thenReturnCorrectRowCount() throws SQLException {
82+
// When
83+
String filePath = getResourceAbsolutePath("/customer.csv");
84+
String query = String.format("CREATE TABLE customer AS SELECT * FROM read_csv('%s')", filePath);
85+
stmt.executeUpdate(query);
86+
87+
// Then
88+
assertThat(getTableRowCount(conn, "customer")).isEqualTo(10);
89+
}
90+
91+
@Test
92+
void whenImportByJson_thenReturnCorrectRowCount() throws SQLException {
93+
// When
94+
String filePath = getResourceAbsolutePath("/product.json");
95+
String query = String.format("CREATE TABLE product AS SELECT * FROM read_json('%s')", filePath);
96+
stmt.executeUpdate(query);
97+
98+
// Then
99+
assertThat(getTableRowCount(conn, "product")).isEqualTo(3);
100+
}
101+
102+
@Test
103+
void whenImportByInsert_thenReturnCorrectRowCount() throws SQLException {
104+
// When
105+
stmt.executeUpdate("CREATE TABLE purchase(customerId BIGINT, productId BIGINT)");
106+
107+
String query = "INSERT INTO purchase(customerId, productId) VALUES (?,?)";
108+
try (PreparedStatement pStmt = conn.prepareStatement(query)) {
109+
110+
pStmt.setInt(1, 101);
111+
pStmt.setInt(2, 1);
112+
pStmt.addBatch();
113+
114+
pStmt.setInt(1, 101);
115+
pStmt.setInt(2, 2);
116+
pStmt.addBatch();
117+
118+
pStmt.setInt(1, 102);
119+
pStmt.setInt(2, 2);
120+
pStmt.addBatch();
121+
122+
pStmt.executeBatch();
123+
}
124+
125+
// Then
126+
assertThat(getTableRowCount(conn, "purchase")).isEqualTo(3);
127+
}
128+
129+
@Test
130+
void whenQueryWithJoin_thenReturnCorrectCount() throws SQLException {
131+
String customerFilePath = getResourceAbsolutePath("/customer.csv");
132+
String productFilePath = getResourceAbsolutePath("/product.json");
133+
whenImportByInsert_thenReturnCorrectRowCount();
134+
135+
String query = String.format("SELECT C.firstName, C.lastName, P.productName " +
136+
"FROM read_csv('%s') AS C, read_json('%s') AS P, purchase S " +
137+
"WHERE S.customerId = C.customerId " +
138+
"AND S.productId = P.productId ",
139+
customerFilePath, productFilePath);
140+
141+
int count = 0;
142+
ResultSet rs = stmt.executeQuery(query);
143+
while (rs.next()) {
144+
count++;
145+
}
146+
147+
assertThat(count).isEqualTo(3);
148+
}
149+
150+
@Test
151+
void whenExportData_thenFileIsCreated() throws IOException, SQLException {
152+
createPurchaseView(conn);
153+
154+
File tempFile = File.createTempFile("temp", "");
155+
String exportFilePath = tempFile.getAbsolutePath();
156+
String query = String.format("COPY purchase_view TO '%s'", exportFilePath);
157+
stmt.executeUpdate(query);
158+
159+
assertThat(tempFile.length()).isGreaterThan(0);
160+
tempFile.delete();
161+
}
162+
163+
@AfterEach
164+
void tearDown() throws SQLException {
165+
stmt.close();
166+
conn.close();
167+
}
168+
169+
private String getResourceAbsolutePath(String name) {
170+
return this.getClass().getResource(name).getPath().replaceFirst("/", "");
171+
}
172+
173+
private int getTableRowCount(Connection conn, String tableName) throws SQLException {
174+
try (Statement stmt = conn.createStatement()) {
175+
ResultSet rs = stmt.executeQuery(String.format("SELECT COUNT(*) FROM %s", tableName));
176+
return (rs.next()) ? rs.getInt(1) : 0;
177+
}
178+
}
179+
180+
private void createPurchaseView(Connection conn) throws SQLException {
181+
whenImportByCsv_thenReturnCorrectRowCount();
182+
whenImportByJson_thenReturnCorrectRowCount();
183+
whenImportByInsert_thenReturnCorrectRowCount();
184+
185+
String query = "CREATE VIEW purchase_view AS " +
186+
"SELECT P.productName, COUNT(*) AS purchaseCount " +
187+
"FROM customer C, product P, purchase S " +
188+
"WHERE S.customerId = C.customerId " +
189+
"AND S.productId = P.productId " +
190+
"GROUP BY P.productName " +
191+
"ORDER BY COUNT(*) DESC ";
192+
193+
try (Statement stmt = conn.createStatement()) {
194+
stmt.executeUpdate(query);
195+
}
196+
}
197+
198+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CustomerId,FirstName,LastName,Gender
2+
101,John,Smith,Male
3+
102,Sarah,Jones,Female
4+
103,Michael,Johnson,Male
5+
104,Emily,Davis,Female
6+
105,David,Brown,Male
7+
106,Emma,Williams,Female
8+
107,Alexander,Miller,Male
9+
108,Samantha,Anderson,Female
10+
109,Matthew,Taylor,Male
11+
110,Olivia,Thompson,Female
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[
2+
{
3+
"productId": 1,
4+
"productName":"EZ Curl Bar",
5+
"category": "Sports Equipment"
6+
},
7+
{
8+
"productId": 2,
9+
"productName": "7' Barbell",
10+
"category": "Sports Equipment"
11+
},
12+
{
13+
"productId": 3,
14+
"productName": "Single Mouthguard - Black",
15+
"category": "Sports Equipment"
16+
}
17+
]

persistence-modules/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<module>core-java-persistence-2</module>
2525
<module>core-java-persistence-3</module>
2626
<module>couchbase</module>
27+
<module>duckdb</module>
2728
<module>elasticsearch</module>
2829
<module>flyway</module>
2930
<module>flyway-repair</module>

0 commit comments

Comments
 (0)