Skip to content

Java basics

pgmem for the JVM is three artifacts: io.github.shibukawa.pgmem:pgmem (the client), io.github.shibukawa.pgmem:pgmem-junit5 (the JUnit 5 extension) and io.github.shibukawa.pgmem:pgmem-native (the binary, one classifier per platform). The client has no third-party runtime dependencies; bring your JDBC driver.

build.gradle.kts
dependencies {
testImplementation("io.github.shibukawa.pgmem:pgmem-junit5:0.1.0") // pulls in io.github.shibukawa.pgmem:pgmem
testRuntimeOnly("io.github.shibukawa.pgmem:pgmem-native:0.1.0:darwin-arm64") // the binary for your platform
testRuntimeOnly("org.postgresql:postgresql:42.7.7")
testImplementation("org.junit.jupiter:junit-jupiter:5.13.4")
}
tasks.test {
useJUnitPlatform()
}

The classifiers are linux-x86_64, linux-arm64, darwin-arm64, windows-x86_64 and windows-arm64. When a build runs on several platforms, pick one per platform with Maven profiles or a Gradle OS detection plugin rather than depending on all five. The binary is extracted once into ~/.cache/pgmem/<version>/. Java 17 or newer.

  1. Start the process. Pgmem is AutoCloseable; closing it stops every server.

    try (Pgmem pg = Pgmem.builder().database("app").start()) {
    Server template = pg.template();
    System.out.println(template.jdbcUrl()); // jdbc:postgresql://127.0.0.1:54321/app?user=postgres&sslmode=disable
    }

    The builder also takes user(...), param("shared_buffers", "128MB"), log(true) and binary(path).

  2. Register the schema with a migration tool. See migration tools.

  3. Load seed data. See seed data.

  4. Use it through JDBC. jdbcUrl() carries the user and sslmode, and dataSource() returns a javax.sql.DataSource.

    try (Connection conn = DriverManager.getConnection(template.jdbcUrl());
    PreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
    ps.setLong(1, 1);
    try (ResultSet rs = ps.executeQuery()) {
    rs.next();
    String name = rs.getString(1);
    }
    }

    A HikariCP pool on the URL works too; its connections serialize at transaction boundaries.

String schema = Files.readString(Path.of("src/test/resources/schema.sql"));
try (Connection conn = DriverManager.getConnection(template.jdbcUrl());
Statement st = conn.createStatement()) {
st.execute(schema);
}
src/test/resources/seed.xml
<dataset>
<users id="1" name="Frank" email="frank@example.com"/>
<users id="2" name="Grace" email="grace@example.com"/>
<orders id="1" user_id="1" amount="1200"/>
</dataset>
IDatabaseTester tester = new JdbcDatabaseTester(
"org.postgresql.Driver", template.jdbcUrl(), template.user(), "");
tester.setDataSet(new FlatXmlDataSetBuilder()
.build(getClass().getResourceAsStream("/seed.xml")));
tester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
tester.onSetup();
try (Pgmem pg = Pgmem.builder().database("app").start()) {
migrate(pg.template().jdbcUrl());
Snapshot snap = pg.template().snapshot(); // waits for open transactions, 30 s
try (Fork fork = snap.fork()) { // a private copy
DataSource ds = fork.dataSource();
// ...
}
}

Commit or close every connection to the template before snapshot(). The JUnit 5 extension on the next page wires this into the test lifecycle.