How Chat2DB's SPI Plugin System Supports Over 35 Databases
Chat2DB leverages Java's Service Provider Interface (SPI) mechanism to discover database drivers at runtime via ServiceLoader, enabling support for over 35 databases through pluggable JAR modules that require no changes to the core application code.
The OtterMind/Chat2DB repository implements a highly extensible architecture that decouples database-specific logic from the core application through a unified plugin system. By defining a strict service contract in the ai.chat2db.spi.IPlugin interface, Chat2DB allows each database to ship its own implementation as an independent module, enabling horizontal scalability where adding support for a new database simply means dropping a JAR file into the classpath.
The Core SPI Contract: IPlugin Interface
At the heart of the system lies the IPlugin interface located in chat2db-community-server/chat2db-community-spi/src/main/java/ai/chat2db/spi/IPlugin.java. This interface defines the contract that every database plugin must fulfill, ensuring the core application can interact with any database through a consistent API.
Every plugin implementation must provide these essential methods:
getDBConfig()– Returns aDBConfigobject containing the database type, URL pattern, default port, and driver class name.getDbMetaData()– Supplies anIDbMetaDataimplementation that handles catalog, table, column, and routine discovery for the specific dialect.getDbManager()– Provides anIDbManagerfor connection lifecycle management and DDL execution.getSqlBuilder(),getValueProcessor(),getSQLIdentifierProcessor()– Expose dialect-specific SQL construction, value quoting, and identifier quoting strategies.getCommandExecutor(),getKeyOperations()– Define command execution patterns and primary key operations.
Optional extension hooks include getSqlSyntaxPlugin(), getAccountManager(), and getRoutineManager(), which allow plugins to inject custom syntax highlighting, cloud account handling, or stored procedure management capabilities.
Runtime Discovery via ServiceLoader
The central class Chat2DBContext handles plugin discovery dynamically using Java's standard ServiceLoader mechanism. Located in chat2db-community-server/chat2db-community-spi/src/main/java/ai/chat2db/spi/sql/Chat2DBContext.java, this class scans the classpath for service provider registrations at startup:
ServiceLoader<IPlugin> loader = ServiceLoader.load(IPlugin.class);
for (IPlugin plugin : loader) {
DBConfig cfg = plugin.getDBConfig();
PLUGIN_MAP.put(cfg.getType(), plugin);
}
The ServiceLoader inspects all JAR files for a text file at META-INF/services/ai.chat2db.spi.IPlugin. Each plugin module contains exactly one such file listing the fully-qualified class name of its implementation. For example, the MySQL plugin registers itself by including the following line in its service file:
ai.chat2db.plugin.mysql.MySQLPlugin
This design allows the core application to aggregate dozens of drivers automatically. The repository ships with a chat2db-community-plugins directory containing ready-made modules for MySQL, PostgreSQL, Oracle, SQL Server, SQLite, ClickHouse, MongoDB, Redis, Hive, Snowflake, and others.
Plugin Implementation Architecture
Each plugin module follows a consistent pattern that maximizes code reuse while allowing dialect-specific customization. The chat2db-community-plugins directory contains independent Maven modules where each database implementation extends default helpers or provides specialized subclasses.
The DefaultMetaService class in chat2db-community-server/chat2db-community-spi/src/main/java/ai/chat2db/spi/DefaultMetaService.java provides generic metadata functionality that plugins can inherit. For example, the MySQL plugin implements IPlugin in MySQLPlugin.java but delegates standard operations to DefaultMetaService while overriding specific behaviors through MySQLMetaData and MySQLSqlBuilder classes.
This layered approach means plugin authors only implement methods where their database diverges from standard JDBC behavior. Common operations like connection pooling or basic result set iteration require no custom code, as the core SPI provides sensible defaults.
Adding a New Database: Implementation Guide
Creating support for a new database requires only three components: an implementation class, a service registration file, and a JAR deployment. Here is the complete workflow for registering a custom database called "mycustom".
First, implement the IPlugin interface:
package ai.chat2db.plugin.mycustom;
import ai.chat2db.spi.*;
public class MyCustomPlugin implements IPlugin {
@Override
public DBConfig getDBConfig() {
return new DBConfig("mycustom", "jdbc:mycustom://{host}:{port}/{db}");
}
@Override
public IDbMetaData getDbMetaData() {
return new MyCustomMetaData();
}
@Override
public IDbManager getDbManager() {
return new MyCustomDBManager();
}
}
Next, create the service registration file at src/main/resources/META-INF/services/ai.chat2db.spi.IPlugin with the content:
ai.chat2db.plugin.mycustom.MyCustomPlugin
Finally, compile the module into a JAR and place it in the chat2db-community-plugins/ directory or add it to the application classpath. Upon restart, Chat2DBContext automatically detects the new plugin through ServiceLoader, making the database immediately selectable in the UI without modifying any core application code.
Summary
- Service Provider Interface architecture enables Chat2DB to support over 35 databases through a standardized
IPlugincontract defined in the SPI module. - Runtime discovery occurs via Java's
ServiceLoader, which scansMETA-INF/services/ai.chat2db.spi.IPluginfiles across the classpath to register available drivers. - Horizontal scalability allows new database support by simply adding plugin JARs to the
chat2db-community-pluginsdirectory, with no core code changes required. - Default implementations in
DefaultMetaServiceminimize boilerplate, requiring plugins to override only dialect-specific behaviors. - Unified access means the UI, query engine, and export tools interact with all databases through the consistent
IPlugininterface, regardless of underlying driver complexity.
Frequently Asked Questions
How does Chat2DB discover new plugins without modifying core code?
Chat2DB uses Java's standard ServiceLoader mechanism to scan the classpath for JARs containing META-INF/services/ai.chat2db.spi.IPlugin registration files. When the application starts, Chat2DBContext loads all implementations of the IPlugin interface found in these files and maps them to their respective DBConfig types, enabling zero-configuration plugin discovery at runtime.
What is the minimum implementation required for a functional database plugin?
At minimum, a plugin must implement the IPlugin interface and provide concrete implementations for getDBConfig(), getDbMetaData(), and getDbManager(). These methods supply the connection configuration, metadata retrieval logic, and connection management required to interact with the database. Optional methods like getSqlBuilder() can inherit from DefaultMetaService if standard JDBC behavior suffices.
Can multiple versions of the same database driver coexist in Chat2DB?
Yes, because each plugin is isolated in its own JAR with a unique implementation class, you can deploy multiple versions or variants of database drivers simultaneously. The PLUGIN_MAP in Chat2DBContext keys plugins by their DBConfig type string, allowing distinct entries for different versions or dialects of the same underlying database system.
Where should custom enterprise plugins be deployed in production?
Place custom plugin JAR files in the chat2db-community-plugins/ directory alongside the bundled open-source drivers, or include them in the application classpath through standard Java classpath configuration. Ensure the JAR contains the required META-INF/services/ai.chat2db.spi.IPlugin file so ServiceLoader can discover the implementation during the initialization phase in Chat2DBContext.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →