Skip to content

Commit 6d9ed67

Browse files
committed
Docs: Improve doc guides wording
1 parent 9e2d821 commit 6d9ed67

8 files changed

Lines changed: 68 additions & 53 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ or [github discussions](https://github.com/ebean-orm/ebean/discussions)
8282
Goto [https://ebean.io/docs/](https://ebean.io/docs/)
8383

8484
## Guides
85+
Library reference (capabilities, scope, and AI guidance): [docs/LIBRARY.md](docs/LIBRARY.md)
86+
8587
Step-by-step guides for common tasks: [docs/guides/](docs/guides/README.md)
8688

8789
Available guides:

docs/guides/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ existing Maven project. Complete the steps in order.
2020

2121
| Guide | Description |
2222
|-------|-------------|
23-
| [Entity Bean Creation](entity-bean-creation.md) | How to generate clean, idiomatic Ebean entity beans for AI agents; patterns and anti-patterns; minimal boilerplate without getters/setters; decision tree for code generation |
23+
| [Entity Bean Creation](entity-bean-creation.md) | How to generate clean, idiomatic Ebean entity beans for AI agents; patterns and anti-patterns; field visibility and accessor guidance; minimal boilerplate |
2424
| [Lombok with Ebean entity beans](lombok-with-ebean-entity-beans.md) | Which Lombok annotations to use and avoid on entity beans; why `@Data` is incompatible with Ebean; how to use `@Getter` + `@Setter` + `@Accessors(chain = true)` |
2525

2626
## Querying

docs/guides/add-ebean-postgres-database-config.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This guide provides step-by-step instructions for configuring an Ebean `Database` bean
66
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.
7+
with Ebean's `DataSourceBuilder`. Follow every step in order. This is Step 2 of 3.
88

99
---
1010

@@ -280,3 +280,11 @@ validation best practices, see the [ebean-datasource guides](https://github.com/
280280
| `Cannot connect to database` at startup | DB unreachable but `skipDataSourceCheck` is `false` | Set `.skipDataSourceCheck(true)` |
281281
| Ebean enhancement warnings in logs | `ebean-maven-plugin` not configured | Complete Step 1 guide |
282282
| `NullPointerException` reading config key | Config key not defined | Add the key to `application.yml` or environment |
283+
284+
---
285+
286+
## Next Step
287+
288+
Proceed to **Step 3: Test container setup**
289+
(`add-ebean-postgres-test-container.md`) to wire an injectable test `Database`
290+
backed by `ebean-test` containers.

docs/guides/add-ebean-postgres-maven-pom.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Purpose
44

55
This guide provides step-by-step instructions for modifying an existing Maven `pom.xml`
6-
to add Ebean ORM with PostgreSQL support. Follow every step in order. This is Step 1 of 2.
6+
to add Ebean ORM with PostgreSQL support. Follow every step in order. This is Step 1 of 3.
77

88
---
99

docs/guides/entity-bean-creation.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Target Audience:** AI systems (Claude, Copilot, ChatGPT, etc.)
44
**Purpose:** Learn how to generate clean, idiomatic Ebean entity beans
5-
**Key Insight:** Ebean requires private fields with getters/setters (or other accessors), but they don't need to follow Java bean conventions; no public fields; no equals/hashCode implementation needed
5+
**Key Insight:** Ebean entity fields must be non-public (no public fields). Getters/setters are optional, and if used they don't need JavaBeans naming conventions; no manual equals/hashCode implementation is needed
66
**Language:** Java
77
**Framework:** Ebean ORM
88

@@ -16,7 +16,7 @@ Before writing entity code, remember:
1616
|-------------|---------|-----------------------------------------------------------------------------------------------------------------------|
1717
| `@Entity` annotation |**YES** | Marks class as persistent entity |
1818
| `@Id` annotation |**YES** | Marks primary key field |
19-
| Getters/setters (or other accessors) | **YES** | Required for field access. Don't need to follow Java bean spec; can be record-style, fluent, or any accessor pattern. |
19+
| Getters/setters (or other accessors) | ⚠️ **OPTIONAL** | Ebean can use field access directly. Add accessors when your API/design needs them; naming can be JavaBeans, fluent, or custom. |
2020
| Default constructor |**NO** | Not required. Ebean can instantiate without it. |
2121
| equals/hashCode |**NO** | Ebean auto-enhances these at compile time. |
2222
| toString() |**NO** | Ebean auto-enhances this. Don't implement with getters. |
@@ -26,8 +26,8 @@ Before writing entity code, remember:
2626

2727
**Critical:**
2828
- Prefer primitive `long` for `@Id` and `@Version`, NOT `Long` object.
29-
- Fields should be **private** or **protected** (not public). Access via getters/setters or other accessors.
30-
- Getters/setters do NOT need to follow Java bean conventions.
29+
- Fields should be non-public: **private**, **protected**, or package-private.
30+
- If you add accessors, they do NOT need to follow Java bean conventions.
3131

3232
---
3333

@@ -112,7 +112,7 @@ public class Customer {
112112
-`@Entity` marks it as persistent
113113
-`@Id private long id` is the primary key
114114
- ✅ Private fields (Ebean does NOT support public fields without expert flags enabled)
115-
-Getters/setters don't need to follow Java bean conventions (shown above with setName but no getId setter)
115+
-Accessors can follow any naming convention, and can be omitted when field access is preferred
116116
- ✅ No default constructor needed
117117
- ✅ No equals/hashCode needed (Ebean enhances these)
118118

@@ -640,7 +640,6 @@ public class Customer {
640640

641641
**Why:** Ebean's naming convention handles this automatically. Only use @Column when your database column doesn't match the convention.
642642

643-
---
644643
---
645644

646645
## What Ebean Enhancement Provides
@@ -662,7 +661,7 @@ At compile time, Ebean enhances your entity classes:
662661
**Recommended for ID/Version:**
663662
- `long` (primitive) ✅ Use this
664663
- `int` (primitive) ✅ Use this
665-
- `uuid` (UUID) ✅ Use this
664+
- `UUID` ✅ Use this
666665

667666
**Not recommended:**
668667
- `Long` object ⚠️ Avoid (use primitive long)
@@ -752,19 +751,19 @@ Each step adds only what's necessary. No getters/setters needed unless your desi
752751
### Creating and saving
753752
```java
754753
Customer customer = new Customer();
755-
customer.name = "Alice";
754+
customer.setName("Alice");
756755
DB.save(customer); // id auto-generated
757756
```
758757

759758
### Finding
760759
```java
761760
Customer found = DB.find(Customer.class, 1);
762-
System.out.println(found.name); // Direct field access works
761+
System.out.println(found.getName());
763762
```
764763

765764
### Updating
766765
```java
767-
found.name = "Bob";
766+
found.setName("Bob");
768767
DB.update(found); // version auto-incremented
769768
```
770769

docs/guides/lombok-with-ebean-entity-beans.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Problems caused:
3939

4040
`@Data` generates a `toString()` that accesses **all** fields, including
4141
`@OneToMany` and `@ManyToOne` associations. Accessing an unloaded lazy association
42-
outside of a transaction triggers a `LazyInitializationException` or fires an unexpected
42+
outside of a transaction triggers a `LazyInitialisationException` or fires an unexpected
4343
SQL query, which can:
4444
- Cause subtle bugs in logging statements
4545
- Trigger N+1 queries in test output or debug logging

docs/guides/persisting-and-transactions-with-ebean.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Customer customer = new QCustomer()
9191

9292
customer.setStatus(Customer.Status.ACTIVE);
9393

94-
DB.save(customer);
94+
DB.update(customer);
9595
```
9696

9797
### When to prefer `insert()` over `save()`

docs/guides/testing-with-testentitybuilder.md

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ The `TestEntityBuilder` class is provided by the `ebean-test` module.
2424
<dependency>
2525
<groupId>io.ebean</groupId>
2626
<artifactId>ebean-test</artifactId>
27-
<version>16.5.0</version>
27+
<version>${ebean.version}</version>
2828
<scope>test</scope>
2929
</dependency>
3030
```
3131

3232
**Gradle:**
3333
```gradle
34-
testImplementation 'io.ebean:ebean-test:16.5.0'
34+
testImplementation "io.ebean:ebean-test:${ebeanVersion}"
3535
```
3636

37-
Replace `16.5.0` with the version matching your Ebean installation.
37+
Use a version that matches your Ebean runtime (`ebean.version` /
38+
`ebeanVersion`), or replace with an explicit fixed version if your build does
39+
not centralize dependency versions.
3840

3941
### Import the Class
4042

@@ -64,15 +66,14 @@ The `build()` method creates an instance with populated fields **without persist
6466
Product product = builder.build(Product.class);
6567

6668
// Fields are populated:
67-
// - id: null (not populated)
69+
// - id: unset (typically 0 for primitive long, null for boxed Long)
6870
// - name: random UUID-based string
6971
// - price: random BigDecimal
7072
// - inStock: true
7173
// - createdAt: current instant
7274
// - etc.
7375

74-
// Not persisted yet:
75-
assert product.getId() == null;
76+
// Not persisted yet (`@Id` is still unset until the entity is persisted).
7677
```
7778

7879
### Build and Save (Persist to Database)
@@ -83,7 +84,7 @@ The `save()` method creates, persists, and returns an entity with the database-a
8384
Product product = builder.save(Product.class);
8485

8586
// Entity is now in the database:
86-
assert product.getId() != null;
87+
assert DB.find(Product.class, product.getId()) != null;
8788

8889
// Verify it was saved:
8990
Product found = DB.find(Product.class, product.getId());
@@ -167,7 +168,8 @@ Order order = builder.build(Order.class);
167168
// Both order and customer are built:
168169
assert order != null;
169170
assert order.getCustomer() != null;
170-
assert order.getCustomer().getId() == null; // not persisted yet
171+
// Before persist, @Id values are typically unset
172+
// (0 for primitive IDs, null for boxed IDs).
171173

172174
// When saved, cascade handles both:
173175
Order saved = builder.save(Order.class);
@@ -253,7 +255,7 @@ Subclass `RandomValueGenerator` and override individual `random*()` methods:
253255

254256
```java
255257
class CompanyTestDataGenerator extends RandomValueGenerator {
256-
258+
257259
@Override
258260
protected String randomString(String propName, int maxLength) {
259261
if (propName != null && propName.toLowerCase().contains("email")) {
@@ -267,14 +269,14 @@ class CompanyTestDataGenerator extends RandomValueGenerator {
267269
}
268270
return super.randomString(propName, maxLength);
269271
}
270-
272+
271273
// Override other methods as needed:
272274
@Override
273275
protected Object randomEnum(Class<?> type) {
274276
if (type == OrderStatus.class) {
275277
// Bias towards common statuses for realistic test data
276-
return ThreadLocalRandom.current().nextDouble() < 0.8
277-
? OrderStatus.PENDING
278+
return ThreadLocalRandom.current().nextDouble() < 0.8
279+
? OrderStatus.PENDING
278280
: OrderStatus.COMPLETED;
279281
}
280282
return super.randomEnum(type);
@@ -299,7 +301,7 @@ assert user.getEmail().endsWith("@mycompany.com");
299301

300302
```java
301303
public class MoneyValueGenerator extends RandomValueGenerator {
302-
304+
303305
@Override
304306
protected BigDecimal randomBigDecimal(int precision, int scale) {
305307
// Generate prices in a realistic range: $5.00 to $999.99
@@ -346,7 +348,7 @@ When test requirements demand specific field values, manually override after bui
346348
void whenStockIsLow_thenShowWarning() {
347349
Product product = builder.build(Product.class);
348350
product.setQuantity(2); // Specific value for this test
349-
351+
350352
boolean shouldWarn = product.shouldShowLowStockWarning();
351353
assertThat(shouldWarn).isTrue();
352354
}
@@ -358,23 +360,23 @@ Encapsulate common test entity setups in factory methods:
358360

359361
```java
360362
public class TestDataFactory {
361-
362-
private static final TestEntityBuilder builder =
363+
364+
private static final TestEntityBuilder builder =
363365
TestEntityBuilder.builder(DB.getDefault()).build();
364-
366+
365367
public static Order createPendingOrder() {
366368
Order order = builder.build(Order.class);
367369
order.setStatus(OrderStatus.PENDING);
368370
return order;
369371
}
370-
372+
371373
public static Order createShippedOrder() {
372374
Order order = builder.build(Order.class);
373375
order.setStatus(OrderStatus.SHIPPED);
374376
order.setShippedAt(Instant.now());
375377
return order;
376378
}
377-
379+
378380
public static Order savePendingOrder() {
379381
Order order = createPendingOrder();
380382
DB.save(order);
@@ -400,7 +402,7 @@ void whenFetchingMultipleOrders_thenAllUnique() {
400402
Order order1 = builder.save(Order.class);
401403
Order order2 = builder.save(Order.class);
402404
Order order3 = builder.save(Order.class);
403-
405+
404406
assertThat(order1.getId()).isNotEqualTo(order2.getId());
405407
assertThat(order2.getId()).isNotEqualTo(order3.getId());
406408
assertThat(order1.getOrderNumber()).isNotEqualTo(order2.getOrderNumber());
@@ -416,31 +418,31 @@ void whenFetchingMultipleOrders_thenAllUnique() {
416418
```java
417419
@SpringBootTest
418420
class OrderRepositoryTest {
419-
421+
420422
@Autowired
421423
private OrderRepository orderRepository;
422-
424+
423425
private TestEntityBuilder builder;
424-
426+
425427
@BeforeEach
426428
void setup() {
427429
builder = TestEntityBuilder.builder(DB.getDefault()).build();
428430
}
429-
431+
430432
@Test
431433
void whenFindingOrdersByStatus_thenReturnsMatching() {
432434
// Create test data quickly
433435
Order pending1 = builder.build(Order.class);
434436
pending1.setStatus(OrderStatus.PENDING);
435-
437+
436438
Order pending2 = builder.build(Order.class);
437439
pending2.setStatus(OrderStatus.PENDING);
438-
440+
439441
Order shipped = builder.build(Order.class);
440442
shipped.setStatus(OrderStatus.SHIPPED);
441-
443+
442444
DB.saveAll(pending1, pending2, shipped);
443-
445+
444446
// Test the query
445447
List<Order> pending = orderRepository.findByStatus(OrderStatus.PENDING);
446448
assertThat(pending).hasSize(2);
@@ -454,12 +456,13 @@ class OrderRepositoryTest {
454456
@Test
455457
void whenBuildingOrderWithCustomer_thenBothPopulated() {
456458
Order order = builder.build(Order.class);
457-
459+
458460
// Customer is recursively built because of @ManyToOne(cascade=PERSIST)
459461
assertThat(order.getCustomer()).isNotNull();
460-
assertThat(order.getCustomer().getId()).isNull(); // not persisted yet
462+
// Before persist, @Id values are typically unset
463+
// (0 for primitive IDs, null for boxed IDs).
461464
assertThat(order.getCustomer().getName()).isNotNull();
462-
465+
463466
// Saving cascades to customer:
464467
Order saved = builder.save(Order.class);
465468
assertThat(saved.getId()).isNotNull();
@@ -486,7 +489,7 @@ void usingCustomGenerator() {
486489
TestEntityBuilder builder = TestEntityBuilder.builder(DB.getDefault())
487490
.valueGenerator(new ECommerceTestDataGenerator())
488491
.build();
489-
492+
490493
Product product = builder.build(Product.class);
491494
assertThat(product.getPrice())
492495
.isBetween(BigDecimal.TEN, BigDecimal.valueOf(500.0));
@@ -510,19 +513,22 @@ public class Product {
510513
}
511514
```
512515

513-
### Fields are null even though I expected them to be populated
516+
### Fields are unset even though I expected them to be populated
514517

515518
**Cause:** `TestEntityBuilder` does **not** populate:
516-
- `@Id` fields (identity/primary key)
517-
- `@Version` fields (optimistic locking)
519+
- `@Id` fields (identity/primary key; left unset until persist)
520+
- `@Version` fields (optimistic locking; left unset until persist)
518521
- `@Transient` fields
519522
- `@OneToMany` collections
520523
- Non-cascade `@ManyToOne` relationships
521524

522-
**Solution:** Set these fields manually in your test if needed:
525+
**Solution:** Set only the fields your test scenario cares about, then persist.
526+
`@Id` and `@Version` are usually database-managed and should typically be left
527+
unset before save:
523528
```java
524529
Product product = builder.build(Product.class);
525-
product.setId(1L); // Set @Id manually
530+
product.setName("specific-name"); // test-specific override
531+
DB.save(product); // database assigns @Id/@Version
526532
```
527533

528534
### Building recursive relationships causes StackOverflowError
@@ -543,7 +549,7 @@ person.getOrganization().setFounder(null); // Break cycle
543549
```java
544550
class DeterministicTestDataGenerator extends RandomValueGenerator {
545551
private int counter = 0;
546-
552+
547553
@Override
548554
protected String randomString(String propName, int maxLength) {
549555
return "test_" + (counter++);

0 commit comments

Comments
 (0)