<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: babyfish-ct</title>
    <description>The latest articles on DEV Community by babyfish-ct (@babyfishct).</description>
    <link>https://dev.to/babyfishct</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F756070%2Fe9b1d013-d894-482a-8c22-da095febdd95.jpeg</url>
      <title>DEV Community: babyfish-ct</title>
      <link>https://dev.to/babyfishct</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/babyfishct"/>
    <language>en</language>
    <item>
      <title>A Node Optimization Idea: Solving GC Bottlenecks Under Concurrency</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Thu, 26 Mar 2026 13:15:50 +0000</pubDate>
      <link>https://dev.to/babyfishct/a-node-optimization-idea-solving-gc-bottlenecks-under-concurrency-1c2f</link>
      <guid>https://dev.to/babyfishct/a-node-optimization-idea-solving-gc-bottlenecks-under-concurrency-1c2f</guid>
      <description>&lt;h1&gt;
  
  
  What If Node.js Could Have Zero-GC Request Handling — Without You Changing a Single Line of Code?
&lt;/h1&gt;

&lt;h2&gt;
  
  
  A Proposal for a Hybrid Arena + GC Memory Model Baked into the JavaScript Runtime
&lt;/h2&gt;

&lt;p&gt;Original link: &lt;a href="https://github.com/orgs/nodejs/discussions/5143" rel="noopener noreferrer"&gt;https://github.com/orgs/nodejs/discussions/5143&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Talks About Enough
&lt;/h2&gt;

&lt;p&gt;Node.js handles millions of requests per day in production systems worldwide. And yet, the GC pause problem never truly goes away.&lt;/p&gt;

&lt;p&gt;You know the symptom: &lt;strong&gt;p99 latency spikes&lt;/strong&gt;. Not p50, not p95 — but that brutal p99 tail that your SLA demands you fix, and that you can never quite eliminate. The culprit is almost always the same: V8's garbage collector kicking in at the worst possible moment, pausing your event loop, freezing your in-flight requests.&lt;/p&gt;

&lt;p&gt;The standard advice? Tune &lt;code&gt;--max-old-space-size&lt;/code&gt;. Reduce allocations. Use object pooling. Profile with &lt;code&gt;--trace-gc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These are band-aids. They don't address the root cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The root cause is this: V8 has no idea where your request boundaries are.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It doesn't know that the 300 objects you just allocated for request #4821 will all be dead in 12 milliseconds when the response is sent. It just sees a heap full of objects and runs its mark-and-sweep algorithm on its own schedule, indiscriminately.&lt;/p&gt;

&lt;p&gt;What if the runtime &lt;em&gt;did&lt;/em&gt; know? What if it could use that knowledge to make almost all of your request objects essentially free to allocate and free?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Idea: Request-Scoped Arena Allocation, Invisible to Developers
&lt;/h2&gt;

&lt;p&gt;Here's the core proposal, stated simply:&lt;/p&gt;

&lt;p&gt;When a short-lived request (e.g., HTTP request/response cycle) enters the Node.js runtime, the runtime automatically creates an Arena — a contiguous memory region — bound to that request's async context. All JavaScript objects allocated during that request's async scope are placed in this Arena. If the request is &lt;strong&gt;lucky enough&lt;/strong&gt;, when it ends, all JavaScript objects are no longer useful, and the entire arena is released in a single operation. No GC needed.&lt;/p&gt;

&lt;p&gt;What's discussed here is the lucky scenario; the unlucky scenario will be discussed later.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: Dual Heap with Address-Masked Write Barriers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Core Solution&lt;/strong&gt;: Rather than forcing the GC to guess which small fraction of objects are short-lived, let the GC expose an SPI — and let the Node layer use that SPI to tell the GC what is actually true in real-world business workloads: the majority of objects are short-lived. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The end goal&lt;/strong&gt;: business code remains completely unaware that any of this is happening.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Heaps, Not One
&lt;/h3&gt;

&lt;p&gt;The fundamental architectural choice is a &lt;strong&gt;dual heap model&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GC Heap&lt;/strong&gt;: The standard V8 managed heap. All long-lived objects live here. GC traces, marks, and sweeps this heap as normal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arena Heap&lt;/strong&gt;: A separate memory region, outside the GC heap's address range, used exclusively for Arena-allocated objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;heap membership is determined entirely by address range&lt;/strong&gt;. An object is an Arena object if and only if its address falls within the Arena Heap's address range. No object header modification. No layout disruption to V8's densely packed object format.&lt;/p&gt;

&lt;p&gt;The GC exposes the following SPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;Arena&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;createArena&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;releaseArena&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arena&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setContextArena&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arena&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arena&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;createArena&lt;/strong&gt;: Allocates an Arena from the Arena heap. Initially small (e.g., 1KB), but supports dynamic growth via a linked chunk chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;releaseArena&lt;/strong&gt;: Releases the Arena, all objects residing within it are automatically destroyed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setContextArena&lt;/strong&gt;: Sets the Arena for the current main thread context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mechanism works as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When Node begins processing a request, it creates an Arena. Once all async flows complete — regardless of success or failure — the Arena is released. Throughout this process, the Arena context is tracked by the associated &lt;code&gt;AsyncContext&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the main thread gains control, it sets the Arena context. Before yielding control, it sets it back to NULL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Whenever the main thread creates a JavaScript object, the following rules apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large objects ignore the Arena context entirely and are allocated directly on the GC heap — born as GC objects from the start.&lt;/li&gt;
&lt;li&gt;If the current Arena context is NULL, the object is allocated directly on the GC heap — born as a GC object. This covers all scenarios where no one has called the SPI, especially non-Node environments.&lt;/li&gt;
&lt;li&gt;If the current Arena context is non-NULL, memory is allocated from the current Arena, yielding an Arena object rather than a GC object.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Invariant
&lt;/h3&gt;

&lt;p&gt;The scenario discussed earlier — where all JavaScript objects in the arena are no longer useful upon request completion — is a very lucky case. However, reality is not always so ideal.&lt;/p&gt;

&lt;p&gt;One invariant governs the entire model:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Arena objects may reference GC objects. GC objects must never reference Arena objects.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Arena objects holding references into the GC heap is perfectly safe — GC objects outlive the Arena, so those references are always valid. The dangerous direction is the reverse: a GC object referencing an Arena object would become a dangling pointer the moment the Arena is freed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write Barrier via Address Masking
&lt;/h3&gt;

&lt;p&gt;V8 already maintains write barriers for generational GC. This proposal extends the write barrier with a single, extremely cheap check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on every reference write: target[field] = value

  if address_of(value) is in Arena Heap range:
    if address_of(target) is NOT in Arena Heap range:
      // GC object attempting to hold Arena reference → promote
      promote(value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The address range check is implemented as a &lt;strong&gt;bitmask operation&lt;/strong&gt; — one AND, one compare. This is effectively free in the context of a write barrier that already runs on every reference write. No hash lookups, no metadata tables, no object header reads.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;promote(value)&lt;/code&gt; is triggered, a depth-first traversal from &lt;code&gt;value&lt;/code&gt; promotes all reachable Arena objects to the GC heap, ensuring the invariant is restored before the write completes.&lt;/p&gt;

&lt;p&gt;The Write barrier is important. This &lt;code&gt;promote&lt;/code&gt; mechanism is critical to prevent dangling pointers caused by premature Arena release.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arena-Tagged Allocation
&lt;/h3&gt;

&lt;p&gt;When a short-lived async context is created (e.g., HTTP request), Node creates an Arena in the Arena Heap and associates it with that context via &lt;code&gt;AsyncContext&lt;/code&gt;. All allocations within this context use pointer-bump allocation within the Arena. When no Arena context is active — including all non-Node environments and long-lived connection scenarios — allocation falls through to the standard GC heap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request Completion = Arena Release
&lt;/h3&gt;

&lt;p&gt;When the async context ends, the Arena is freed in a single operation — one &lt;code&gt;free()&lt;/code&gt; call, potentially releasing thousands of objects simultaneously. No tracing, no marking, no sweeping.&lt;/p&gt;




&lt;h2&gt;
  
  
  This Is Generational GC — Reimagined for Business Workloads
&lt;/h2&gt;

&lt;p&gt;It is worth being explicit about what this model &lt;em&gt;is&lt;/em&gt; at a deeper level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is a variant of generational garbage collection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Classical generational GC is built on the &lt;em&gt;generational hypothesis&lt;/em&gt;: most objects die young. The young generation is a small, fast-collected region; objects that survive enough GC cycles are promoted to the old generation.&lt;/p&gt;

&lt;p&gt;This proposal takes the same hypothesis and applies it with &lt;strong&gt;business domain knowledge&lt;/strong&gt; that the GC runtime has never had before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Classical young generation: small (a few MB), collected every few milliseconds, objects survive by outlasting GC cycles&lt;/li&gt;
&lt;li&gt;Arena generation: &lt;strong&gt;as large as a request demands&lt;/strong&gt;, collected exactly once (at request end), objects survive by being explicitly promoted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight is that for high-throughput HTTP workloads, the &lt;em&gt;request boundary&lt;/em&gt; is a far more precise and meaningful lifetime boundary than anything a generic GC cycle can infer. Rather than letting the GC guess which objects are short-lived by watching allocation pressure, we tell the runtime exactly where the lifetime boundary is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Arena is a young generation that perfectly matches the business workload's actual object lifetime distribution.&lt;/strong&gt; It is not a small, eagerly-collected nursery — it is a request-sized generation that is collected exactly once, with zero tracing overhead, at exactly the right moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Works: Most Objects Are Boring
&lt;/h2&gt;

&lt;p&gt;The insight that makes this viable is embarrassingly simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The vast majority of objects in a typical web request have a lifespan of exactly one request.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your ORM query result? Dead at response time. Your DTO? Dead at response time. Your middleware context object? Dead at response time. Your parsed JSON body? Dead at response time.&lt;/p&gt;

&lt;p&gt;In a well-structured Node.js application, perhaps 95%+ of allocated objects never escape the request that created them. They're born, used, and should die together. But today's GC doesn't know this — so it treats them the same as long-lived objects, tracing them repeatedly before eventually collecting them.&lt;/p&gt;

&lt;p&gt;With request-scoped Arenas, these objects cost almost nothing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allocation&lt;/strong&gt;: pointer bump. Near zero cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deallocation&lt;/strong&gt;: the entire Arena is freed at once when the request ends. Near zero cost per object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GC involvement&lt;/strong&gt;: zero. The GC never sees these objects at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The minority of objects that genuinely escape — items placed in a Redis-like in-memory cache, singleton services, WebSocket state — are promoted by the runtime when a cross-boundary write is detected, and the GC handles them normally.&lt;/p&gt;




&lt;h2&gt;
  
  
  Invariants
&lt;/h2&gt;

&lt;p&gt;The model maintains two critical invariants:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No cross-heap references from GC to Arena&lt;/strong&gt;: After promotion, no GC-managed object may reference an Arena object. The write barrier enforces this by promoting entire reachable subgraphs when escape is detected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Arena objects are request-scoped&lt;/strong&gt;: All objects allocated within an Arena share exactly one lifetime — the request that created them. An Arena object cannot outlive its request without being explicitly promoted to the GC heap.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These invariants guarantee that when a request completes, the entire Arena can be freed in a single operation without risk of dangling pointers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero Developer Awareness Required
&lt;/h2&gt;

&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; a new allocator API for application developers. This is &lt;strong&gt;not&lt;/strong&gt; a pragma or annotation. This is &lt;strong&gt;not&lt;/strong&gt; a new language feature.&lt;/p&gt;

&lt;p&gt;You write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Arena allocated&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transformUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// Arena allocated&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;             &lt;span class="c1"&gt;// Arena allocated&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Request ends → Arena freed → all of the above gone instantly&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing changes. No &lt;code&gt;arena.alloc()&lt;/code&gt;. No &lt;code&gt;defer arena.free()&lt;/code&gt;. No lifetime annotations. Your existing code, your existing third-party libraries, your existing ORM — all of it silently benefits. &lt;code&gt;express&lt;/code&gt;, &lt;code&gt;fastify&lt;/code&gt;, &lt;code&gt;koa&lt;/code&gt; — they all just work. Because this lives entirely inside the runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;promote()&lt;/code&gt; Case: When You Actually Need Global State
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// GC-managed (module scope, no Arena)&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Arena allocated initially&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// Write barrier fires!&lt;/span&gt;
                                     &lt;span class="c1"&gt;// address_of(config) → Arena Heap range&lt;/span&gt;
                                     &lt;span class="c1"&gt;// address_of(cache entry) → GC Heap range&lt;/span&gt;
                                     &lt;span class="c1"&gt;// → promote(config) triggered&lt;/span&gt;
                                     &lt;span class="c1"&gt;// config and its entire object graph move to GC heap&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer wrote nothing special. The write barrier detected the address range violation and promoted the object graph automatically. &lt;code&gt;config&lt;/code&gt; is now GC-managed and lives until removed from &lt;code&gt;cache&lt;/code&gt;. All other objects from this request still die with the Arena.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Async JSON Serialization via libuv Thread Pool
&lt;/h2&gt;

&lt;p&gt;One additional optimization becomes possible under this model — and it addresses a long-standing Node.js pain point: &lt;strong&gt;&lt;code&gt;JSON.stringify&lt;/code&gt; blocking the main thread&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Under this proposal, the runtime knows at serialization time whether the root object being serialized is a GC object or an Arena object. This distinction opens a new optimization path:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case A: GC root&lt;/strong&gt; — The object graph may contain long-lived references and shared state. Serialization proceeds on the main thread as today. No change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case B: Arena root&lt;/strong&gt; — The object graph is request-scoped. It is, by construction, not shared with any other concurrent request. This makes it safe to serialize off the main thread.&lt;/p&gt;

&lt;p&gt;When the serialization root is an Arena object, the runtime dispatches a serialization task to &lt;strong&gt;libuv's thread pool&lt;/strong&gt;. The I/O thread walks the object graph and serializes it. If it encounters a reference to a GC object (which is valid — Arena objects may reference GC objects), it writes a &lt;strong&gt;placeholder&lt;/strong&gt; in the output buffer and records the reference.&lt;/p&gt;

&lt;p&gt;When the I/O thread completes, control returns to the main thread. If placeholders exist, the main thread serializes only those GC-rooted fragments and splices them into the output. If no placeholders exist, the result is ready immediately.&lt;/p&gt;

&lt;p&gt;In the common case — a fully Arena-rooted response object with no GC references — &lt;code&gt;JSON.stringify&lt;/code&gt; completes entirely off the main thread, with &lt;strong&gt;zero main thread blocking time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is particularly significant for large API responses: the serialization of a 500KB response object currently occupies the event loop for the entire duration. Under this model, that work moves to a background thread, keeping the event loop free to accept and begin processing the next request.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This optimization is proposed here as a direction worth exploring, not a fully specified design. The interaction between Arena lifetime, thread safety, and GC object references during concurrent serialization requires careful analysis. It is presented to illustrate the broader potential of the dual-heap model beyond GC pressure reduction.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Edge Cases: When Arena Is Not Used
&lt;/h2&gt;

&lt;p&gt;Not all asynchronous contexts are suitable for Arena allocation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebSockets and SSE&lt;/strong&gt;: Long-lived connections spanning minutes or hours bypass Arena allocation. The runtime detects these persistent contexts and allocates directly to the GC heap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming responses&lt;/strong&gt;: The runtime may create a fresh Arena per chunk rather than per request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested request contexts&lt;/strong&gt;: When one request spawns another (e.g., internal &lt;code&gt;fetch&lt;/code&gt;), the child inherits the parent's Arena by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-Node environments&lt;/strong&gt;: CLI tools, build scripts, long-running background workers — none of these set &lt;code&gt;Arena*&lt;/code&gt;, so they see exactly the same behavior as today.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The decision is made at context creation time based on the async resource type. HTTP request/response is the primary beneficiary. Everything else is unchanged.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison With Existing Approaches
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Object pooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reuses objects, reduces allocation&lt;/td&gt;
&lt;td&gt;Requires developer effort; only works for specific types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reducing allocations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Less GC pressure&lt;/td&gt;
&lt;td&gt;Fights JavaScript's grain; makes code ugly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bun / JavaScriptCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Faster I/O, native code&lt;/td&gt;
&lt;td&gt;Same GC model; doesn't solve the root problem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rust/Go rewrite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero GC, predictable latency&lt;/td&gt;
&lt;td&gt;Discards ecosystem and team knowledge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;This proposal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero developer effort; all existing code benefits; solves the root cause&lt;/td&gt;
&lt;td&gt;Requires V8 implementation work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Hard Parts (Being Honest)
&lt;/h2&gt;

&lt;p&gt;This is not a trivial change to V8. The challenges are real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dual heap address space management&lt;/strong&gt;: Reserving and managing a separate Arena Heap address range requires coordination with the OS allocator and V8's existing memory management. The address masking approach avoids object header changes but requires careful virtual memory layout.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write barrier completeness&lt;/strong&gt;: The address-range write barrier must cover all reference write paths — compiled JIT code, interpreter, native extensions, WeakMaps, and internal V8 structures. Missing one path is a memory safety bug.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Promotion atomicity&lt;/strong&gt;: The DFS traversal that promotes an object graph must be atomic with respect to JavaScript execution. No interleaving GC or other operations should observe a partially promoted graph.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tooling&lt;/strong&gt;: Chrome DevTools memory profiler, heap snapshots, and allocation trackers would need to understand the dual heap model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are unsolvable. But they require V8 team involvement — this is an RFC-level proposal, not a weekend project.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Philosophical Point
&lt;/h2&gt;

&lt;p&gt;The systems programming world has known about Arena allocation for decades. Game engines use it. Compilers use it. High-performance C++ servers use it. Apache's &lt;code&gt;apr_pool_t&lt;/code&gt; has been doing request-scoped Arena allocation since the early 2000s.&lt;/p&gt;

&lt;p&gt;The conventional wisdom has been that managed languages can't have this because "you don't control allocation."&lt;/p&gt;

&lt;p&gt;But that's only true if you think of the runtime as a black box that the language sits on top of. If the runtime &lt;em&gt;is&lt;/em&gt; the implementation — if Node.js chooses to implement this — then the managed language gets all the ergonomics of GC &lt;em&gt;and&lt;/em&gt; the performance characteristics of Arena allocation, for the common case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GC is not the enemy. GC doing work it doesn't need to do is the enemy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most of your objects don't need GC. They need a bump allocator and a &lt;code&gt;free()&lt;/code&gt; at request end. Let GC focus on the objects that actually need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This proposal is not about replacing GC. It is about giving the runtime &lt;strong&gt;semantic awareness of request boundaries&lt;/strong&gt; — awareness it could always have had, but never used — and using that awareness to implement a form of generational GC that is perfectly tuned to the actual object lifetime distribution of high-throughput HTTP workloads.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arena objects&lt;/strong&gt;: ~95% of request allocations. Near-zero allocation cost. Near-zero deallocation cost. GC never touches them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GC objects&lt;/strong&gt;: ~5% that genuinely escape. Handled normally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience&lt;/strong&gt;: Unchanged. Zero new APIs to learn. Zero migration cost. All existing code benefits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async JSON serialization&lt;/strong&gt;: A path toward moving large response serialization off the main thread entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The best performance optimization is the one your users don't have to think about.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/babyfish-ct" rel="noopener noreferrer"&gt;ChenTao&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;March 26, 2026&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>High-quality documentation for my ORM framework is completed</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Fri, 22 Sep 2023 15:48:28 +0000</pubDate>
      <link>https://dev.to/babyfishct/high-quality-documentation-for-my-orm-framework-is-completed-3ji9</link>
      <guid>https://dev.to/babyfishct/high-quality-documentation-for-my-orm-framework-is-completed-3ji9</guid>
      <description>&lt;p&gt;The high-quality documentation for my ORM framework is now complete: &lt;a href="https://babyfish-ct.github.io/jimmer/"&gt;https://babyfish-ct.github.io/jimmer/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on suggestions from a group of Jimmer users, I spent a full four months writing this high-quality documentation.&lt;/p&gt;

&lt;p&gt;In the past, I’ve introduced this ORM framework before, but for over a year prior, I invested 100% of my energy into continuously implementing planned features, with no time to write quality docs to tell more users what Jimmer actually is.&lt;/p&gt;

&lt;p&gt;Now, the high-quality documentation is finally complete. It not only clearly shows what the current 0.8.23 version of Jimmer has accomplished, but also tells users what features it will add in the future.&lt;/p&gt;

&lt;p&gt;Simple problems keep getting repeatedly solved, while truly complex problems are constantly ignored. I really hope to solve these truly complex problems. Therefore, I have contemplated these problems for over a decade, eventually summarizing a methodology, and providing abstraction and implementation.&lt;/p&gt;

&lt;p&gt;Jimmer will bring tremendous value. If it obviously makes developing projects much easier for everyone, that will be the greatest reward for my persistent efforts thus far.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Finally, the kotlin API and External Cache are supported, and my ORM is finished.</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Fri, 26 Aug 2022 09:01:48 +0000</pubDate>
      <link>https://dev.to/babyfishct/finally-the-kotlin-api-and-external-cache-are-supported-and-my-orm-is-finished-307h</link>
      <guid>https://dev.to/babyfishct/finally-the-kotlin-api-and-external-cache-are-supported-and-my-orm-is-finished-307h</guid>
      <description>&lt;p&gt;Hi Guys&lt;/p&gt;

&lt;p&gt;It's been a few more months since the last message, my ORM is completely finished, with two important changes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kotlin API is supported, all the code snippets in the documentation become tab containing 2 cards: Java and Kotlin.&lt;/li&gt;
&lt;li&gt;Support powerful external cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Does not affect user's choice of caching technology_(The examples use Caffeine and Redis)_.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For object trees with arbitrary depth, not simple objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatically guarantee data consistency between database and cache.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Youtube link:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=Rt5zNv0YR2E"&gt;&lt;strong&gt;https://www.youtube.com/watch?v=Rt5zNv0YR2E&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The framework supports Java API and kotlin API, but for easy understanding, this video only uses kotlin to explain)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Total time: &lt;em&gt;36 minutes&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Object Fetcher: Query object trees with arbitrary depth*(06:10)*&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save Command: Save object trees with arbitrary depth*(06:44)*&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;External Cache: Cache object trees with arbitrary depth, ensure data consistency*(12:09)*&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic table joins &lt;em&gt;(01:23)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Paging query &lt;em&gt;(01:50)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quickly build Spring GraphQL applications &lt;em&gt;(05:27)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;The story of the project, its creative process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the past nearly 20 years of work, I have deeply used too many ORM frameworks, and read all the source code of some of them, but none of them can meet my expectations.&lt;/p&gt;

&lt;p&gt;So I quit my job in June last year, stay at home, work hard for my ideals, develop several open source projects. The last project is to create a revolutionary ORM, to be the most powerful and fastest.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Q: The competition for ORM ended many years ago, why create new ORM?&lt;/p&gt;

&lt;p&gt;A: To solve deep-seated problems, a large number of developers are troubled by these problems every day, but they are not aware of it.&lt;/p&gt;

&lt;p&gt;Q: Why give up all income to create these projects?&lt;/p&gt;

&lt;p&gt;A: My environment that seeks short-term interests, not long-term ones. Lack of innovation and no support for innovation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Keep trying, keep looking for the best solution. During this period, I also encountered a lot of unhappy things. For example, others were not optimistic, and the &lt;a href="https://www.reddit.com/r/java/"&gt;r/java&lt;/a&gt; community of reddit blocked my account on the grounds that they were not allowed to promote their related matters. But these are not important, I tried to solve all the problems I faced before, and finally today, jimmer is fully completed, and all my pre-design goals have been achieved.&lt;/p&gt;

&lt;p&gt;During the 14 months of voluntarily giving up any income for the sake of the ideal. I contributed to three projects, in chronological order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A small project for web developers: &lt;a href="https://github.com/babyfish-ct/graphql-ts-client"&gt;https://github.com/babyfish-ct/graphql-ts-client&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A small project for web developers: &lt;a href="https://github.com/babyfish-ct/graphql-state"&gt;https://github.com/babyfish-ct/graphql-state&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A medium project for Java and Kotlin developers: &lt;a href="https://github.com/babyfish-ct/jimmer"&gt;https://github.com/babyfish-ct/jimmer&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In any case, now I'm happy because all my ideas became reality.&lt;/p&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>orm</category>
      <category>redis</category>
    </item>
    <item>
      <title>A benchmark to show you how fast my ORM framework is</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Mon, 18 Jul 2022 19:12:11 +0000</pubDate>
      <link>https://dev.to/babyfishct/a-benchmark-to-show-you-how-fast-my-orm-framework-is-2hd2</link>
      <guid>https://dev.to/babyfishct/a-benchmark-to-show-you-how-fast-my-orm-framework-is-2hd2</guid>
      <description>&lt;p&gt;I've create a new ORM framework &lt;a href="https://babyfish-ct.github.io/jimmer-doc/"&gt;Jimmer&lt;/a&gt;, it has achieved the following two goals&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More powerful than other ORM frameworks&lt;/li&gt;
&lt;li&gt;Faster than other ORM frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regarding the first point, it is difficult to explain in the limited space. Users must read all the contents of the documentation before they can understand it.&lt;/p&gt;

&lt;p&gt;However, regarding the second point, one benchmark picture can tell everything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8qanGN3r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ilfdh8jn380aolu7v7gi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8qanGN3r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ilfdh8jn380aolu7v7gi.png" alt="Image description" width="880" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The abscissa represents the count of data objects queried from the database.&lt;/li&gt;
&lt;li&gt;The ordinate represents the operation count per second.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you want to view full benchmark report, click &lt;a href="https://babyfish-ct.github.io/jimmer-doc/docs/benchmark"&gt;here&lt;/a&gt; please.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to run the benchmark, run this &lt;a href="https://github.com/babyfish-ct/jimmer/tree/main/benchmark"&gt;project&lt;/a&gt; please.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>orm</category>
    </item>
    <item>
      <title>A new Java ORM and why we need it</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Mon, 20 Jun 2022 12:35:24 +0000</pubDate>
      <link>https://dev.to/babyfishct/a-new-java-orm-and-why-we-need-it-2604</link>
      <guid>https://dev.to/babyfishct/a-new-java-orm-and-why-we-need-it-2604</guid>
      <description>&lt;p&gt;I've created a framework about immutable data and ORM for immutable data.&lt;/p&gt;

&lt;p&gt;Project name: Jimmer&lt;/p&gt;

&lt;p&gt;Project Home: &lt;a href="https://babyfish-ct.github.io/jimmer-doc/"&gt;https://babyfish-ct.github.io/jimmer-doc/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Jimmer is divided into two parts, jimmer-core and jimmer-sql.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;jimmer-core: Immutable data&lt;/li&gt;
&lt;li&gt;jimmer-sql: ORM based on jimmer-core&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Their respective responsibilities are as follows&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;jimmer-core&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porting a well-known project &lt;a href="https://github.com/immerjs/immer"&gt;immer&lt;/a&gt; for Java, modifying immutable objects in the way of mutable objects.&lt;/p&gt;

&lt;p&gt;Jimmer can be used in any context where immutable data structures are required to replace java records. Immutable data structures allow for (effective) change detection: if the reference to the object hasn't changed, then neither has the object itself. Also, it makes cloning relatively cheap: unchanged parts of the data tree do not need to be copied and are shared in memory with older versions of the same state.&lt;/p&gt;

&lt;p&gt;In general, these benefits are achieved by ensuring that you never change any properties of an object or list, but always create a changed copy. In practice, this can lead to very cumbersome code to write, and it is easy to accidentally violate these constraints. Jimmer will help you follow the immutable data paradigm by addressing the following pain points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Jimmer will detect an unexpected mutation and throw an error.&lt;/li&gt;
&lt;li&gt;Jimmer will eliminate the need to create the typical boilerplate code required when doing deep updates to immutable objects: without Jimmer, you would need to manually make copies of objects at each level. Usually by using a lot of copy construction.&lt;/li&gt;
&lt;li&gt;When using JImmer, changes are made to the draft object, which records the changes and takes care of creating the necessary copies without affecting the original.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When using Jimmer, you don't need to learn specialized APIs or data structures to benefit from paradigms.&lt;/p&gt;

&lt;p&gt;In addition, to support ORM, Jimmer adds object dynamics to immer. Any property of an object is allowed to be missing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing properties cause exceptions when accessed directly by code&lt;/li&gt;
&lt;li&gt;Missing properties are automatically ignored during Jackson serialization and will not cause an exception&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;strong&gt;jimmer-sql&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ORM based on jimmer-core dynamic immutable objects.&lt;/p&gt;

&lt;p&gt;In terms of implementation, jimmer-sql is incredibly lightweight, with no dependencies other than JDBC, not even some lightweight encapsulation for database connection like &lt;code&gt;SqlSession&lt;/code&gt; of myBatis.&lt;/p&gt;

&lt;p&gt;Similar to QueryDsl, JOOQ, JPA Criteria, with strongly typed SQL DSLs, most SQL errors are reported at compile time rather than as runtime exceptions.&lt;/p&gt;

&lt;p&gt;However, strongly-typed SQL DSL does not conflict with Native SQL. Through elegant API, Native SQL is mixed into strongly-typed SQL DSL, and developers are encouraged to use features specific to specific database products, such as analytical functions and regularization.&lt;/p&gt;

&lt;p&gt;In addition to all ORM's must-have features, jimmer-sql provides 4 other features that far exceed other ORMs:&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; - [Save command](https://babyfish-ct.github.io/jimmer-doc/docs/jimmer-sql/mutation/save-command)
 - [Object fetcher](https://babyfish-ct.github.io/jimmer-doc/docs/jimmer-sql/query/fetcher)
 - [Dynamic table joins](https://babyfish-ct.github.io/jimmer-doc/docs/jimmer-sql/table-join#dynamic-join)
 - [Smarter pagination queries](https://babyfish-ct.github.io/jimmer-doc/docs/jimmer-sql/query/pagination).

 These four powerful functions that are clearly different from other ORMs are the goals pursued by the ORM part of this framework.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  1. Make User Bean powerful enough
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1  Use immutable data, but support temporary mutable proxies.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Immutable&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The annotation processor will generate a mutable derived interface for the user: &lt;code&gt;TreeNodeDraft&lt;/code&gt;. User can use it like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step1: Create object from scratch&lt;/span&gt;
&lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;oldTreeNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  
    &lt;span class="n"&gt;root&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Root"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Drinks"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;        
        &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Breads"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;        
        &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step2: Create object based on existing object&lt;/span&gt;
&lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;newTreeNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;oldTreeNode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// existing object&lt;/span&gt;
    &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Get child proxy&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dranks+"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Change child proxy&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Old tree node: "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldTreeNode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New tree node: "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newTreeNode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final print result is as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old tree node: 
{"name": "Root", childNodes: [{"name": "Drinks"}, {"name": "Breads"}]}
New tree node: 
{"name": "Root", childNodes: [{"name": "`Drinks+`"}, {"name": "Breads"}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Dynamic object.
&lt;/h3&gt;

&lt;p&gt;Any property of the data object can be unspecified.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Direct access to unspecified properties causes an exception.&lt;/li&gt;
&lt;li&gt;Using Jackson serialization, Unspecified properties will be ignored, without exception throwing.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Father"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Son"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// You can access specified properties&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * But you cannot access unspecified fields, like this
 *
 * System.out.println(current.parent().parent());
 * System.out.println(
 *     current.childNodes().get(0).childNodes()
 * );
 *
 * , because direct access to unspecified 
 * properties causes an exception.
 */&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * Finally You will get JSON string like this
 * 
 * {
 *     "name": "Current", 
 *     parent: {"name": "Father"},
 *     childNodes:[
 *         {"name": "Son"}
 *     ]
 * }
 *
 * , because unspecified will be ignored by 
 * jackson serialization, without exception throwing.
 */&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerModule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImmutableModule&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because entity objects are dynamic, users can build arbitrarily complex data structures. There are countless possibilities, such as&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Lonely object, for example&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;lonelyObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;draft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Lonely object"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shallow object tree, for example&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;shallowTree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;draft&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shallow Tree"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Father"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Son"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deep object tree, for example&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;deepTree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;draft&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Deep Tree"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
             &lt;span class="n"&gt;parent&lt;/span&gt;
                 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Father"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deeperParent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                     &lt;span class="n"&gt;deeperParent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grandfather"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                 &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
            &lt;span class="n"&gt;child&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Son"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deeperChild&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
                    &lt;span class="n"&gt;deeperChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grandson"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This object dynamism, which includes countless possibilities, is the fundamental reason why jimmer's ORM can provide more powerful features.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. ORM base on immutable object.
&lt;/h2&gt;

&lt;p&gt;In jimmer's ORM, entities are also immutable interfaces&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SEQUENCE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sequence:TREE_NODE_ID_SEQ"&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@Key&lt;/span&gt; &lt;span class="c1"&gt;// jimmer annotation, `name()` is business key,&lt;/span&gt;
    &lt;span class="c1"&gt;// business key will be used when `id` property is not specified&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@Key&lt;/span&gt; &lt;span class="c1"&gt;// Business key too&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
    &lt;span class="nd"&gt;@OnDelete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DeleteAction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DELETE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"parent"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note!&lt;/p&gt;

&lt;p&gt;Although jimmer uses some JPA annotations to complete the mapping between entities and tables, jimmer is not JPA.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.1 Save arbitrarily complex object tree into database
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Save lonely entity&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;sqlClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntities&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;lonelyObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;draft&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RootNode"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save shallow entity tree&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;sqlClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntities&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;lonelyObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;draft&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RootNode"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100L&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101L&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;102L&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save deep entity tree&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;sqlClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntities&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;saveCommand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;TreeNode&lt;/span&gt; &lt;span class="n"&gt;lonelyObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeDraft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;draft&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RootNode"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;parent&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Parent"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandParent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;grandParent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grand parent"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;child&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child-1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandChild&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;grandChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child-1-1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
                   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandChild&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;grandChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child-1-2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;child&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child-2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandChild&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;grandChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child-2-1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addIntoChildNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandChild&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;grandChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child-2-2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;configure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Auto insert associated objects &lt;/span&gt;
    &lt;span class="c1"&gt;// if they do not exists in database&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAutoAttachingAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.2 Query arbitrarily complex object trees from a database
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Select root nodes from database &lt;em&gt;(&lt;code&gt;TreeNodeTable&lt;/code&gt; is a java class generated by annotation processor)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rootNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// filter roots&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select root nodes and their child nodes from database &lt;em&gt;(&lt;code&gt;TreeNodeFetcher&lt;/code&gt; is a java class generated by annotation processor)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rootNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// filter roots&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;TreeNodeFetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allScalarFields&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="nc"&gt;TreeNodeFetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allScalarFields&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Query the root nodes, with two levels of child nodes&lt;/p&gt;

&lt;p&gt;You have two ways to do it&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-   Specify a deeper tree format
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ```java
    List&amp;lt;TreeNode&amp;gt; rootNodes = sqlClient
    .createQuery(TreeNodeTable.class, (q, treeNode) -&amp;gt; {
        q.where(treeNode.parent().isNull()) // filter roots
        return q.select(
            treeNode.fetch(
                TreeNodeFetcher.$
                    .allScalarFields()
                    .childNodes( // level-1 child nodes
                        TreeNodeFetcher.$
                            .allScalarFields()
                            .childNodes( // level-2 child nodes
                                TreeNodeFetcher.$
                                    .allScalarFields()
                            )
                    )
            )
        );
    })
    .execute();
    ```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-   You can also specify depth for self-associative property, this is better way
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ```java
    List&amp;lt;TreeNode&amp;gt; rootNodes = sqlClient
    .createQuery(TreeNodeTable.class, (q, treeNode) -&amp;gt; {
        q.where(treeNode.parent().isNull()) // filter roots
        return q.select(
            treeNode.fetch(
                TreeNodeFetcher.$
                    .allScalarFields()
                    .childNodes(
                        TreeNodeFetcher.$
                            .allScalarFields(),
                        it -&amp;gt; it.depth(2) // Fetch 2 levels
                    )
            )
        );
    })
    .execute();
    ```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Query all root nodes, recursively get all child nodes, no matter how deep&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rootNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// filter roots&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;TreeNodeFetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allScalarFields&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nc"&gt;TreeNodeFetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allScalarFields&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;

                    &lt;span class="c1"&gt;// Recursively fetch all, &lt;/span&gt;
                    &lt;span class="c1"&gt;// no matter how deep&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; 
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Query all root nodes, it is up to the developer to control whether each node needs to recursively query child nodes&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rootNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// filter roots&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;TreeNodeFetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allScalarFields&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nc"&gt;TreeNodeFetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allScalarFields&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="c1"&gt;// - If the node name starts with `Tmp_`, &lt;/span&gt;
                        &lt;span class="c1"&gt;// do not recursively query child nodes.&lt;/span&gt;
                        &lt;span class="c1"&gt;//&lt;/span&gt;
                        &lt;span class="c1"&gt;// - Otherwise, &lt;/span&gt;
                        &lt;span class="c1"&gt;// recursively query child nodes.&lt;/span&gt;
                        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntity&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tmp_"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.3 Dynamic table joins.
&lt;/h3&gt;

&lt;p&gt;In order to develop powerful dynamic queries, it is not enough to support dynamic where predicates, but dynamic table joins are required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TreeNodeRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SqlClient&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TreeNodeRepository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SqlClient&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sqlClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findTreeNodes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Nullable&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@Nullable&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;parentName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@Nullable&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;grandParentName&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sqlClient&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;eq&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parentName&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;parentName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                       &lt;span class="n"&gt;treeNode&lt;/span&gt;
                       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Join: current -&amp;gt; parent&lt;/span&gt;
                       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;eq&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parentName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                   &lt;span class="o"&gt;);&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandParentName&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;grandParentName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                       &lt;span class="n"&gt;treeNode&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Join: current -&amp;gt; parent&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Join: parent -&amp;gt; grand parent&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;eq&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grandParentName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                   &lt;span class="o"&gt;);&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
               &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;})&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dynamic query supports three nullable parameters.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When the parameter &lt;code&gt;parentName&lt;/code&gt; is not null, the table join &lt;code&gt;current -&amp;gt; parent&lt;/code&gt; is required&lt;/li&gt;
&lt;li&gt;When the parameter &lt;code&gt;grandParentName&lt;/code&gt; is not null, you need to join &lt;code&gt;current -&amp;gt; parent -&amp;gt; grandParent&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the parameters &lt;code&gt;parentName&lt;/code&gt; and &lt;code&gt;grandParent&lt;/code&gt; are both specified, the table join paths &lt;code&gt;current -&amp;gt; parent&lt;/code&gt; and &lt;code&gt;current -&amp;gt; parent -&amp;gt; grandParent&lt;/code&gt; are both added to the query conditions. Among them, &lt;code&gt;current-&amp;gt;parent&lt;/code&gt; appears twice, jimmer will automatically merge the duplicate table joins. &lt;/p&gt;

&lt;p&gt;This means&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`current -&amp;gt; parent` 
+ 
`current -&amp;gt; parent -&amp;gt; grandParent` 
= 
--+-current
  |
  \--+-parent
     |
     \----grandParent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the process of merging different table join paths into a join tree, duplicate table joins are removed.&lt;/p&gt;

&lt;p&gt;The final SQL is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; 
    &lt;span class="n"&gt;tb_1_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tb_1_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tb_1_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PARENT_ID&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;TREE_NODE&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tb_1_&lt;/span&gt;

&lt;span class="cm"&gt;/* Two java joins are merged to one sql join*/&lt;/span&gt;
&lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;TREE_NODE&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tb_2_&lt;/span&gt; 
    &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;tb_1_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PARENT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tb_2_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;

&lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;TREE_NODE&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tb_3_&lt;/span&gt; 
    &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;tb_2_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PARENT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tb_3_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;tb_2_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="cm"&gt;/* parentName */&lt;/span&gt;
&lt;span class="k"&gt;and&lt;/span&gt;
    &lt;span class="n"&gt;tb_3_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="cm"&gt;/* grandParentName */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.4 Automatically generate count-query by data-query.
&lt;/h3&gt;

&lt;p&gt;Pagination query requires two SQL statements, one for querying the total row count of data, and the other one for querying data in one page, let's call them count-query and data-query. &lt;/p&gt;

&lt;p&gt;Developers only need to focus on data-count, and count-query can be generated automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Developer create data-query&lt;/span&gt;
&lt;span class="nc"&gt;ConfigurableTypedRootQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dataQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="n"&gt;sqlClient&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeNodeTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;q&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;treeNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Framework generates count-query&lt;/span&gt;
&lt;span class="nc"&gt;TypedRootQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;countQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataQuery&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reselect&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;oldQuery&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;oldQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withoutSortingAndPaging&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Execute count-query&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rowCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;countQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Execute data-query&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TreeNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;someRootNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="n"&gt;dataQuery&lt;/span&gt;
        &lt;span class="c1"&gt;// limit(limit, offset), from 1/3 to 2/3&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rowCount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowCount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>java</category>
      <category>orm</category>
      <category>immutable</category>
      <category>immer</category>
    </item>
    <item>
      <title>A 20-minute video introducing GraphQL-state</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Wed, 22 Dec 2021 09:56:11 +0000</pubDate>
      <link>https://dev.to/babyfishct/a-20-minute-video-introducing-graphql-state-4ida</link>
      <guid>https://dev.to/babyfishct/a-20-minute-video-introducing-graphql-state-4ida</guid>
      <description>&lt;p&gt;I created a 20-minute video introducing GraphQL-state(An intelligent react state management framework).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=05Xx_t8GC18"&gt;https://www.youtube.com/watch?v=05Xx_t8GC18&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(My English is too bad, so I can only use AI to synthesize speech, sorry)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In daily development, React developers will encounter a type of complex problems that have not been solved by any other state management frameworks. GraphQL-state is committed to a simple and intelligent way to solve those complex problems.&lt;/p&gt;

&lt;p&gt;This video can show you how powerful and smart it is.&lt;/p&gt;

&lt;p&gt;Welcome everyone to put forward your valuable opinions, let's work together to make it better and better, smarter and smarter.&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>A new react state management framework</title>
      <dc:creator>babyfish-ct</dc:creator>
      <pubDate>Thu, 18 Nov 2021 03:15:55 +0000</pubDate>
      <link>https://dev.to/babyfishct/a-new-react-state-management-framework-24mk</link>
      <guid>https://dev.to/babyfishct/a-new-react-state-management-framework-24mk</guid>
      <description>&lt;p&gt;I created a new react state management framework &lt;a href="https://github.com/babyfish-ct/graphql-state"&gt;https://github.com/babyfish-ct/graphql-state&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article does not intend to introduce the details of the framework itself, because the framework provides get-start example,  documentation, comprehensive examples, and GIF animations, so I won’t repeat them here.&lt;/p&gt;

&lt;p&gt;In this article, we discuss why this new framework was created, whether it is to reinvent the wheel, or whether it has its own unique insights and value.&lt;/p&gt;

&lt;p&gt;How to consume GraphQL services in React can be divided into four levels&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The first level, provides API to communicate with server
&lt;/h2&gt;

&lt;p&gt;This level of framework is represented by &lt;a href="https://github.com/prisma-labs/graphql-request"&gt;https://github.com/prisma-labs/graphql-request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This kind of framework is the most basic and simplest GraphQL client, which can use its API to communicate with the server, but it does not have any caching services.&lt;/p&gt;

&lt;p&gt;When your application is very simple, the minimal function can bring the lowest learning difficulty and use cost. When the project is very simple so that each page is an island of information with a small amount of data, and you can deal with the lack of no cache through frequent refresh queries.&lt;/p&gt;

&lt;p&gt;However, when you encounter a slightly more complex application, a variety of different data are presented on UI at the same time, and there are relationships and dependencies between them, you will start to be powerless, because you can't ignore performance issues and refresh all data frequently. You desperately need caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The second level, simple key/value cache
&lt;/h2&gt;

&lt;p&gt;This level of framework is represented by &lt;a href="https://github.com/tannerlinsley/react-query"&gt;https://github.com/tannerlinsley/react-query&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is true that react-query is very powerful in terms of configurability; but the biggest problem is that the cache is a simple key/value cache. In fact, the real data model is a graph structure, and different objects are related to each other. E.g&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BookStore &amp;lt;--1:n--&amp;gt; Book &amp;lt;--m:n--&amp;gt; Author
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the associations in the example, a simple key/value cache will easily lead to the following two cache items&lt;/p&gt;

&lt;p&gt;Cache data 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BookStore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;O'REILLY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Learning GraphQL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Effective TypeScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cached data 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alex Banks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Learning GraphQL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above data, the book named "Learning GraphQL" exists in both the cache items. This redundancy will cause data inconsistency in subsequent data changes.&lt;/p&gt;

&lt;p&gt;As the relationship between objects becomes more and more complex, you will find it more and more difficult to eliminate the side effects of redundancy. You urgently need to normalize the data in the cache, just as you do in RDBMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The third level, normalized-cache
&lt;/h2&gt;

&lt;p&gt;This level of framework is represented by Apollo Client and Relay.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apollo Client: &lt;a href="https://github.com/apollographql/apollo-client"&gt;https://github.com/apollographql/apollo-client&lt;/a&gt; (normalized cache is supported since 3.0, older versions are not supported)&lt;/li&gt;
&lt;li&gt;Relay: &lt;a href="https://github.com/facebook/relay"&gt;https://github.com/facebook/relay&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normalized-cache stores data rows and the relationships between them just like RDBMS. Highly normalized data has no redundancy. Of course, this kind of internal relational data needs to be converted with the hierarchical data used by the user. Fortunately, the framework can easily black-box this conversion automatically, and users cannot perceive the existence of internal relational data.&lt;/p&gt;

&lt;p&gt;Now, users don’t need to worry about data redundancy in the cache. But there is also a serious problem, that is, the mutation operation will be very complicated, developers need to maintain the consistency of the cache.&lt;/p&gt;

&lt;p&gt;For example, the data in the existing cache is as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;findBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="nx"&gt;findBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;g&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Learning GraphQL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Effective TypeScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Among them, &lt;em&gt;findBooks({"name": ...})&lt;/em&gt; represents the query condition, and performs fuzzy matching and filtering on the name of the book.&lt;/p&gt;

&lt;p&gt;Now, mutate the data, change &lt;em&gt;{id:2, name: "Learning GraphQL"}&lt;/em&gt; to &lt;em&gt;{id:2, name: "Learning TypeScript"}&lt;/em&gt;. After mutation, the new name &lt;em&gt;"Learning TypeScript"&lt;/em&gt; no longer matches the query condition &lt;em&gt;{name: "g"}&lt;/em&gt;. So, the new cache should look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;findBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;

        &lt;span class="c1"&gt;// Extra mutation: old data references need to disappear&lt;/span&gt;
        &lt;span class="nx"&gt;findBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;g&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="c1"&gt;// Main mutation: the original intention of the developer&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Learning TypeScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Book:3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Effective TypeScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The "main mutation" above is very simple, and this is the developer's intention in itself. However, "extra mutation" is very troublesome. It is the changes that other existing data in the cache have to make in order to adapt to the new data mutation.&lt;/p&gt;

&lt;p&gt;The number of such "extra mutations" is affected by the amount of existing data in the cache and the complexity of the data structure. From a theoretical level, the complexity cannot be limited, and one main mutation can lead to countless extra mutations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To allow the cache to complete the above "extra mutations", there are nothing more than two ways.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Manually change the local cache, which is a high-performance but not necessarily feasible method&lt;/li&gt;
&lt;li&gt;Let the query &lt;em&gt;Query.findBooks({name: "g"})&lt;/em&gt; refetch latest data from the server again. This is a low-performance but always feasible method&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For Apollo Client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modify the cache: &lt;a href="https://www.apollographql.com/docs/react/data/mutations/#updating-the-cache-directly"&gt;https://www.apollographql.com/docs/react/data/mutations/#updating-the-cache-directly&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Refetch: &lt;a href="https://www.apollographql.com/docs/react/data/mutations/#refetching-queries"&gt;https://www.apollographql.com/docs/react/data/mutations/#refetching-queries&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relay prefers to modify the cache directly: &lt;a href="https://relay.dev/docs/guided-tour/updating-data/graphql-mutations/#updater-functions"&gt;&lt;/a&gt;&lt;a href="https://relay.dev/docs/guided-tour/updating-data/graphql-mutations/#updater-functions"&gt;https://relay.dev/docs/guided-tour/updating-data/graphql-mutations/#updater-functions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As explained above, the complexity of this "extra mutations" cannot be limited. It is foreseeable that if there are more UI modules, the data types inside the modules are richer, and the relationship between data types is more complex, then it will be more difficult to ensure cache consistency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you choose to modify the cache directly, the logic of the code you need to write will become more and more complex.&lt;/li&gt;
&lt;li&gt;If you choose to let affected queries to refetch data again, it is also difficult to determine which queries will be affected by the current mutation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, if you use Apollo Client or Relay, you will find that the pain point you face has become cache consistency maintenance, and the degree of pain is positively related to the complexity of the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The fourth level, automatically guarantees cache consistency
&lt;/h2&gt;

&lt;p&gt;In response to the problems of Apollo Client and Relay, I developed this new state management framework. One of the most important functions is to consume GraphQL services. The framework can automatically ensure the consistency of the cache after mutation. The method of directly modifying the cache is preferred, and if not feasible, it will be upgraded to a re-query. No matter how the framework chooses, everything is automatic.&lt;/p&gt;

&lt;p&gt;For details on how to achieve this powerful goal, please visit the project homepage.&lt;/p&gt;

&lt;p&gt;Attached:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Since it is known as the react state management framework, does it only support GraphQL? What about REST?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the next version to be released, the framework can simulate GraphQL base on REST. Even for the REST server, it can be abstracted into GraphQL on the client side, enjoying its powerful language meaning and convenience. This feature is called "GraphQL style, but not GraphQL only", please look forward to it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
    </item>
  </channel>
</rss>
