This guide provides step-by-step instructions for configuring an Ebean Database bean
using Avaje Inject (@Factory / @Bean), backed by a PostgreSQL datasource built
with Ebean's DataSourceBuilder. Follow every step in order. This is Step 2 of 2.
- Step 1 complete:
pom.xmlalready includesebean-postgres,ebean-maven-plugin, andquerybean-generator(seeadd-ebean-postgres-maven-pom.md) - Avaje Inject is on the classpath (e.g.
io.avaje:avaje-inject) - A configuration source is available at runtime (e.g.
avaje-configreadingapplication.ymlor environment variables) - The following configuration keys are resolvable at runtime (adapt names to your project):
Key Description db_urlJDBC URL for the master/write connection db_userDatabase username db_passDatabase password db_master_min_connectionsMinimum pool size (default: 1) db_master_max_connectionsMaximum pool size (default: 200)
Look for an existing Avaje Inject @Factory-annotated class in the project
(often named AppConfig, DatabaseConfig, or similar). If one exists, add the new
@Bean method to it. If none exists, create one:
package com.example.configuration;
import io.avaje.inject.Bean;
import io.avaje.inject.Factory;
@Factory
class DatabaseConfig {
// beans will be added in the steps below
}Add the following @Bean method to the @Factory class. This creates an Ebean
Database backed by a single master (read-write) PostgreSQL datasource.
import io.ebean.Database;
import io.ebean.config.DatabaseConfig;
import io.ebean.datasource.DataSourceBuilder;
@Bean
Database database() {
var dataSource = DataSourceBuilder.create()
.url(/* resolve from config, e.g.: */ Config.get("db_url"))
.username(Config.get("db_user"))
.password(Config.get("db_pass"))
.driver("org.postgresql.Driver")
.schema("myschema") // set to your target schema
.applicationName("my-app") // visible in pg_stat_activity
.minConnections(Config.getInt("db_master_min_connections", 1))
.maxConnections(Config.getInt("db_master_max_connections", 200));
return new DatabaseConfig()
.name("db") // logical name for this Database instance
.dataSourceBuilder(dataSource)
.build();
}| Field | Notes |
|---|---|
url |
Full JDBC URL, e.g. jdbc:postgresql://host:5432/dbname |
schema |
The Postgres schema Ebean should use (omit if using public) |
applicationName |
Shown in pg_stat_activity.application_name; helps with DB-side diagnostics |
name("db") |
Logical Ebean database name; relevant if multiple Database instances exist |
Rather than calling Config.get(...) inline, inject a typed config helper or the
Avaje Configuration bean if one is available. This makes the factory testable and
keeps the wiring explicit. For example:
@Bean
Database database(Configuration config) {
String url = config.get("db_url");
String user = config.get("db_user");
String pass = config.get("db_pass");
int min = config.getInt("db_master_min_connections", 1);
int max = config.getInt("db_master_max_connections", 200);
var dataSource = DataSourceBuilder.create()
.url(url)
.username(user)
.password(pass)
.driver("org.postgresql.Driver")
.schema("myschema")
.applicationName("my-app")
.minConnections(min)
.maxConnections(max);
return new DatabaseConfig()
.name("db")
.dataSourceBuilder(dataSource)
.skipDataSourceCheck(true)
.build();
}If the project has a dedicated config-wrapper class (a @Component that reads config
keys), accept it as a parameter instead of Configuration.
For production services that have a separate read-replica, add a second
DataSourceBuilder for read-only queries and wire it via
readOnlyDataSourceBuilder(...). The read-only datasource:
- Uses
readOnly(true)andautoCommit(true)(Ebean routes read queries there automatically) - Typically has a higher max connection count than the master
- Benefits from a prepared-statement cache (
pstmtCacheSize)
@Bean
Database database(Configuration config) {
String masterUrl = config.get("db_url");
String readOnlyUrl = config.get("db_url_readonly");
String user = config.get("db_user");
String pass = config.get("db_pass");
var masterDataSource = buildDataSource(user, pass)
.url(masterUrl)
.minConnections(config.getInt("db_master_min_connections", 1))
.maxConnections(config.getInt("db_master_max_connections", 50));
var readOnlyDataSource = buildDataSource(user, pass)
.url(readOnlyUrl)
.readOnly(true)
.autoCommit(true)
.pstmtCacheSize(250) // cache up to 250 prepared statements per connection
.maxInactiveTimeSecs(600) // close idle connections after 10 minutes
.minConnections(config.getInt("db_readonly_min_connections", 2))
.initialConnections(config.getInt("db_readonly_initial_connections", 10))
.maxConnections(config.getInt("db_readonly_max_connections", 200));
return new DatabaseConfig()
.name("db")
.dataSourceBuilder(masterDataSource)
.readOnlyDataSourceBuilder(readOnlyDataSource)
.build();
}
private static DataSourceBuilder buildDataSource(String user, String pass) {
return DataSourceBuilder.create()
.username(user)
.password(pass)
.driver("org.postgresql.Driver")
.schema("myschema")
.applicationName("my-app")
.addProperty("prepareThreshold", "2"); // PostgreSQL: server-side prepared statements
}| Key | Description | Default |
|---|---|---|
db_url_readonly |
JDBC URL for the read replica | — |
db_readonly_min_connections |
Minimum pool size | 2 |
db_readonly_initial_connections |
Initial pool size at startup | same as min |
db_readonly_max_connections |
Maximum pool size | 20 |
-
Start the application (or run
mvn test -pl <your-module>). -
Look for log output similar to:
INFO o.a.datasource.pool.ConnectionPool - DataSourcePool [db] autoCommit[false] min[1] max[5] INFO io.ebean.internal.DefaultContainer - DatabasePlatform name:db platform:postgres -
If you see
DataSourcePoolandDatabasePlatformlog lines, Ebean is connected and the database bean is wired correctly.
| Symptom | Likely cause | Fix |
|---|---|---|
ClassNotFoundException: org.postgresql.Driver |
PostgreSQL JDBC driver missing | Add org.postgresql:postgresql dependency (see Step 1 guide) |
Cannot connect to database at startup |
DB unreachable but skipDataSourceCheck is false |
Set .skipDataSourceCheck(true) |
| Ebean enhancement warnings in logs | ebean-maven-plugin not configured |
Complete Step 1 guide |
NullPointerException reading config key |
Config key not defined | Add the key to application.yml or environment |