Skip to content

Commit af443ef

Browse files
authored
Merge pull request #3745 from ebean-orm/docs/guides-one
Docs: Add docs / guides for step-by-step instructions for AI agents
2 parents 367eba8 + ad4f027 commit af443ef

File tree

4 files changed

+402
-0
lines changed

4 files changed

+402
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ or [github discussions](https://github.com/ebean-orm/ebean/discussions)
8181
## Documentation
8282
Goto [https://ebean.io/docs/](https://ebean.io/docs/)
8383

84+
## Guides
85+
Step-by-step guides for common tasks: [docs/guides/](docs/guides/README.md)
86+
8487
## Maven central
8588
[Maven central - g:io.ebean](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22io.ebean%22%20)
8689

docs/guides/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Guides
2+
3+
Step-by-step guides written as instructions for AI agents and developers.
4+
5+
## Adding Ebean ORM with PostgreSQL to an existing Maven project
6+
7+
A two-part guide covering everything needed to wire Ebean + PostgreSQL into an
8+
existing Maven project. Complete the steps in order.
9+
10+
| Step | Guide | Description |
11+
|------|-------|-------------|
12+
| 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` |
13+
| 2 | [Database configuration](add-ebean-postgres-database-config.md) | Configure the Ebean `Database` bean using `DataSourceBuilder` and `DatabaseConfig` with Avaje Inject |
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# Guide: Add Ebean ORM (PostgreSQL) to an Existing Maven Project — Step 2: Database Configuration
2+
3+
## Purpose
4+
5+
This guide provides step-by-step instructions for configuring an Ebean `Database` bean
6+
using **Avaje Inject** (`@Factory` / `@Bean`), backed by a PostgreSQL datasource built
7+
with Ebean's `DataSourceBuilder`. Follow every step in order. This is Step 2 of 2.
8+
9+
---
10+
11+
## Prerequisites
12+
13+
- **Step 1 complete**: `pom.xml` already includes `ebean-postgres`, `ebean-maven-plugin`,
14+
and `querybean-generator` (see `add-ebean-postgres-maven-pom.md`)
15+
- **Avaje Inject** is on the classpath (e.g. `io.avaje:avaje-inject`)
16+
- A configuration source is available at runtime (e.g. `avaje-config` reading
17+
`application.yml` or environment variables)
18+
- The following configuration keys are resolvable at runtime (adapt names to your project):
19+
| Key | Description |
20+
|-----|-------------|
21+
| `db_url` | JDBC URL for the master/write connection |
22+
| `db_user` | Database username |
23+
| `db_pass` | Database password |
24+
| `db_master_min_connections` | Minimum pool size (default: 1) |
25+
| `db_master_max_connections` | Maximum pool size (default: 200) |
26+
27+
---
28+
29+
## Step 1 — Locate or create the `@Factory` class
30+
31+
Look for an existing Avaje Inject `@Factory`-annotated class in the project
32+
(often named `AppConfig`, `DatabaseConfig`, or similar). If one exists, add the new
33+
`@Bean` method to it. If none exists, create one:
34+
35+
```java
36+
package com.example.configuration;
37+
38+
import io.avaje.inject.Bean;
39+
import io.avaje.inject.Factory;
40+
41+
@Factory
42+
class DatabaseConfig {
43+
// beans will be added in the steps below
44+
}
45+
```
46+
47+
---
48+
49+
## Step 2 — Add the `Database` bean method (minimal — master datasource only)
50+
51+
Add the following `@Bean` method to the `@Factory` class. This creates an Ebean
52+
`Database` backed by a single master (read-write) PostgreSQL datasource.
53+
54+
```java
55+
import io.ebean.Database;
56+
import io.ebean.config.DatabaseConfig;
57+
import io.ebean.datasource.DataSourceBuilder;
58+
59+
@Bean
60+
Database database() {
61+
var dataSource = DataSourceBuilder.create()
62+
.url(/* resolve from config, e.g.: */ Config.get("db_url"))
63+
.username(Config.get("db_user"))
64+
.password(Config.get("db_pass"))
65+
.driver("org.postgresql.Driver")
66+
.schema("myschema") // set to your target schema
67+
.applicationName("my-app") // visible in pg_stat_activity
68+
.minConnections(Config.getInt("db_master_min_connections", 1))
69+
.maxConnections(Config.getInt("db_master_max_connections", 200));
70+
71+
return new DatabaseConfig()
72+
.name("db") // logical name for this Database instance
73+
.dataSourceBuilder(dataSource)
74+
.build();
75+
}
76+
```
77+
78+
### Field guidance
79+
80+
| Field | Notes |
81+
|-------|-------|
82+
| `url` | Full JDBC URL, e.g. `jdbc:postgresql://host:5432/dbname` |
83+
| `schema` | The Postgres schema Ebean should use (omit if using `public`) |
84+
| `applicationName` | Shown in `pg_stat_activity.application_name`; helps with DB-side diagnostics |
85+
| `name("db")` | Logical Ebean database name; relevant if multiple Database instances exist |
86+
87+
---
88+
89+
## Step 3 — Inject configuration via a constructor or config helper (recommended)
90+
91+
Rather than calling `Config.get(...)` inline, inject a typed config helper or the
92+
Avaje `Configuration` bean if one is available. This makes the factory testable and
93+
keeps the wiring explicit. For example:
94+
95+
```java
96+
@Bean
97+
Database database(Configuration config) {
98+
String url = config.get("db_url");
99+
String user = config.get("db_user");
100+
String pass = config.get("db_pass");
101+
int min = config.getInt("db_master_min_connections", 1);
102+
int max = config.getInt("db_master_max_connections", 200);
103+
104+
var dataSource = DataSourceBuilder.create()
105+
.url(url)
106+
.username(user)
107+
.password(pass)
108+
.driver("org.postgresql.Driver")
109+
.schema("myschema")
110+
.applicationName("my-app")
111+
.minConnections(min)
112+
.maxConnections(max);
113+
114+
return new DatabaseConfig()
115+
.name("db")
116+
.dataSourceBuilder(dataSource)
117+
.skipDataSourceCheck(true)
118+
.build();
119+
}
120+
```
121+
122+
If the project has a dedicated config-wrapper class (a `@Component` that reads config
123+
keys), accept it as a parameter instead of `Configuration`.
124+
125+
---
126+
127+
## Step 4 (Optional) — Add a read-only datasource
128+
129+
For production services that have a separate read-replica, add a second
130+
`DataSourceBuilder` for read-only queries and wire it via
131+
`readOnlyDataSourceBuilder(...)`. The read-only datasource:
132+
133+
- Uses `readOnly(true)` and `autoCommit(true)` (Ebean routes read queries there automatically)
134+
- Typically has a higher max connection count than the master
135+
- Benefits from a prepared-statement cache (`pstmtCacheSize`)
136+
137+
```java
138+
@Bean
139+
Database database(Configuration config) {
140+
String masterUrl = config.get("db_url");
141+
String readOnlyUrl = config.get("db_url_readonly");
142+
String user = config.get("db_user");
143+
String pass = config.get("db_pass");
144+
145+
var masterDataSource = buildDataSource(user, pass)
146+
.url(masterUrl)
147+
.minConnections(config.getInt("db_master_min_connections", 1))
148+
.maxConnections(config.getInt("db_master_max_connections", 50));
149+
150+
var readOnlyDataSource = buildDataSource(user, pass)
151+
.url(readOnlyUrl)
152+
.readOnly(true)
153+
.autoCommit(true)
154+
.pstmtCacheSize(250) // cache up to 250 prepared statements per connection
155+
.maxInactiveTimeSecs(600) // close idle connections after 10 minutes
156+
.minConnections(config.getInt("db_readonly_min_connections", 2))
157+
.initialConnections(config.getInt("db_readonly_initial_connections", 10))
158+
.maxConnections(config.getInt("db_readonly_max_connections", 200));
159+
160+
return new DatabaseConfig()
161+
.name("db")
162+
.dataSourceBuilder(masterDataSource)
163+
.readOnlyDataSourceBuilder(readOnlyDataSource)
164+
.build();
165+
}
166+
167+
private static DataSourceBuilder buildDataSource(String user, String pass) {
168+
return DataSourceBuilder.create()
169+
.username(user)
170+
.password(pass)
171+
.driver("org.postgresql.Driver")
172+
.schema("myschema")
173+
.applicationName("my-app")
174+
.addProperty("prepareThreshold", "2"); // PostgreSQL: server-side prepared statements
175+
}
176+
```
177+
178+
### Additional configuration keys for the read-only datasource
179+
180+
| Key | Description | Default |
181+
|-----|-------------|---------|
182+
| `db_url_readonly` | JDBC URL for the read replica ||
183+
| `db_readonly_min_connections` | Minimum pool size | 2 |
184+
| `db_readonly_initial_connections` | Initial pool size at startup | same as min |
185+
| `db_readonly_max_connections` | Maximum pool size | 20 |
186+
187+
---
188+
189+
## Verification
190+
191+
1. Start the application (or run `mvn test -pl <your-module>`).
192+
2. Look for log output similar to:
193+
194+
```
195+
INFO o.a.datasource.pool.ConnectionPool - DataSourcePool [db] autoCommit[false] min[1] max[5]
196+
INFO io.ebean.internal.DefaultContainer - DatabasePlatform name:db platform:postgres
197+
```
198+
199+
3. If you see `DataSourcePool` and `DatabasePlatform` log lines, Ebean is connected and
200+
the database bean is wired correctly.
201+
202+
---
203+
204+
## Troubleshooting
205+
206+
| Symptom | Likely cause | Fix |
207+
|---------|-------------|-----|
208+
| `ClassNotFoundException: org.postgresql.Driver` | PostgreSQL JDBC driver missing | Add `org.postgresql:postgresql` dependency (see Step 1 guide) |
209+
| `Cannot connect to database` at startup | DB unreachable but `skipDataSourceCheck` is `false` | Set `.skipDataSourceCheck(true)` |
210+
| Ebean enhancement warnings in logs | `ebean-maven-plugin` not configured | Complete Step 1 guide |
211+
| `NullPointerException` reading config key | Config key not defined | Add the key to `application.yml` or environment |

0 commit comments

Comments
 (0)