<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <font size="4" face="monospace">I think we might be saying the same
      thing.  When I say "regular class", it doesn't matter whether it
      is hand written or generated; the key is that the right to choose
      IDs is reserved by that class.  Maybe its hidden behind an
      interface, maybe not.  But you have an _entity_ class that
      associates (id, PersonData) and a record/carrier for PersonData,
      which is _just_ the data.  <br>
    </font><br>
    <div class="moz-cite-prefix">On 1/26/2026 11:05 AM, Ethan McCue
      wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:CA+NR86ipS+y5YcyJT1xMm_yPGAg+y3agU-p9RqTrLDNvCJrg8w@mail.gmail.com">
      
      <div dir="auto">Forgive me if I'm mixing up terms here, but I
        think a database entity when fetched from some persistence
        context can actually be a runtime generated subclass that also
        maintains a reference to the context that produced it.
        <div dir="auto"><br>
        </div>
        <div dir="auto">So you'd have an instance of Person and be
          tempted to write p = p with { name = "..."; }. This could
          maybe work if the subclass could do its own thing, but if you
          are feeding the data back into new Person(...) it can't.</div>
        <div dir="auto"><br>
        </div>
        <div dir="auto"><br>
        </div>
      </div>
      <br>
      <div class="gmail_quote gmail_quote_container">
        <div dir="ltr" class="gmail_attr">On Mon, Jan 26, 2026, 10:55 AM
          Brian Goetz <<a href="mailto:brian.goetz@oracle.com" 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 #ccc solid;padding-left:1ex">
          <div> <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>On 1/26/2026 10:52 AM, Aaryn Tonita wrote:<br>
            </div>
            <blockquote type="cite"> 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 href="https://urldefense.com/v3/__https://proton.me/mail/home__;!!ACWV5N9M2RV99hQ!M9c8nZS-3Ga3cPLqwU5QkNrUJs_RoN8etK5ZrHbzmmz29wzkUXoSJmTLdIVhe9GYuWhACJi2C0eIRWF10YI$" rel="noreferrer noreferrer" target="_blank" moz-do-not-send="true">Proton Mail</a> for Android. </div>
              <br>
              <div><br>
                <br>
                -------- Original Message --------<br>
                On Monday, 01/26/26 at 16:13 Ethan McCue <a href="mailto:ethan@mccue.dev" target="_blank" rel="noreferrer" moz-do-not-send="true"><ethan@mccue.dev></a>
                wrote:<br>
                <blockquote>
                  <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" 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 noreferrer" target="_blank" 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" rel="noreferrer noreferrer" target="_blank" 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>
          </div>
        </blockquote>
      </div>
    </blockquote>
    <br>
  </body>
</html>