You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: documentation/src/main/asciidoc/introduction/Interacting.adoc
+51Lines changed: 51 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1393,6 +1393,57 @@ The `Connection` passed to the work is the same connection being used by the ses
1393
1393
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.
1394
1394
====
1395
1395
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.
Copy file name to clipboardExpand all lines: documentation/src/main/asciidoc/introduction/Introduction.adoc
+29Lines changed: 29 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -116,10 +116,33 @@ Take your time with this code, and try to produce a Java model that's as close a
116
116
When in the slightest doubt, map a foreign key relationship using `@ManyToOne` with `@OneToMany(mappedBy=...)` in preference to more complicated association mappings.
117
117
====
118
118
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
+
119
141
The second part of the code is much trickier to get right. This code must:
120
142
121
143
- manage transactions and sessions,
122
144
- interact with the database via the Hibernate session,
145
+
- publish CDI events and send JMS messages,
123
146
- fetch and prepare data needed by the UI, and
124
147
- handle failures.
125
148
@@ -128,6 +151,12 @@ The second part of the code is much trickier to get right. This code must:
128
151
Responsibility for transaction and session management, and for recovery from certain kinds of failure, is best handled in some sort of framework code.
129
152
====
130
153
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
+
131
160
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.
132
161
// First we want to make the ideas above concrete by seeing a simple example program that uses Hibernate in isolation.
0 commit comments