You are viewing documentation for an older version of Lucille.

This is a static snapshot.
For up-to-date information, see the latest version.

Database Connector

A Connector that reads rows from a JDBC-compatible database and publishes each row as a Lucille Document.

Source Code

The DatabaseConnector reads rows from any JDBC-compatible relational database and publishes each row as a Lucille Document. Column names become field names on the Document.

Basic Configuration

connectors: [
  {
    name: "db-connector"
    class: "com.kmwllc.lucille.connector.jdbc.DatabaseConnector"
    pipeline: "my-pipeline"

    driver: "org.postgresql.Driver"
    connectionString: "jdbc:postgresql://localhost:5432/mydb"
    jdbcUser: "username"
    jdbcPassword: ${?DB_PASSWORD}
    sql: "SELECT id, title, body, published_at FROM articles WHERE active = true"
    idField: "id"
  }
]

Configuration Parameters

ParameterTypeRequiredDescription
driverStringYesJDBC driver class name. The driver JAR must be on the classpath.
connectionStringStringYesJDBC connection URL.
jdbcUserStringNoDatabase username.
jdbcPasswordStringNoDatabase password. Use ${?VAR} for environment variable substitution.
sqlStringYesSELECT statement to execute. All returned rows are published as Documents.
idFieldStringNoColumn whose value becomes the Document ID. If omitted, a UUID is generated per row.
docIdPrefixStringNoPrefix prepended to every Document ID.
fetchSizeIntegerNoJDBC fetch size hint for streaming large result sets. For MySQL, set to Integer.MIN_VALUE (i.e., -2147483648) to avoid buffering the full result set in memory.
preSQLStringNoA SQL statement (INSERT, DELETE, UPDATE, or DDL) executed once before the main query. Useful for creating temp tables, acquiring locks, or seeding data.
postSQLStringNoA SQL statement executed once after the main query completes successfully. Useful for cleanup, releasing locks, or writing completion markers.
otherSQLsList<String>NoAdditional SELECT queries to JOIN onto the primary result. Each query must return rows ordered by its join key.
otherJoinFieldsList<String>NoJoin fields parallel to otherSQLs. Required when otherSQLs is specified. Must be integer-valued columns.
ignoreColumnsList<String>NoColumn names to skip when populating Documents.
connectionRetriesInteger1Number of connection retry attempts on failure.
connectionRetryPauseInteger10000Milliseconds to wait between connection retries.

Pre and Post SQL

Use preSQL and postSQL to run setup and teardown logic that must happen before and after the main query:

{
  name: "db-connector"
  class: "com.kmwllc.lucille.connector.jdbc.DatabaseConnector"
  pipeline: "my-pipeline"

  driver: "org.postgresql.Driver"
  connectionString: "jdbc:postgresql://localhost:5432/mydb"
  jdbcUser: ${?DB_USER}
  jdbcPassword: ${?DB_PASSWORD}

  preSQL: "CREATE TEMP TABLE export_snapshot AS SELECT * FROM articles WHERE active = true"
  sql: "SELECT id, title, body FROM export_snapshot ORDER BY id"
  postSQL: "DROP TABLE IF EXISTS export_snapshot"
  idField: "id"
}

postSQL runs only if preSQL and the main query both succeed. If the connector throws, postSQL is skipped but close() is always called.

Multi-Query Joins

otherSQLs allows you to enrich primary rows with data from additional queries at read time, without a SQL JOIN. Each secondary query must be ordered by its join key (which must be an integer column matching a column in the primary query).

{
  sql: "SELECT id, title FROM articles ORDER BY id"
  idField: "id"
  otherSQLs: ["SELECT article_id, tag_name FROM article_tags ORDER BY article_id"]
  otherJoinFields: ["article_id"]
}

For each primary row, the connector merges matching rows from otherSQLs as multi-valued fields onto the Document.

Incremental Ingest

The DatabaseConnector does not maintain internal state. For incremental ingest (only rows modified since the last run), filter at the SQL level:

SELECT id, title, updated_at FROM articles
WHERE updated_at > TIMESTAMP '2025-01-01 00:00:00'
ORDER BY updated_at ASC

Store the high-water mark externally (e.g., in a config file, environment variable, or the database itself) and substitute it via HOCON variable substitution.

MySQL Streaming

For large MySQL tables, set fetchSize to avoid loading the entire result set into memory:

{
  driver: "com.mysql.cj.jdbc.Driver"
  connectionString: "jdbc:mysql://localhost:3306/mydb?useCursorFetch=true"
  fetchSize: -2147483648
  sql: "SELECT id, title FROM large_table ORDER BY id"
  idField: "id"
}

Common JDBC Drivers

DatabaseDriver ClassMaven Artifact
PostgreSQLorg.postgresql.Driverorg.postgresql:postgresql
MySQLcom.mysql.cj.jdbc.Drivercom.mysql:mysql-connector-j
SQL Servercom.microsoft.sqlserver.jdbc.SQLServerDrivercom.microsoft.sqlserver:mssql-jdbc
SQLiteorg.sqlite.JDBCorg.xerial:sqlite-jdbc
Apache Derbyorg.apache.derby.iapi.jdbc.AutoloadedDriverorg.apache.derby:derby
H2org.h2.Drivercom.h2database:h2

Integration with QueryDatabase Stage

For per-document database enrichment (joining a lookup table for each document mid-pipeline, rather than reading a source table), use the QueryDatabase Stage.