talk about events and about EntityListeners in the intro doc
This commit is contained in:
parent
cacd1a7ecc
commit
cdf88f6a31
|
@ -1393,6 +1393,57 @@ The `Connection` passed to the work is the same connection being used by the ses
|
|||
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.
|
||||
====
|
||||
|
||||
[[callbacks]]
|
||||
=== Lifecycle callbacks and entity listeners
|
||||
|
||||
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.
|
||||
For example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
class Order {
|
||||
...
|
||||
transient double total;
|
||||
|
||||
@PostLoad
|
||||
void computeTotal() {
|
||||
total = items.stream().mapToDouble(i -> i.price * i.quantity).sum();
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
If we need to interact with technical objects, we can place the lifecycle callback on a separate class, called an _entity listener_.
|
||||
The `@EntityListeners` annotation specifies the listeners for a given entity class:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Entity
|
||||
@EntityListeners(OrderEvents.class)
|
||||
class Order { ... }
|
||||
----
|
||||
|
||||
An entity listener may inject CDI beans:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// entity listener class
|
||||
class OrderEvents {
|
||||
@Inject
|
||||
Event<NewOrder> newOrderEvent;
|
||||
|
||||
@PostPersist
|
||||
void newOrder(Order order) {
|
||||
// send a CDI event
|
||||
newOrderEvent.fire(new NewOrder(order));
|
||||
}
|
||||
}
|
||||
----
|
||||
A single entity listener class may even be a generic listener that receives lifecycle callbacks for multiple different entity classes.
|
||||
|
||||
|
||||
[[advice]]
|
||||
=== What to do when things go wrong
|
||||
|
||||
|
|
|
@ -116,10 +116,33 @@ Take your time with this code, and try to produce a Java model that's as close a
|
|||
When in the slightest doubt, map a foreign key relationship using `@ManyToOne` with `@OneToMany(mappedBy=...)` in preference to more complicated association mappings.
|
||||
====
|
||||
|
||||
.What sort of logic belongs in an entity?
|
||||
****
|
||||
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".
|
||||
|
||||
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.
|
||||
|
||||
A more interesting question is not _how much_ logic belongs in the entity class, but _what sort_ of logic belongs there.
|
||||
We think the answer is that an entity should never implement technical concerns, and should never obtain references to framework objects.
|
||||
Nor should it hold extra mutable state which is not very directly related to its role in representing persistent state.
|
||||
For example:
|
||||
|
||||
- an entity may compute totals and averages, even caching them if necessary, enforce its invariants, interact with and construct other entities, and so on,
|
||||
- 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.
|
||||
|
||||
One way to summarize this is:
|
||||
|
||||
> Entities do business logic; but they don't do orchestration.
|
||||
|
||||
So which code is responsible for orchestration of things like transaction management, query execution, or event publication?
|
||||
Well, that's what we're about to discuss.
|
||||
****
|
||||
|
||||
The second part of the code is much trickier to get right. This code must:
|
||||
|
||||
- manage transactions and sessions,
|
||||
- interact with the database via the Hibernate session,
|
||||
- publish CDI events and send JMS messages,
|
||||
- fetch and prepare data needed by the UI, and
|
||||
- handle failures.
|
||||
|
||||
|
@ -128,6 +151,12 @@ The second part of the code is much trickier to get right. This code must:
|
|||
Responsibility for transaction and session management, and for recovery from certain kinds of failure, is best handled in some sort of framework code.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
A great way to handle CDI event publication is via a <<callbacks,JPA entity listener>>.
|
||||
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.
|
||||
====
|
||||
|
||||
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.
|
||||
// First we want to make the ideas above concrete by seeing a simple example program that uses Hibernate in isolation.
|
||||
|
||||
|
|
Loading…
Reference in New Issue