Skip to content

Commit cdf88f6

Browse files
committed
talk about events and about EntityListeners in the intro doc
1 parent cacd1a7 commit cdf88f6

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

documentation/src/main/asciidoc/introduction/Interacting.adoc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,57 @@ The `Connection` passed to the work is the same connection being used by the ses
13931393
In a container environment where transactions and database connections are managed by the container, this might not be the easiest way to obtain the JDBC connection.
13941394
====
13951395

1396+
[[callbacks]]
1397+
=== Lifecycle callbacks and entity listeners
1398+
1399+
The annotations `@PrePersist`, `@PreRemove`, `@PreUpdate`, `@PostPersist`, `@PostRemove`, `@PostUpdate`, and `@PostLoad` allow an entity to respond to persistence lifecycle operations and maintain its transient internal state.
1400+
For example:
1401+
1402+
[source,java]
1403+
----
1404+
@Entity
1405+
class Order {
1406+
...
1407+
transient double total;
1408+
1409+
@PostLoad
1410+
void computeTotal() {
1411+
total = items.stream().mapToDouble(i -> i.price * i.quantity).sum();
1412+
}
1413+
1414+
...
1415+
}
1416+
----
1417+
1418+
If we need to interact with technical objects, we can place the lifecycle callback on a separate class, called an _entity listener_.
1419+
The `@EntityListeners` annotation specifies the listeners for a given entity class:
1420+
1421+
[source,java]
1422+
----
1423+
@Entity
1424+
@EntityListeners(OrderEvents.class)
1425+
class Order { ... }
1426+
----
1427+
1428+
An entity listener may inject CDI beans:
1429+
1430+
[source,java]
1431+
----
1432+
// entity listener class
1433+
class OrderEvents {
1434+
@Inject
1435+
Event<NewOrder> newOrderEvent;
1436+
1437+
@PostPersist
1438+
void newOrder(Order order) {
1439+
// send a CDI event
1440+
newOrderEvent.fire(new NewOrder(order));
1441+
}
1442+
}
1443+
----
1444+
A single entity listener class may even be a generic listener that receives lifecycle callbacks for multiple different entity classes.
1445+
1446+
13961447
[[advice]]
13971448
=== What to do when things go wrong
13981449

documentation/src/main/asciidoc/introduction/Introduction.adoc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,33 @@ Take your time with this code, and try to produce a Java model that's as close a
116116
When in the slightest doubt, map a foreign key relationship using `@ManyToOne` with `@OneToMany(mappedBy=...)` in preference to more complicated association mappings.
117117
====
118118

119+
.What sort of logic belongs in an entity?
120+
****
121+
There exists an extensive online literature which posits that there are _rich domain model_, where entities have methods implementing interesting business logic, and _anemic domain models_, where the entities are pure data holders, and that a developer should hold an opinion that one or the other of these sorts of domain model is "better".
122+
123+
We do not hold any such opinion, and if you ask us for one, we will most likely suddenly discover somewhere else we need to be.
124+
125+
A more interesting question is not _how much_ logic belongs in the entity class, but _what sort_ of logic belongs there.
126+
We think the answer is that an entity should never implement technical concerns, and should never obtain references to framework objects.
127+
Nor should it hold extra mutable state which is not very directly related to its role in representing persistent state.
128+
For example:
129+
130+
- an entity may compute totals and averages, even caching them if necessary, enforce its invariants, interact with and construct other entities, and so on,
131+
- but the entity should never call the `EntityManager` or a Jakarta Data repository, build a criteria query, send a JMS message, start a transaction, publish events to the CDI event bus, maintain a stateful queue of events to be published later, or anything of a similar nature.
132+
133+
One way to summarize this is:
134+
135+
> Entities do business logic; but they don't do orchestration.
136+
137+
So which code is responsible for orchestration of things like transaction management, query execution, or event publication?
138+
Well, that's what we're about to discuss.
139+
****
140+
119141
The second part of the code is much trickier to get right. This code must:
120142

121143
- manage transactions and sessions,
122144
- interact with the database via the Hibernate session,
145+
- publish CDI events and send JMS messages,
123146
- fetch and prepare data needed by the UI, and
124147
- handle failures.
125148

@@ -128,6 +151,12 @@ The second part of the code is much trickier to get right. This code must:
128151
Responsibility for transaction and session management, and for recovery from certain kinds of failure, is best handled in some sort of framework code.
129152
====
130153

154+
[TIP]
155+
====
156+
A great way to handle CDI event publication is via a <<callbacks,JPA entity listener>>.
157+
Whereas we would never want to inject a CDI https://jakarta.ee/specifications/cdi/3.0/apidocs/[event publisher] into an entity object, it's perfectly fine to inject them in an entity listener.
158+
====
159+
131160
We're going to <<organizing-persistence,come back soon>> to the thorny question of how this persistence logic should be organized, and how it should fit into the rest of the system.
132161
// First we want to make the ideas above concrete by seeing a simple example program that uses Hibernate in isolation.
133162

0 commit comments

Comments
 (0)