<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<font size="4" face="monospace">I think the entity-modeling story
here is more like:<br>
<br>
- a regular class that associates (id, PersonInfo), where the ids
are dispensed exclusively by the ORM, and<br>
- a record/carrier for PersonInfo, that lets you "mutate" the
information but not the ID association. <br>
<br>
<br>
</font><br>
<div class="moz-cite-prefix">On 1/26/2026 10:52 AM, Aaryn Tonita
wrote:<br>
</div>
<blockquote type="cite" cite="mid:zwD8cpG_pzAEQV2n73E8wxJmAo0EjqoLrMFPh14kmbmt1n76MP-bHN2XwYH0fNpYmjaeGtuLr3zZ46iZzZb-SufnsX4-UykKfkMeHT1_ZhQ=@proton.me">
This past sprint we had such a case where unconditional
deconstruction would have helped with database entities. Basically
a user had created a patient twice over quite some time span and
operations and graft allocations were associated with both
patients but the medical and personal details were most accurate
on the newest and the user desire was to merge them. Our
deduplication detection didn't trigger because of incompleteness
of the old record. However because so many downstream systems
depend on the oldest record that was the id to keep.
<div><br>
</div>
<div>In the database you would just alter the id of the old with
cascade, then relink the foreign key constraints of related
tables to the new patient, then modify the id back to the old
value again with cascade and delete the old. Now JPA doesn't
support this approach of altering a primary key... So instead
you need to fetch the new and old entity and deconstruct the new
entity before deleting it and then reconstruct the old entity
with the new data and same old id... Or you reach for the @Query
approach instead and after modifying the database you change
just the id (and linked relations) of the newer patient
representation object. This latter approach is less brittle to
future changes. </div>
<div><br>
</div>
<div>But the original point that Brian made stands: the
constructor always allows a nonsense representation. People
exploit that in unit tests to create unpersisted entities
or relations to other entities that don't exist. Without
fetching the entire database all at once you won't really get
away from that but I also wouldn't want to.</div>
<div><br>
</div>
<div>We have more and more places where withers would help (and
sad places where a carrier class would have helped but we used a
class in place of a record). <br>
<br>
<br>
<br>
<br>
Sent from <a target="_blank" href="https://urldefense.com/v3/__https://proton.me/mail/home__;!!ACWV5N9M2RV99hQ!M9c8nZS-3Ga3cPLqwU5QkNrUJs_RoN8etK5ZrHbzmmz29wzkUXoSJmTLdIVhe9GYuWhACJi2C0eIRWF10YI$" rel="noreferrer" moz-do-not-send="true">Proton Mail</a> for
Android. </div>
<br>
<div class="protonmail_quote"><br>
<br>
-------- Original Message --------<br>
On Monday, 01/26/26 at 16:13 Ethan McCue <a class="moz-txt-link-rfc2396E" href="mailto:ethan@mccue.dev"><ethan@mccue.dev></a>
wrote:<br>
<blockquote class="protonmail_quote">
<div dir="auto">
<div>My immediate thought (aside from imagining Brian
trapped in an eternal version of that huffalumps and
woozles scene from Winnie the Pooh, but it's all these
emails) is that database entities aren't actually good
candidates for "unconditional deconstruction" </div>
<div dir="auto"><br>
</div>
<div dir="auto">I think this because the act of getting the
data from the db/persistence context is intrinsically
fallible *and* attached to instance behavior; maybe we
need to look forward to what the conditional
deconstruction story would be?</div>
<div dir="auto"><br>
<div class="gmail_quote gmail_quote_container" dir="auto">
<div dir="ltr" class="gmail_attr">On Mon, Jan 26, 2026,
10:04 AM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" rel="noreferrer" moz-do-not-send="true" class="moz-txt-link-freetext">brian.goetz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin: 0 0 0 .8ex; border-left: 1px solid #ccc; padding-left: 1ex">
<div> <br>
<br>
<blockquote type="cite">
<div dir="ltr">
<div>It's interesting that when language
designers make the code easier to write,
somebody may complain that it's too easy :-)</div>
</div>
</blockquote>
<br>
I too had that "you can't win" feeling :)<br>
<br>
I would recast the question here as "Can Java
developers handle carrier classes". Records are
restricted enough to keep developers _mostly_ out of
trouble, but the desire to believe that this is a
syntactic and not semantic feature is a strong one,
and given that many developers education about how
the language works is limited to "what does IntelliJ
suggest to me", may not even _realize_ they are
giving into the dark side. <br>
<br>
I think it is worth working through the example here
for "how would we recommend handling the case of a
"active" row like this. <br>
<br>
<blockquote type="cite">
<div dir="ltr">
<div>I think it's a perfect place for static
analysis tooling. One may invent an annotation
like `@NonUpdatable` </div>
<div>with the `RECORD_COMPONENT` target and use
it on such fields, then create an annotation
processor </div>
<div>(ErrorProne plugin, IntelliJ IDEA
inspection, CodeQL rule, etc.), that will
check the violations and fail the build if
there are any.</div>
<div>Adding such a special case to the language
specification would be an overcomplication.</div>
<div><br>
</div>
<div>With best regards,<br>
Tagir Valeev.</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Sun, Jan
25, 2026 at 11:48 PM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" target="_blank" rel="noreferrer" moz-do-not-send="true" class="moz-txt-link-freetext">brian.goetz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin: 0 0 0 .8ex; border-left: 1px solid #ccc; padding-left: 1ex">
<div> <font size="4" face="monospace">The
important mental model here is that a
reconstruction (`with`) expression is
"just" a syntactic optimization for:<br>
<br>
- destructure with the canonical
deconstruction pattern<br>
- mutate the components <br>
- reconstruct with the primary
constructor<br>
<br>
So the root problem here is not the
reconstruction expression; if you can bork
up your application state with a
reconstruction expression, you can bork it
up without one. <br>
<br>
</font><font size="4" face="monospace">Primary
constructors can enforce invariants _on_
or _between_ components, such as:<br>
<br>
record Rational(int num, int denom) { <br>
Rational { if (denom == 0) throw
... }<br>
}<br>
<br>
or<br>
<br>
record Range(int lo, int hi) { <br>
Range { if (lo > hi) throw... }<br>
}<br>
<br>
What they can't do is express invariants
between the record / carrier state and
"the rest of the system", because they are
supposed to be simple data carriers, not
serialized references to some external
system. </font><font size="4" face="monospace">A class that models a
database row in this way is complecting
entity state with an external entity id.
By modeling in this way, you have
explicitly declared that <br>
<br>
rec with { dbId++ }<br>
<br>
*is explicitly OK* in your system; that
the components of the record can be freely
combined in any way (modulo enforced
cross-component invariants). And there
are systems in which this is fine! But
you're imagining (correctly) that this
modeling technique will be used in systems
in which this is not fine. <br>
</font><font size="4" face="monospace"><br>
The main challenge here is that developers
will be so attracted to the syntactic
concision that they will willfully ignore
the semantic inconsistencies they are
creating. <br>
<br>
<br>
</font><br>
<br>
<div>On 1/25/2026 1:37 PM, Andy Gegg wrote:<br>
</div>
<blockquote type="cite"> <font face="Times New Roman">Hello,<br>
I apologise for coming late to the party
here - Records have been of limited use
to me but Mr Goetz's email on carrier
classes is something that would be very
useful so I've been thinking about the
consequences.<br>
<br>
Since carrier classes and records are
for data, in a database application
somewhere or other you're going to get
database ids in records:<br>
record MyRec(int dbId, String name,...)<br>
<br>
While everything is immutable this is
fine but JEP 468 opens up the
possibility of mutation:<br>
<br>
</font><font face="Times New Roman">MyRec
rec</font><font face="Times New Roman"> =
readDatabase(...);<br>
rec = rec with {name="...";};<br>
writeDatabase(rec);<br>
<br>
which is absolutely fine and what an
application wants to do. But:<br>
</font><font face="Times New Roman">MyRec </font><font face="Times New Roman">rec =
readDatabase(...);<br>
rec = rec with {dbId++;};<br>
writeDatabase(rec);<br>
<br>
is disastrous. There's no way the
canonical constructor invoked from
'with' can detect stupidity nor can
whatever the database access layer does.<br>
<br>
In the old days, the lack of a 'setter'
would usually prevent stupid code - the
above could be achieved, obviously, but
the code is devious enough to make
people stop and think (one hopes).<br>
<br>
Here there is nothing to say "do not
update this!!!" except code comments,
JavaDoc and naming conventions.<br>
<br>
It's not always obvious which fields may
or may not be changed in the
application.<br>
<br>
record </font><font face="Times New Roman">MyRec(int dbId,
int fatherId,...)<br>
probably doesn't want<br>
rec = rec with { fatherId = ... }<br>
<br>
but a HR application will need to be
able to do:<br>
<br>
record </font><font face="Times New Roman">MyRec(int dbId,
int departmentId, ...);<br>
...<br>
rec = rec with { </font><font face="Times New Roman">departmentId =
newDept; };<br>
<br>
Clearly, people can always write stupid
code (guilty...) and the current state
of play obviously allows the possibility
(rec = new MyRec(rec.dbId++, ...);)
which is enough to stop people using
records here but carrier classes will be
very tempting and that brings derived
creation back to the fore.<br>
<br>
It's not just database ids which might
need restricting from update, e.g.
timestamps (which are better done in the
database layer) and no doubt different
applications will have their own
business case restrictions.<br>
<br>
Thank you for your time,<br>
Andy Gegg<br>
</font><br>
</blockquote>
<br>
</div>
</blockquote>
</div>
</blockquote>
<br>
</div>
</blockquote>
</div>
</div>
</div>
</blockquote>
</div>
</blockquote>
<br>
</body>
</html>