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.
Dependencies
Section titled “Dependencies”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()}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'}
test { useJUnitPlatform()}<dependencies> <dependency> <groupId>io.github.shibukawa.pgmem</groupId> <artifactId>pgmem-junit5</artifactId> <version>0.1.0</version> <scope>test</scope> </dependency> <dependency> <groupId>io.github.shibukawa.pgmem</groupId> <artifactId>pgmem-native</artifactId> <version>0.1.0</version> <classifier>darwin-arm64</classifier> <scope>test</scope> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.7</version> <scope>test</scope> </dependency></dependencies>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.
The basic flow
Section titled “The basic flow”-
Start the process.
PgmemisAutoCloseable; 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}Pgmem.builder().database("app").start().use { pg ->val template = pg.template()println(template.jdbcUrl())}The builder also takes
user(...),param("shared_buffers", "128MB"),log(true)andbinary(path). -
Register the schema with a migration tool. See migration tools.
-
Load seed data. See seed data.
-
Use it through JDBC.
jdbcUrl()carries the user andsslmode, anddataSource()returns ajavax.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);}}DriverManager.getConnection(template.jdbcUrl()).use { conn ->conn.prepareStatement("SELECT name FROM users WHERE id = ?").use { ps ->ps.setLong(1, 1)ps.executeQuery().use { rs -> rs.next(); println(rs.getString(1)) }}}A HikariCP pool on the URL works too; its connections serialize at transaction boundaries.
Migration tools
Section titled “Migration tools”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);}Flyway.configure() .dataSource(template.jdbcUrl(), template.user(), null) .locations("classpath:db/migration") .load() .migrate();Flyway 10 and later also need org.flywaydb:flyway-database-postgresql on the test classpath.
try (Connection conn = DriverManager.getConnection(template.jdbcUrl())) { Database db = DatabaseFactory.getInstance() .findCorrectDatabaseImplementation(new JdbcConnection(conn)); new CommandScope("update") .addArgumentValue("database", db) .addArgumentValue("changelogFile", "db/changelog/db.changelog-master.yaml") .execute();}Seed data
Section titled “Seed data”<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 (Connection conn = DriverManager.getConnection(template.jdbcUrl()); Statement st = conn.createStatement()) { st.execute(Files.readString(Path.of("src/test/resources/seed.sql")));}try (Connection conn = DriverManager.getConnection(template.jdbcUrl())) { CopyManager copy = conn.unwrap(PGConnection.class).getCopyAPI(); copy.copyIn("COPY users (id, name, email) FROM STDIN (FORMAT csv)", new StringReader("1,Frank,frank@example.com\n2,Grace,grace@example.com\n"));}Snapshot and fork by hand
Section titled “Snapshot and fork by hand”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.