MCPcopy Index your code
hub / github.com/bytefish/PgBulkInsert

github.com/bytefish/PgBulkInsert @9.0.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release 9.0.1 ↗ · + Follow
160 symbols 322 edges 3 files 0 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

PgBulkInsert

PgBulkInsert is a Java library for Bulk Inserts to PostgreSQL using the Binary COPY Protocol.

It provides a wrapper around the PostgreSQL COPY command:

The COPY command is a PostgreSQL specific feature, which allows efficient bulk import or export of data to and from a table. This is a much faster way of getting data in and out of a table than using INSERT and SELECT.

This project wouldn't be possible without the great Npgsql library, which has a beautiful implementation of the Postgres protocol.

Setup

PgBulkInsert is available in the Central Maven Repository.

You can add the following dependencies to your pom.xml to include PgBulkInsert in your project.

<dependency>
    <groupId>de.bytefish</groupId>
    <artifactId>pgbulkinsert</artifactId>
    <version>9.0.1</version>
</dependency>

The library is a single file, so you can also copy and paste the file into your project, without taking an additional dependency.

PgBulkInsert 9: Modern, Functional API

PgBulkInsert 9.0.0 comes with a new API, that's functional and type-safe to work with.

Quick Start

The new API separates the What (Structure and Mapping) from the How (Execution and Configuration).

1. Define your Data Model

The library works with Java Records, POJOs, or any other data carrier.

public record UserSession(
    UUID id,
    long visits,         // Primitive
    String userAgent,    // Nullable String
    Instant createdAt,   // Precise Timestamp
    double[] latLon,     // Array
    PgPoint location     // Geometric Point
) {}

2. Define your Mapping

The PgMapper is the heart of the library. It is stateless and thread-safe.

PgMapper<UserSession> mapper = PgMapper.forClass(UserSession.class)
    .map("id", PostgresTypes.UUID.from(UserSession::id))

    // ZERO-ALLOCATION: Direct access to primitives via ToLongFunction
    .map("visits", PostgresTypes.INT8.primitive(UserSession::visits))

    // SAFE STRINGS: Strips invalid \u0000 characters to prevent COPY errors
    .map("user_agent", PostgresTypes.TEXT.removeNullCharacters().from(UserSession::userAgent))

    // TYPE-SAFE TIME: Validated at compile-time to prevent timezone issues
    .map("created_at", PostgresTypes.TIMESTAMPTZ.instant(UserSession::createdAt))

    // GEOMETRY: Native support for Postgres geometric types
    .map("location", PostgresTypes.POINT.from(UserSession::location));

3. Configure and Execute

The PgBulkWriter handles execution details like buffer sizes and stream management.

PgBulkWriter<UserSession> writer = new PgBulkWriter<>(mapper)
    .withBufferSize(256 * 1024); // 256 KB Buffer

try (Connection conn = dataSource.getConnection()) {
    writer.saveAll(conn, "public.user_sessions", sessionList);
}

Streaming and Lazy Evaluation

If you are working with a Java Stream (e.g., from a file, a reactive source, or another database), you can pass it directly:

Stream<UserSession> massiveStream = getMassiveStreamFromSource();

try (Connection conn = dataSource.getConnection()) {
    // Uses the stream's iterator to pull data lazily
    writer.saveAll(conn, "public.user_sessions", massiveStream);
}

This approach ensures that records are transformed and written to the PostgreSQL wire format on-the-fly, keeping your application's memory usage constant regardless of the total number of rows. You can also pass a Collection<T> or an Iterable<T> to the method.

Mastering the Fluent API

The API is designed around so called PostgresTypes. This class serves as your single entry point for all PostgreSQL data types.

The Power of PostgresTypes

Instead of a generic map() method that tries to guess your intent, the API uses a "Type-First" approach. When you type PostgresTypes.INT4., your IDE will offer specific choices:

  • .primitive(ToIntFunction<T>): High-performance path. No objects created on the heap.
  • .boxed(Function<T, Integer>): Use this for nullable database columns or if your POJO uses Integer.
  • .from(Function<T, Integer>): Standard object mapping.

Eliminating Timezone Confusion

One of the most common bugs is the confusion between timestamp and timestamptz.

The API solves this:

  • PostgresTypes.TIMESTAMP only allows .localDateTime().
  • PostgresTypes.TIMESTAMPTZ allows .instant(), .zonedDateTime(), or .offsetDateTime().

Advanced Type Mapping

N-Dimensional Arrays (Matrices & Tensors)

// Mapping a 2D Matrix (Collection of Collections)
.map("data_matrix", PostgresTypes.array2D(PostgresTypes.INT4).from(MyEntity::getMatrix))

// Mapping a 3D Tensor
.map("data_tensor", PostgresTypes.array3D(PostgresTypes.FLOAT8).from(MyEntity::getTensor))

Ranges

// Mapping an integer range
.map("age_limit", PostgresTypes.INT4RANGE.from(MyEntity::getAgeRange))

// Mapping a timestamp range
.map("validity", PostgresTypes.TSRANGE.from(MyEntity::getValidityPeriod))

Geometric Types

Native support for all PostgreSQL geometric types using dedicated helper records:

  • POINT: PostgresTypes.POINT
  • CIRCLE: PostgresTypes.CIRCLE
  • POLYGON: PostgresTypes.POLYGON
  • PATH / LSEG / BOX / LINE
.map("area", PostgresTypes.POLYGON.from(MyEntity::getBoundaries))

Supported PostgreSQL Types

Extension points exported contracts — how you extend this code

BinaryRowWriter (Interface)
(no doc) [2 implementers]
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
PgColumn (Interface)
(no doc) [1 implementers]
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
ToFloatFunction (Interface)
(no doc)
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
ToShortFunction (Interface)
(no doc)
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
ThrowingConsumer (Interface)
(no doc)
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java

Core symbols most depended-on inside this repo

writeInt
called by 45
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
createType
called by 24
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
map
called by 17
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
writeShort
called by 15
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
write
called by 14
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
from
called by 12
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
writeDouble
called by 11
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java
writeLong
called by 7
PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java

Shape

Method 126
Class 18
Interface 16

Languages

Java100%

Modules by API surface

PgBulkInsert/src/main/java/de/bytefish/pgbulkinsert/PgBulkInsert.java146 symbols
PgBulkInsert/src/test/java/de/bytefish/pgbulkinsert/test/IntegrationTest.java7 symbols
PgBulkInsert/src/test/java/de/bytefish/pgbulkinsert/test/FirewallRuleIntegrationTest.java7 symbols

Datastores touched

postgresDatabase · 1 repos

For agents

$ claude mcp add PgBulkInsert \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact