Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ or [github discussions](https://github.com/ebean-orm/ebean/discussions)
## Documentation
Goto [https://ebean.io/docs/](https://ebean.io/docs/)

## Guides
Step-by-step guides for common tasks: [docs/guides/](docs/guides/README.md)

## Maven central
[Maven central - g:io.ebean](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22io.ebean%22%20)

Expand Down
13 changes: 13 additions & 0 deletions docs/guides/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Guides

Step-by-step guides written as instructions for AI agents and developers.

## Adding Ebean ORM with PostgreSQL to an existing Maven project

A two-part guide covering everything needed to wire Ebean + PostgreSQL into an
existing Maven project. Complete the steps in order.

| Step | Guide | Description |
|------|-------|-------------|
| 1 | [Maven POM setup](add-ebean-postgres-maven-pom.md) | Add Ebean dependencies, the enhancement plugin, and the querybean-generator annotation processor to `pom.xml` |
| 2 | [Database configuration](add-ebean-postgres-database-config.md) | Configure the Ebean `Database` bean using `DataSourceBuilder` and `DatabaseConfig` with Avaje Inject |
211 changes: 211 additions & 0 deletions docs/guides/add-ebean-postgres-database-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# Guide: Add Ebean ORM (PostgreSQL) to an Existing Maven Project — Step 2: Database Configuration

## Purpose

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.

---

## Prerequisites

- **Step 1 complete**: `pom.xml` already includes `ebean-postgres`, `ebean-maven-plugin`,
and `querybean-generator` (see `add-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-config` reading
`application.yml` or environment variables)
- The following configuration keys are resolvable at runtime (adapt names to your project):
| Key | Description |
|-----|-------------|
| `db_url` | JDBC URL for the master/write connection |
| `db_user` | Database username |
| `db_pass` | Database password |
| `db_master_min_connections` | Minimum pool size (default: 1) |
| `db_master_max_connections` | Maximum pool size (default: 200) |

---

## Step 1 — Locate or create the `@Factory` class

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:

```java
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
}
```

---

## Step 2 — Add the `Database` bean method (minimal — master datasource only)

Add the following `@Bean` method to the `@Factory` class. This creates an Ebean
`Database` backed by a single master (read-write) PostgreSQL datasource.

```java
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 guidance

| 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 |

---

## Step 3 — Inject configuration via a constructor or config helper (recommended)

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:

```java
@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`.

---

## Step 4 (Optional) — Add a read-only datasource

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)` and `autoCommit(true)` (Ebean routes read queries there automatically)
- Typically has a higher max connection count than the master
- Benefits from a prepared-statement cache (`pstmtCacheSize`)

```java
@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
}
```

### Additional configuration keys for the read-only datasource

| 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 |

---

## Verification

1. Start the application (or run `mvn test -pl <your-module>`).
2. 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
```

3. If you see `DataSourcePool` and `DatabasePlatform` log lines, Ebean is connected and
the database bean is wired correctly.

---

## Troubleshooting

| 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 |
Loading
Loading