<?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: sugaiketadao</title>
    <description>The latest articles on DEV Community by sugaiketadao (@sugaiketadao).</description>
    <link>https://dev.to/sugaiketadao</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%2F3731315%2Fae0500af-3b9d-4a0d-9116-460e3b447fa9.jpg</url>
      <title>DEV Community: sugaiketadao</title>
      <link>https://dev.to/sugaiketadao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sugaiketadao"/>
    <language>en</language>
    <item>
      <title>Always Pair VLOOKUP with ISNA (or IFERROR)</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Sat, 11 Apr 2026 08:23:49 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/always-pair-vlookup-with-isna-or-iferror-4cl8</link>
      <guid>https://dev.to/sugaiketadao/always-pair-vlookup-with-isna-or-iferror-4cl8</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;VLOOKUP&lt;/code&gt; returns a &lt;code&gt;#N/A&lt;/code&gt; error when the lookup value isn't found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=VLOOKUP(A2, ProductMaster!A:B, 2, FALSE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the value in A2 doesn't exist in the product master, the cell shows &lt;code&gt;#N/A&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's annoying on its own — but the real problem is that &lt;strong&gt;any formula referencing that cell also breaks&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=SUM(B2:B10)  -- if any cell in range is #N/A, the whole SUM returns #N/A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Fix with ISNA
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ISNA&lt;/code&gt; returns &lt;code&gt;TRUE&lt;/code&gt; if the value is &lt;code&gt;#N/A&lt;/code&gt;, &lt;code&gt;FALSE&lt;/code&gt; otherwise. Combine it with &lt;code&gt;IF&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF(ISNA(VLOOKUP(A2, ProductMaster!A:B, 2, FALSE)), "", VLOOKUP(A2, ProductMaster!A:B, 2, FALSE))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When no match is found, the cell returns an empty string instead of &lt;code&gt;#N/A&lt;/code&gt;. The cascading error problem goes away.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shorter: Use IFERROR
&lt;/h2&gt;

&lt;p&gt;Excel 2007+ has &lt;code&gt;IFERROR&lt;/code&gt;, which is the modern standard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IFERROR(VLOOKUP(A2, ProductMaster!A:B, 2, FALSE), "")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much cleaner. One caveat: &lt;code&gt;IFERROR&lt;/code&gt; catches &lt;strong&gt;all&lt;/strong&gt; errors (&lt;code&gt;#REF!&lt;/code&gt;, &lt;code&gt;#VALUE!&lt;/code&gt;, etc.), not just &lt;code&gt;#N/A&lt;/code&gt;. If you need to distinguish between error types, stick with &lt;code&gt;ISNA&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison
&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;Length&lt;/th&gt;
&lt;th&gt;Error scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ISNA&lt;/code&gt; + &lt;code&gt;IF&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Verbose&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;#N/A&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IFERROR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Concise&lt;/td&gt;
&lt;td&gt;All errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Bonus: Use ISNA for Conditional Display
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ISNA&lt;/code&gt; is also useful when you want to show a value based on whether a match exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF(ISNA(VLOOKUP(A2, ProductMaster!A:B, 2, FALSE)), "Not found", "Found")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Whenever you write a &lt;code&gt;VLOOKUP&lt;/code&gt;, wrap it in &lt;code&gt;IFERROR&lt;/code&gt; right away. It's a simple habit that keeps your spreadsheets clean and prevents confusing error cascades.&lt;/p&gt;




&lt;h2&gt;
  
  
  About SIcore Framework
&lt;/h2&gt;

&lt;p&gt;I'm building an open-source Java framework designed to make enterprise web app development straightforward — even for junior developers. HTML, CSS, JavaScript, and Java are all structured around consistent, minimal conventions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;
If you found this useful, a ❤️ would mean a lot.&lt;/p&gt;

</description>
      <category>excel</category>
      <category>spreadsheet</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>CSS Blocks and Inlines — What I Wish I'd Known on Day One</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Fri, 03 Apr 2026 11:19:07 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/css-blocks-and-inlines-what-i-wish-id-known-on-day-one-3o9i</link>
      <guid>https://dev.to/sugaiketadao/css-blocks-and-inlines-what-i-wish-id-known-on-day-one-3o9i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;"I set a &lt;code&gt;width&lt;/code&gt; but nothing changed." "The top and bottom &lt;code&gt;margin&lt;/code&gt; isn't doing anything." "I just want these elements side by side but it's not working."&lt;/p&gt;

&lt;p&gt;If you've spent time staring at CSS that refuses to cooperate, there's a good chance the root cause was &lt;strong&gt;the difference between block and inline elements&lt;/strong&gt; — and that knowing this one concept would have resolved most of those issues instantly.&lt;/p&gt;

&lt;p&gt;Here's why it matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can immediately understand &lt;em&gt;why&lt;/em&gt; a CSS rule isn't taking effect&lt;/li&gt;
&lt;li&gt;You know exactly &lt;em&gt;where&lt;/em&gt; to look to fix it&lt;/li&gt;
&lt;li&gt;You know &lt;em&gt;what to check&lt;/em&gt; in the browser DevTools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not knowing this means hours of trial and error. Past-me knows this all too well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Block vs. Inline — The Short Version
&lt;/h2&gt;

&lt;p&gt;Every HTML element has a default display behavior: it's either &lt;strong&gt;block&lt;/strong&gt; or &lt;strong&gt;inline&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Block elements&lt;/th&gt;
&lt;th&gt;Inline elements&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Layout direction&lt;/td&gt;
&lt;td&gt;Stack vertically&lt;/td&gt;
&lt;td&gt;Flow horizontally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Width&lt;/td&gt;
&lt;td&gt;Stretches to fill the parent&lt;/td&gt;
&lt;td&gt;Shrinks to fit content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;width&lt;/code&gt; / &lt;code&gt;height&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Does &lt;strong&gt;not&lt;/strong&gt; work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;margin&lt;/code&gt; (top/bottom)&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Does &lt;strong&gt;not&lt;/strong&gt; work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;padding&lt;/code&gt; (top/bottom)&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Visually expands, but &lt;strong&gt;doesn't push adjacent elements&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;line-height&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Common tags&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;–&lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Block Elements
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;div (block)&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;div (block)&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&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;┌────────────────────────────────────┐
│ div (block)                        │
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ div (block)                        │
└────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; is a block element by default. Without any CSS, it stretches to fill the full width of its parent and pushes the next element below it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, and &lt;code&gt;padding&lt;/code&gt; all work exactly as you'd expect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Inline Elements
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;span (inline)&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;span (inline)&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&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;┌─────────────────────┐┌─────────────────────┐
│ span (inline)        ││ span (inline)        │
└─────────────────────┘└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; is an inline element by default. It flows horizontally alongside other content and only takes up as much width as its content needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS That Doesn't Work on Inline Elements
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c"&gt;/* No effect */&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c"&gt;/* No effect */&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c"&gt;/* No effect */&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* No effect */&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c"&gt;/* Visually expands, but doesn't push neighbors away */&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c"&gt;/* Works — line-height applies to inline elements */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the root cause of most "why isn't this CSS working?!" moments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Switching Display with the &lt;code&gt;display&lt;/code&gt; Property
&lt;/h2&gt;

&lt;p&gt;The display mode can be changed with the CSS &lt;code&gt;display&lt;/code&gt; property. This is the key to resolving most of the issues above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make an inline element respond to size and vertical margin → &lt;code&gt;display: block&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Now works */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commonly used to expand the clickable area of a link beyond its text.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make block elements flow side by side while keeping size control → &lt;code&gt;display: inline-block&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&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;&lt;code&gt;inline-block&lt;/code&gt; gives you the best of both worlds: elements flow horizontally (like inline), but &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; are respected (like block).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────┐┌──────────┐
│   div    ││   div    │
│ 100×100  ││ 100×100  │
└──────────┘└──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Traps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Trap 1: Setting &lt;code&gt;width&lt;/code&gt; on a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; has no effect
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ❌ span is inline — width is ignored */&lt;/span&gt;
&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ✅ Change the display mode */&lt;/span&gt;
&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* or block */&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&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;h3&gt;
  
  
  Trap 2: &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags have a tiny click area
&lt;/h3&gt;

&lt;p&gt;Navigation links where only the text itself is clickable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ❌ Padding doesn't push neighboring elements */&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ✅ Make the padding work properly */&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* or inline-block */&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&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;h3&gt;
  
  
  Trap 3: Trying to place &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements side by side
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ❌ div is block — elements stack vertically */&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ✅ Option 1: inline-block */&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ✅ Option 2: flexbox on the parent (the modern approach) */&lt;/span&gt;
&lt;span class="nc"&gt;.parent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&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;Flexbox handles horizontal layout, centering, and even spacing distribution. If you're learning CSS today, &lt;strong&gt;prioritize flexbox over &lt;code&gt;inline-block&lt;/code&gt;&lt;/strong&gt; — it covers almost everything you'd want for modern layouts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check It in DevTools
&lt;/h2&gt;

&lt;p&gt;You can confirm any element's &lt;code&gt;display&lt;/code&gt; value instantly in the browser's developer tools (F12):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click the element → "Inspect"&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;Computed&lt;/strong&gt; tab on the right&lt;/li&gt;
&lt;li&gt;Search for or find &lt;code&gt;display&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you see "display: inline — that's why width isn't working," you know exactly what to fix.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;block&lt;/span&gt;        &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nt"&gt;Stacks&lt;/span&gt; &lt;span class="nt"&gt;vertically&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;height&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;margin&lt;/span&gt; &lt;span class="nt"&gt;all&lt;/span&gt; &lt;span class="nt"&gt;work&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;inline&lt;/span&gt;       &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nt"&gt;Flows&lt;/span&gt; &lt;span class="nt"&gt;horizontally&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;height&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;vertical&lt;/span&gt; &lt;span class="nt"&gt;margin&lt;/span&gt; &lt;span class="nt"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nt"&gt;t&lt;/span&gt; &lt;span class="nt"&gt;work&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;inline-block&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nt"&gt;Flows&lt;/span&gt; &lt;span class="nt"&gt;horizontally&lt;/span&gt; &lt;span class="nt"&gt;AND&lt;/span&gt; &lt;span class="nt"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;height&lt;/span&gt; &lt;span class="nt"&gt;work&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;flex&lt;/span&gt;         &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nt"&gt;Flexible&lt;/span&gt; &lt;span class="nt"&gt;layout&lt;/span&gt; &lt;span class="nt"&gt;for&lt;/span&gt; &lt;span class="nt"&gt;children&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;The&lt;/span&gt; &lt;span class="nt"&gt;modern&lt;/span&gt; &lt;span class="nt"&gt;standard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever a CSS rule doesn't seem to apply to a new element, make it a habit to ask: &lt;strong&gt;"What is this element's default &lt;code&gt;display&lt;/code&gt; value?"&lt;/strong&gt; That single question will dramatically speed up your debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Block vs. inline feels like a minor detail at first. But knowing the distinction changes how fast and how confidently you work with CSS.&lt;/p&gt;

&lt;p&gt;I think back to the time I spent wondering "why isn't this working?" — and I wish I'd had this explanation then.&lt;/p&gt;

&lt;p&gt;Next time a &lt;code&gt;width&lt;/code&gt; doesn't seem to apply or elements refuse to go side by side, come back to the table at the top of this article. The answer is almost certainly there.&lt;/p&gt;




&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;I've published a Java framework designed for SI projects, built so that even junior programmers can develop business applications with ease. The HTML, CSS, JavaScript, and Java structure is cleanly organized, and you can learn by running the sample code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/what-i-learned-developing-a-custom-framework-with-generative-ai-3npe"&gt;What I Learned Developing a Custom Framework &lt;em&gt;with&lt;/em&gt; Generative AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/when-is-it-ok-to-build-a-system-with-ai-alone-a-framework-for-thinking-about-responsibility-39on"&gt;When Is It OK to Build a System with AI Alone?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla? — The Design Philosophy Behind SIcore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Go Vanilla? — Why I Built a Java Framework (third attempt in 10 years) #11</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Thu, 02 Apr 2026 01:07:36 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5</link>
      <guid>https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In articles #02 through #10, I introduced the design and features of the SIcore framework. In this final installment, I want to step back and explain the philosophy behind every design decision I've described — &lt;strong&gt;why I built it with vanilla (plain Java, JavaScript, and HTML)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SIcore uses no annotations in Java, no JavaScript framework, no template engine for HTML, and no Bootstrap or Tailwind for CSS — just a hand-written 400-line stylesheet.&lt;/p&gt;

&lt;p&gt;Going vanilla is a practical choice that pays off in two concrete ways: &lt;strong&gt;compatibility with AI code generation&lt;/strong&gt; and &lt;strong&gt;a design that beginners and junior developers can actually learn from&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Vanilla" Means Here
&lt;/h2&gt;

&lt;p&gt;Here's a summary of what SIcore deliberately does &lt;em&gt;not&lt;/em&gt; use:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What's omitted&lt;/th&gt;
&lt;th&gt;SIcore's alternative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;Spring Boot, annotations, DI&lt;/td&gt;
&lt;td&gt;Direct URL-to-class mapping (#02), reflection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java (SQL)&lt;/td&gt;
&lt;td&gt;MyBatis, JPA, XML definitions&lt;/td&gt;
&lt;td&gt;SQL written directly in Java — SqlBuilder / SqlConst (#10)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java (data)&lt;/td&gt;
&lt;td&gt;Entity / DTO / Bean&lt;/td&gt;
&lt;td&gt;Map-based design — Io class (#07)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;React, Vue, Angular&lt;/td&gt;
&lt;td&gt;Vanilla JS + framework utilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;td&gt;JSP, Thymeleaf, template engines&lt;/td&gt;
&lt;td&gt;Static HTML + JSON communication (#03, #04)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;Bootstrap, Tailwind&lt;/td&gt;
&lt;td&gt;Custom 400-line stylesheet (#08)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Tomcat, Jetty&lt;/td&gt;
&lt;td&gt;JDK standard HttpServer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In short: enterprise business apps work with nothing beyond the JDK and browser standards.&lt;/p&gt;

&lt;p&gt;This isn't a philosophical stance. It's the result of chasing practical outcomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefit: Plays Well with AI Code Generation
&lt;/h2&gt;

&lt;p&gt;As I mentioned in #01, &lt;strong&gt;a design that's beginner-friendly turns out to also be AI-friendly&lt;/strong&gt;. Here's why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified patterns
&lt;/h3&gt;

&lt;p&gt;Every screen and every process in SIcore follows the same pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript side&lt;/strong&gt;:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&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;res&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;HttpUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callJsonService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/services/module/ClassName&lt;/span&gt;&lt;span class="dl"&gt;'&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;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Java side&lt;/strong&gt;:&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="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doExecute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Io&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IoItems&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;LOAD_SQL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&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;Three files — HTML, JS, Java — and one screen works. No routing config, no bean definitions.&lt;/p&gt;

&lt;p&gt;AI performs better when patterns are consistent. SIcore documents its coding patterns (&lt;code&gt;21-event-coding-pattern.md&lt;/code&gt;), and loading that document into the AI's context produces highly accurate business code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lower token consumption
&lt;/h3&gt;

&lt;p&gt;AI tools like GitHub Copilot have limits on how much code (tokens) they can hold in context.&lt;/p&gt;

&lt;p&gt;Spring Boot is well-represented in AI training data, but version drift is significant — models generate outdated code or misread project-specific configurations. A &lt;strong&gt;custom framework&lt;/strong&gt; doesn't exist in training data at all, so it must be passed as context every time. But that's only a problem if the context is large.&lt;/p&gt;

&lt;p&gt;SIcore's core is small (Java ~5,000 lines, JavaScript ~2,000 lines), and the API reference documentation is compressed specifically for AI use. The entire framework can fit in a reasonable context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  No magic
&lt;/h3&gt;

&lt;p&gt;Annotations, DI, AOP — these are mechanisms where things happen "automatically" behind the scenes. AI knows these concepts in the abstract, but &lt;strong&gt;without the actual code in context, it can only guess at what your project-specific setup does&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In SIcore, the full execution flow is traceable in code. How a URL becomes a class name (#02), how HTML and JSON are transformed (#04) — all of it is readable. When generating code, AI never needs to &lt;em&gt;infer&lt;/em&gt; invisible behavior.&lt;/p&gt;

&lt;p&gt;The same applies to SQL. Because SIcore writes SQL directly in Java (#10), AI can see exactly what query runs — no XML-to-code linkage to trace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Works for beginners too
&lt;/h3&gt;

&lt;p&gt;What's easy for AI is easy for humans who are just starting out.&lt;/p&gt;

&lt;p&gt;Unified patterns mean you always know what to write next. No magic means you can follow why something works. SQL in code means you can see what data flows where. These are ideal conditions for learning enterprise app development.&lt;/p&gt;

&lt;p&gt;"Run it, then read it" is a valid learning strategy here. Someone with basic Java knowledge can start writing business code within days. (In theory. 🤞)&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcoming AI as a New Kind of Developer
&lt;/h2&gt;

&lt;p&gt;Frameworks have always been built for human users. But today, AI has joined the development team as a new kind of developer.&lt;/p&gt;

&lt;p&gt;SIcore chose &lt;strong&gt;not to hand AI tools designed for humans&lt;/strong&gt; but to design from scratch with AI as a first-class user. Choosing vanilla is the answer to that question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Series Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;One-liner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Motivation&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Beginner-friendly design turned out to be AI-friendly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL mapping&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;URL = class name. Zero routing config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-only&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Server never returns HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Mockup = implementation&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add a name attribute and the mockup becomes production code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b"&gt;Dynamic list rendering&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Template row auto-expands array data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;06&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo"&gt;Custom HTML attributes&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;data-* attributes for common patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i"&gt;Map-based data design&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No entities. Just Maps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;08&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3"&gt;Single-file CSS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;400 lines covers an entire business app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3"&gt;Client-side data + JWT&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Stateless design, no server sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54"&gt;SQL directly in Java&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No XML. SQL injection protection built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Why go vanilla? (this article)&lt;/td&gt;
&lt;td&gt;The design philosophy behind it all&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Laid out like this, every decision connects along one axis: adding nothing unnecessary. By not adding, the result became a framework that AI handles well and beginners can actually read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;"Vanilla" sounds old-fashioned. Inefficient, maybe. But in practice, vanilla is a &lt;em&gt;strength&lt;/em&gt; in the age of AI-assisted development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code that AI understands → &lt;strong&gt;higher generation accuracy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Code that beginners can read → &lt;strong&gt;junior devs become productive faster, with AI's help&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going vanilla isn't "old." It's a deliberate choice to add nothing unnecessary. A framework's value is defined not by what it adds, but by what it leaves out — that's the conclusion I reached after building this for the third time in ten years.&lt;/p&gt;

&lt;p&gt;You can run the sample code directly in VS Code with just a browser. Setup instructions are in the README. If any of this sounds interesting, I'd love for you to give it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All code and documentation is publicly available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com/" rel="noopener noreferrer"&gt;https://onepg.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/what-i-learned-developing-a-custom-framework-with-generative-ai-3npe"&gt;What I Learned Developing a Custom Framework &lt;em&gt;with&lt;/em&gt; Generative AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/when-is-it-ok-to-build-a-system-with-ai-alone-a-framework-for-thinking-about-responsibility-39on"&gt;When Is It OK to Build a System with AI Alone? A Framework for Thinking About Responsibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;
If you found this useful, reactions and comments are always appreciated. ❤️&lt;/p&gt;

</description>
      <category>java</category>
      <category>javascript</category>
      <category>framework</category>
      <category>ai</category>
    </item>
    <item>
      <title>Four Pagination Strategies for Enterprise Database Lists</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:07:43 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/four-pagination-strategies-for-enterprise-database-lists-2en0</link>
      <guid>https://dev.to/sugaiketadao/four-pagination-strategies-for-enterprise-database-lists-2en0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When implementing a paginated list screen in a business application, the first idea that comes to mind is usually "fetch rows with OFFSET/LIMIT."&lt;/p&gt;

&lt;p&gt;But once you actually run it, the problems pile up fast: "The same row appeared again on the next page," "A record I saw earlier is gone," "Page 1000 takes forever to load." OFFSET/LIMIT alone can't solve all of these.&lt;/p&gt;

&lt;p&gt;I've been adding pagination support to a Java framework I'm building from scratch, which prompted me to revisit and organize the options. This article walks through &lt;strong&gt;four pagination strategies I've actually used in enterprise projects&lt;/strong&gt;, along with their trade-offs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Pagination Needs to Get Right
&lt;/h2&gt;

&lt;p&gt;Before diving in, let's define what a solid pagination implementation should provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Correct data&lt;/strong&gt; — no missing rows, no duplicates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt; — minimal interference from other users' updates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acceptable DB load&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-controlled sort order&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two phenomena are worth calling out specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Row shift&lt;/strong&gt; — When another user inserts or deletes a row between page loads, rows that should appear on the next page are skipped, or rows from the previous page appear again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale read&lt;/strong&gt; — The list keeps showing snapshot data from the initial query and doesn't reflect updates made by other users during navigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example of row shift. You're viewing page 1 of 10 rows per page, then another user deletes ID:03:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         Normal
--- When page 1 loads ---   →    --- When page 2 loads ---
ID: 01 (sort rank  1)             ID: 11 (sort rank 11)
ID: 02                            ID: 12
ID: 03                            ID: 13
    ...                               ...
ID: 10 (sort rank 10)             ID: 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;↓ Another user deletes ID:03 before page 2 loads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- When page 2 loads ---
ID: 12  ← ID:11 moved to page 1 due to the deletion; not shown on page 2
ID: 13
ID: 14
  ...
ID: 21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No single strategy perfectly satisfies every requirement — you'll always be choosing a trade-off based on your project's priorities.&lt;/p&gt;

&lt;p&gt;When baking this into a framework, two additional constraints also matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implementation transparency&lt;/strong&gt; — simple enough to use without ceremony&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DBMS portability&lt;/strong&gt; — works across different database engines&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Strategy 1: Per-Request DB Access — Row Number (OFFSET)
&lt;/h2&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;   &lt;span class="c1"&gt;-- page 3 (10 rows per page)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simplest approach. Each page load tells the DB "give me M rows starting from row N."&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DBMS-specific syntax.&lt;/strong&gt; Oracle uses &lt;code&gt;FETCH FIRST N ROWS ONLY&lt;/code&gt;, MySQL uses &lt;code&gt;LIMIT ... OFFSET&lt;/code&gt;, SQL Server uses &lt;code&gt;OFFSET ... FETCH NEXT&lt;/code&gt;, and so on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High DB load at large offsets.&lt;/strong&gt; The DB must scan and skip all rows before the offset — the further you page, the more work it does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Row shift occurs&lt;/strong&gt; due to concurrent insertions or deletions. (See the diagram above.)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Strategy 2: Per-Request DB Access — Application-Side Scrolling
&lt;/h2&gt;

&lt;p&gt;Instead of delegating the offset to the DB, the application reads from the beginning of the ResultSet and skips rows itself until it reaches the desired page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java implementation sketch (JDBC):&lt;/strong&gt;&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="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PreparedStatement&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prepareStatement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT * FROM orders ORDER BY id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="nc"&gt;ResultSet&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeQuery&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip the first N rows in the application&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&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;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// No more data&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Collect M rows and return&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;Like Strategy 1, this queries the DB on every page load. But because no OFFSET is sent to the DB, &lt;strong&gt;the same code works across any DBMS&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App-side processing cost grows with page depth.&lt;/strong&gt; Viewing page 100 means skipping 990 rows in the application.&lt;/li&gt;
&lt;li&gt;Row shift happens in the same way as Strategy 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Variation: Key-based cursor&lt;/strong&gt;&lt;br&gt;
If you store the last row's key value from the previous page and filter with &lt;code&gt;WHERE id &amp;gt; :lastId&lt;/code&gt;, you can reduce the number of skipped rows and also mitigate row shift. The downside: changing sort order or jumping directly to an arbitrary page becomes difficult, and it's hard to encapsulate inside a framework.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Strategies 1 and 2 are in the "query the DB on every request" group. Strategies 3 and 4 belong to a different group: &lt;strong&gt;"query the DB once, then cache the result."&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Strategy 3: Single DB Query — Work File Cache
&lt;/h2&gt;

&lt;p&gt;On the first search, fetch all matching rows from the DB and write them to a file. Subsequent page loads read from that file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[First load]  DB → fetch all rows → write to file → display page 1
[Later loads] read from file → display page N
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No DB access after the first load — low DB load for navigation&lt;/li&gt;
&lt;li&gt;Consistency is guaranteed during navigation (no row shift)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stale reads occur.&lt;/strong&gt; The list shows a snapshot from the initial query; updates by other users are not reflected during navigation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-server deployments are problematic.&lt;/strong&gt; Files written on one web server aren't visible on others — shared or object storage is needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When do you delete the files?&lt;/strong&gt; You need a cleanup strategy: on session end, on logout, after a timeout, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Files multiply quickly.&lt;/strong&gt; Each concurrent user creates their own file.&lt;/li&gt;
&lt;li&gt;Large first-load queries can be heavy on memory and I/O (capping the result size helps).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Strategy 4: Single DB Query — Work Table Cache
&lt;/h2&gt;

&lt;p&gt;On the first search, fetch all matching rows and INSERT them into a dedicated work table. Subsequent page loads SELECT from that table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[First load]  DB → fetch all rows → INSERT into work table → SELECT from work table → display page 1
[Later loads] SELECT from work table → display page N
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is Strategy 3 with files replaced by a database table. The mechanics are essentially the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Same consistency guarantee as Strategy 3 — no row shift&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works fine in multi-server setups.&lt;/strong&gt; No need to worry about where files land.&lt;/li&gt;
&lt;li&gt;Pagination is just INSERT + SELECT&lt;/li&gt;
&lt;li&gt;Row numbers can be assigned at INSERT time, making direct page jumps straightforward&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stale reads occur&lt;/strong&gt;, just like Strategy 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When do you purge the work table?&lt;/strong&gt; A periodic cleanup job or session-lifecycle integration is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heavy concurrent usage bloats the work table.&lt;/strong&gt; Each active user adds their own rows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Each search type needs its own work table schema.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparison Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Per-Request DB Access (Strategies 1 &amp;amp; 2)
&lt;/h3&gt;

&lt;p&gt;Other users' updates are reflected in real time (no stale reads), but row shift can occur.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Strategy 1: OFFSET&lt;/th&gt;
&lt;th&gt;Strategy 2: App-side scrolling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Load location&lt;/td&gt;
&lt;td&gt;DB (OFFSET scan)&lt;/td&gt;
&lt;td&gt;App (skip loop)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DBMS portability&lt;/td&gt;
&lt;td&gt;Syntax varies by DBMS&lt;/td&gt;
&lt;td&gt;Works with any DBMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row shift&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale read&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Single DB Query + Cache (Strategies 3 &amp;amp; 4)
&lt;/h3&gt;

&lt;p&gt;The snapshot from the first query is preserved, so no row shift. But stale reads do occur.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Strategy 3: Work file&lt;/th&gt;
&lt;th&gt;Strategy 4: Work table&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pagination mechanism&lt;/td&gt;
&lt;td&gt;File I/O&lt;/td&gt;
&lt;td&gt;INSERT / SELECT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-server support&lt;/td&gt;
&lt;td&gt;Needs shared storage&lt;/td&gt;
&lt;td&gt;No issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup design needed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row shift&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale read&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Which Strategy Fits Which Situation?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Recommended Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rarely updated reference data (master lists, etc.)&lt;/td&gt;
&lt;td&gt;Strategy 1 (OFFSET) — simplest option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workflows where minor row shift is acceptable&lt;/td&gt;
&lt;td&gt;Strategy 1 or 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DBMS without OFFSET support, or DBMS-agnostic requirement&lt;/td&gt;
&lt;td&gt;Strategy 2 (app-side scrolling)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency is critical (approval flows, reports, etc.)&lt;/td&gt;
&lt;td&gt;Strategy 3 or 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-server deployment&lt;/td&gt;
&lt;td&gt;Strategy 4 (work table)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Work file and work table strategies win on consistency, but the cleanup design adds non-trivial operational complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;That's a rundown of four pagination strategies. There's no universally "correct" answer — the right choice depends on your project's characteristics.&lt;/p&gt;

&lt;p&gt;For my own framework, DBMS portability is a priority, so I'm leaning toward Strategy 2 (app-side scrolling) as the base implementation. But honestly, I'm still mulling it over. If you've handled pagination in a real project — or know of an approach I missed — I'd love to hear about it in the comments!&lt;/p&gt;




&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;My custom framework is published here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/what-i-learned-developing-a-custom-framework-with-generative-ai-3npe"&gt;What I Learned Developing a Custom Framework &lt;em&gt;with&lt;/em&gt; Generative AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/when-is-it-ok-to-build-a-system-with-ai-alone-a-framework-for-thinking-about-responsibility-39on"&gt;When Is It OK to Build a System with AI Alone?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla? — The Design Philosophy Behind SIcore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>sql</category>
      <category>java</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>When Is It OK to Build a System with AI Alone? A Framework for Thinking About Responsibility</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Sat, 14 Mar 2026 09:28:45 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/when-is-it-ok-to-build-a-system-with-ai-alone-a-framework-for-thinking-about-responsibility-39on</link>
      <guid>https://dev.to/sugaiketadao/when-is-it-ok-to-build-a-system-with-ai-alone-a-framework-for-thinking-about-responsibility-39on</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;These days, "I built X with AI alone" posts are everywhere. For personal projects, it feels like this has become the new normal. Yet in professional software development (by which I mean contract-based system development for clients), I've never heard anyone say "we built it with AI alone."&lt;/p&gt;

&lt;p&gt;So why, despite all the progress AI has made, do professional projects still not go "AI only"?&lt;/p&gt;

&lt;p&gt;I want to nail down my own answer to this question now — so that when AI capabilities increase even further, I have a benchmark to revisit and ask: "Does that change anything?"&lt;/p&gt;

&lt;p&gt;My conclusion: it comes down to &lt;strong&gt;responsibility&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Down "Responsibility"
&lt;/h2&gt;

&lt;p&gt;A contract for system development cannot include a clause like: "Built entirely by AI, so humans accept no liability for bugs." (Deeply discounted, of course!)&lt;/p&gt;

&lt;p&gt;So what does "responsibility" actually mean in a contract context? It breaks down into roughly three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Warranty period (free bug fixes)&lt;/strong&gt; — The &lt;em&gt;work&lt;/em&gt; of fixing bugs can involve AI. But the &lt;em&gt;liability&lt;/em&gt; itself — handling damage claims or contract termination — can only be borne by humans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The commitment to do everything possible when something goes wrong&lt;/strong&gt; — Apologizing, coordinating, explaining preventive measures. Only humans can do this. AI cannot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The resolve to minimize bugs as close to zero as possible&lt;/strong&gt; — Even humans can't promise perfection. But a human can say "we will do our absolute best." AI famously cannot guarantee zero bugs, and everyone knows it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short: &lt;strong&gt;only humans can be held responsible&lt;/strong&gt; — and that's the fundamental reason "AI only" is a step no one takes in professional development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Systems Where AI Alone Is Dangerous
&lt;/h2&gt;

&lt;p&gt;With that understanding of responsibility, these domains still require human involvement:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Healthcare (diagnosis, medication, etc.)&lt;/td&gt;
&lt;td&gt;Directly affects life and death. The cost of errors is too high.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payments and finance&lt;/td&gt;
&lt;td&gt;Financial damage can extend to third parties.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legal documents between companies (invoices, purchase orders, etc.)&lt;/td&gt;
&lt;td&gt;Legally binding. Errors affect relationships outside the organization.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The common thread: &lt;strong&gt;errors have direct, irreversible impacts on people or society&lt;/strong&gt;. Using AI-generated output in these fields without human review is, in my view, still too risky at this point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Systems Where AI Alone Is Fine
&lt;/h2&gt;

&lt;p&gt;On the other hand, these kinds of systems have no significant barrier to being built AI-only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Games&lt;/li&gt;
&lt;li&gt;Bill-splitting calculators&lt;/li&gt;
&lt;li&gt;Inventory trackers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common thread: errors are limited in impact and easy to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In professional contract work, the developer using AI is a &lt;strong&gt;human who holds the contract&lt;/strong&gt;. Whether to use AI as a development tool is fundamentally up to the contractor's discretion — and the real question is &lt;strong&gt;whether humans can be accountable for the output&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AI is just a tool. No matter how capable the tool gets, the questions of &lt;em&gt;what you build with it&lt;/em&gt; and &lt;em&gt;who takes responsibility&lt;/em&gt; don't change. To put it bluntly: in a contract dispute, the worst case is court. AI cannot stand trial.&lt;/p&gt;

&lt;p&gt;"Built entirely with AI" is something you can proudly say only when the impact of mistakes stays entirely within the maker's own sphere — that's my conclusion for now.&lt;/p&gt;

&lt;p&gt;That said, even life-critical systems like autonomous vehicles are increasingly powered by AI. But they're not "AI alone" — they work because of strict legal frameworks, multiple layers of safety design, and a clear chain of human accountability. Which suggests: wherever that kind of structure can be built, AI adoption will follow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/what-i-learned-developing-a-custom-framework-with-generative-ai-3npe"&gt;What I Learned Developing a Custom Framework &lt;em&gt;with&lt;/em&gt; Generative AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla? — The Design Philosophy Behind SIcore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>systemdevelopment</category>
      <category>careerdevelopment</category>
      <category>programming</category>
    </item>
    <item>
      <title>SQL Directly in Java — Why I Built a Java Framework (third attempt in 10 years) #10</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Sat, 07 Mar 2026 16:06:09 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54</link>
      <guid>https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Over the years I've written SQL in XML files, embedded it in entity classes, and built it inline in Java. Each approach has trade-offs, but two pain points kept recurring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic search conditions&lt;/strong&gt; — the more conditions you add, the harder the XML &lt;code&gt;&amp;lt;if&amp;gt;&lt;/code&gt; tags become to read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing performance&lt;/strong&gt; — when INSERT/UPDATE runs inside a loop, you start worrying about PreparedStatement overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SIcore's answer is &lt;strong&gt;two SQL-building classes&lt;/strong&gt; tailored to each use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SqlBuilder&lt;/code&gt; — assembles SQL dynamically (primarily for web service search queries)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SqlConst&lt;/code&gt; — defines SQL statically (primarily for batch processing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article introduces both and explains when to use each.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Article Covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The friction of XML-based SQL management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SqlBuilder&lt;/code&gt; — how dynamic SQL assembly works&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SqlConst&lt;/code&gt; — static SQL with type-safe binding&lt;/li&gt;
&lt;li&gt;PreparedStatement caching with &lt;code&gt;SqlConst&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Combining with Java Text Blocks (&lt;code&gt;"""&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem with XML-Based SQL
&lt;/h2&gt;

&lt;p&gt;O/R mappers like MyBatis write SQL in XML files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"searchUsers"&lt;/span&gt; &lt;span class="na"&gt;resultType=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  SELECT u.user_id, u.user_nm, u.email
  FROM t_user u
  WHERE 1=1
  &lt;span class="nt"&gt;&amp;lt;if&lt;/span&gt; &lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;"userId != null and userId != ''"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    AND u.user_id = #{userId}
  &lt;span class="nt"&gt;&amp;lt;/if&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;if&lt;/span&gt; &lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;"userNm != null and userNm != ''"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    AND u.user_nm LIKE '%' || #{userNm} || '%'
  &lt;span class="nt"&gt;&amp;lt;/if&amp;gt;&lt;/span&gt;
  ORDER BY u.user_id
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The content is readable enough, but several friction points appear before you can use it effectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL is &lt;strong&gt;separated into a different file (XML)&lt;/strong&gt;, making it hard to trace where it's used. On the Java side, mapping uses an &lt;strong&gt;ID string&lt;/strong&gt; like &lt;code&gt;searchUsers&lt;/code&gt; — renaming or deleting won't produce a compile error&lt;/li&gt;
&lt;li&gt;You need to &lt;strong&gt;learn the XML tag syntax&lt;/strong&gt; for &lt;code&gt;&amp;lt;if test="..."&amp;gt;&lt;/code&gt; and friends&lt;/li&gt;
&lt;li&gt;You constantly &lt;strong&gt;switch between the XML file and the Java class&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The difference between &lt;code&gt;#{}&lt;/code&gt; and &lt;code&gt;${}&lt;/code&gt; is a SQL injection trap — &lt;code&gt;${}&lt;/code&gt; embeds values directly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dynamic SQL is where the friction really hits. The more conditions you add, the more tags pile up, and the harder the file becomes to read. And there's one more subtle annoyance: comparison operators like &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; must be written as XML entities (&lt;code&gt;&amp;amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;amp;gt;&lt;/code&gt;) — easy to forget, and mildly infuriating every time.&lt;/p&gt;

&lt;p&gt;SIcore takes a different approach: build SQL using a &lt;strong&gt;Java library with SQL injection protection built in&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SqlBuilder — Dynamic SQL Assembly
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SqlBuilder&lt;/code&gt; assembles SQL and bind parameters together. It's designed for &lt;strong&gt;search queries in web services&lt;/strong&gt;, where WHERE clause conditions are added only when the corresponding input has a value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SqlBuilder&lt;/span&gt; &lt;span class="n"&gt;sb&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;SqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  u.user_id "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", u.user_nm "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", u.email "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" FROM t_user u "&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" WHERE 1=1 "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// method chaining also works&lt;/span&gt;

&lt;span class="c1"&gt;// Add WHERE condition only if value is not blank&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQnotB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   AND u.user_id = ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQnotB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   AND u.user_nm LIKE '%' || ? || '%' "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQnotB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   AND u.email LIKE ? || '%' "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQnotB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   AND u.income_am &amp;gt;= ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBigDecimalNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQnotB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   AND u.birth_dt = ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDateNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" ORDER BY u.user_id "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Execute the query&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;IoRows&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectBulk&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;addQnotB&lt;/code&gt; (&lt;strong&gt;Q&lt;/strong&gt;uery if &lt;strong&gt;not&lt;/strong&gt; &lt;strong&gt;B&lt;/strong&gt;lank) skips the SQL fragment when the parameter is &lt;code&gt;null&lt;/code&gt; or blank. That's the core of dynamic SQL assembly here.&lt;/p&gt;

&lt;p&gt;Unlike XML's &lt;code&gt;&amp;lt;if test="..."&amp;gt;&lt;/code&gt;, the code looks the same regardless of which conditions are active. &lt;code&gt;addQuery&lt;/code&gt; and &lt;code&gt;addQnotB&lt;/code&gt; just line up uniformly — the SQL structure is readable directly from the code.&lt;/p&gt;

&lt;p&gt;When complex branching is needed, you're not locked into &lt;code&gt;addQnotB&lt;/code&gt;. You can mix in regular Java &lt;code&gt;if&lt;/code&gt; statements with &lt;code&gt;addQuery&lt;/code&gt;. Because it's standard Java code, it's easy to debug and easy for AI to parse.&lt;/p&gt;

&lt;p&gt;Bind variables (&lt;code&gt;?&lt;/code&gt;) are always used, so SQL injection is not a concern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variable-Length IN Clauses
&lt;/h3&gt;

&lt;p&gt;Writing an IN clause in MyBatis requires the &lt;code&gt;&amp;lt;foreach&amp;gt;&lt;/code&gt; tag, which surprises many developers. With &lt;code&gt;addListInBind&lt;/code&gt;, you just pass a list and the placeholders expand 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="kd"&gt;final&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;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;statusList&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;statusList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;statusList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;statusList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" AND u.status_cs IN ("&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addListInBind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statusList&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Generated SQL: AND u.status_cs IN (?,?,?)&lt;/span&gt;
&lt;span class="c1"&gt;// ? expands to match list size; each value is passed as a bind parameter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addQuery(sql)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append SQL only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addQuery(sql, params...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append SQL and parameters (multiple params allowed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addQnotB(sql, param)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append SQL and parameter only if param is not null/blank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addParams(params...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append parameters only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addListInBind(list)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append variable IN clause placeholders and parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addSqlBuilder(other)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merge another SqlBuilder into this one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  SqlConst — Static SQL Definition
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SqlConst&lt;/code&gt; defines &lt;strong&gt;fixed, immutable SQL&lt;/strong&gt;. It shines in batch processing where the same SQL runs repeatedly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Usage (Declare as a Class Field)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SqlConst&lt;/span&gt; &lt;span class="no"&gt;SQL_INS_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlConst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INSERT INTO t_user ( "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  user_id "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", user_nm "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", email "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", income_am "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", birth_dt "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" ) VALUES ( "&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;   &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;   &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;     &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BIGDECIMAL&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&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;end&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You chain calls from &lt;code&gt;begin()&lt;/code&gt;, building SQL and bind definitions together, then call &lt;code&gt;end()&lt;/code&gt; to produce an immutable &lt;code&gt;SqlConst&lt;/code&gt; object. Declared as &lt;code&gt;static final&lt;/code&gt;, it's built once at application startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bind Types (BindType)
&lt;/h3&gt;

&lt;p&gt;The mapping between SQL columns and bind types is declared explicitly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;BindType&lt;/th&gt;
&lt;th&gt;SQL Type&lt;/th&gt;
&lt;th&gt;Java Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STRING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;VARCHAR and other strings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BIGDECIMAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;INTEGER, NUMERIC, DECIMAL and other numerics&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BigDecimal&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DATE (yyyyMMdd format)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java.sql.Date&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP (yyyyMMddHHmmssSSS format)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java.sql.Timestamp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;When &lt;code&gt;bind(io)&lt;/code&gt; is called, values are retrieved and converted using the corresponding io method for each BindType: &lt;code&gt;getString&lt;/code&gt;, &lt;code&gt;getBigDecimal&lt;/code&gt;, &lt;code&gt;getDate&lt;/code&gt;, and &lt;code&gt;getTimestamp&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Binding at Runtime
&lt;/h3&gt;

&lt;p&gt;At execution time, &lt;code&gt;bind(io)&lt;/code&gt; extracts values from a parameter map and produces a bound &lt;code&gt;SqlBean&lt;/code&gt;:&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;// Extract values from io (map), bind them, and execute&lt;/span&gt;
&lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;SQL_INS_USER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, &lt;code&gt;bind()&lt;/code&gt; pulls values from the map in the order declared at definition time, converting each to the declared type. The order and types guaranteed at declaration time are enforced at runtime — type mismatches and ordering errors are managed in the code itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  PreparedStatement Caching with SqlConst
&lt;/h2&gt;

&lt;p&gt;When INSERT/UPDATE runs inside a batch loop, the normal &lt;code&gt;executeOne&lt;/code&gt; creates and destroys a PreparedStatement on every call. &lt;code&gt;executeOneCache&lt;/code&gt; (or &lt;code&gt;executeCache&lt;/code&gt;) caches and reuses the PreparedStatement, improving performance:&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;@Override&lt;/span&gt;
&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doExecute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;IoItems&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Read CSV row by row and UPSERT&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CsvReader&lt;/span&gt; &lt;span class="n"&gt;cr&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;CsvReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputPath&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CharSet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UTF8&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CsvType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DQ_ALL_LF&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;IoItems&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cr&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Try UPDATE; if 0 rows affected, INSERT (UPSERT)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeOneCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;SQL_UPD_USER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeOneCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;SQL_INS_USER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache is held &lt;strong&gt;per &lt;code&gt;DbConn&lt;/code&gt; instance&lt;/strong&gt;. When the connection closes, the cache is released.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Return&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;executeOne(conn, sb)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Normal execution. &lt;code&gt;true&lt;/code&gt; for 1 row, &lt;code&gt;false&lt;/code&gt; for 0, exception for multiple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;executeOneCache(conn, sb)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cached execution. &lt;code&gt;SqlConst&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;execute(conn, sb)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Normal execution. Returns row count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;executeCache(conn, sb)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cached execution. Returns row count&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;SqlBuilder&lt;/code&gt; (dynamic SQL) is not eligible for caching because the SQL string changes on every call. Caching only makes sense for fixed SQL like &lt;code&gt;SqlConst&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Can't MyBatis Cache Too?
&lt;/h3&gt;

&lt;p&gt;MyBatis-style frameworks do support PreparedStatement caching. However, the mechanism is different.&lt;/p&gt;

&lt;p&gt;Caching depends on the JDBC driver and its defaults vary by environment — MySQL Connector/J defaults to &lt;strong&gt;off&lt;/strong&gt; (&lt;code&gt;cachePrepStmts=true&lt;/code&gt; required), while PostgreSQL defaults to &lt;strong&gt;on&lt;/strong&gt; per connection. Even when enabled, there's no way to see from code which SQL statements are cached, and dynamic SQL may end up cached unnecessarily.&lt;/p&gt;

&lt;p&gt;SIcore's &lt;code&gt;executeOneCache&lt;/code&gt; lets you &lt;strong&gt;explicitly&lt;/strong&gt; nominate which SQL to cache at the application level. It works consistently across any JDBC driver, and reading the code alone tells you "this SQL is cached and reused."&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining with Java Text Blocks
&lt;/h2&gt;

&lt;p&gt;Java 15+ &lt;strong&gt;Text Blocks&lt;/strong&gt; (&lt;code&gt;"""&lt;/code&gt;) let you write multi-line SQL strings naturally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SqlBuilder:&lt;/strong&gt;&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="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
    SELECT
      u.user_id
    , u.user_nm
    , u.email
    FROM t_user u
    WHERE 1=1
    """&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQnotB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   AND u.user_id = ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With SqlConst:&lt;/strong&gt;&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="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SqlConst&lt;/span&gt; &lt;span class="no"&gt;SQL_INS_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlConst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
        INSERT INTO t_user (
          user_id
        , user_nm
        , email
        , income_am
        , birth_dt
        ) VALUES (
        """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;   &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;   &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;     &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BIGDECIMAL&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ? "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="nc"&gt;BindType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&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;end&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating the SQL block from the bind-definition lines keeps readability high while maintaining type-safe binding.&lt;/p&gt;

&lt;p&gt;Note: the framework requires Java 11+, so confirm your project's Java version before adopting Text Blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  SqlBuilder vs SqlConst — When to Use Which
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;SqlBuilder&lt;/th&gt;
&lt;th&gt;SqlConst&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL nature&lt;/td&gt;
&lt;td&gt;Dynamic (changes by condition)&lt;/td&gt;
&lt;td&gt;Static (fixed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary use&lt;/td&gt;
&lt;td&gt;Web service search queries&lt;/td&gt;
&lt;td&gt;Batch INSERT/UPDATE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bind type declaration&lt;/td&gt;
&lt;td&gt;Not required (pass as Object)&lt;/td&gt;
&lt;td&gt;Required (explicit BindType)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PS caching&lt;/td&gt;
&lt;td&gt;Not supported (SQL changes per call)&lt;/td&gt;
&lt;td&gt;Supported (&lt;code&gt;executeOneCache&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Declaration location&lt;/td&gt;
&lt;td&gt;Inside methods&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;static final&lt;/code&gt; class field&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule is simple: dynamic conditions → &lt;code&gt;SqlBuilder&lt;/code&gt;, fixed SQL → &lt;code&gt;SqlConst&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SqlUtil&lt;/code&gt; also provides methods like &lt;code&gt;insertOne&lt;/code&gt;, &lt;code&gt;updateOne&lt;/code&gt;, and &lt;code&gt;deleteOne&lt;/code&gt; that generate INSERT/UPDATE/DELETE SQL automatically from database metadata — just pass the table name and &lt;code&gt;IoItems&lt;/code&gt;. These cover simple single-record operations without writing SQL at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Both &lt;code&gt;SqlBuilder&lt;/code&gt; and &lt;code&gt;SqlConst&lt;/code&gt; ultimately produce a &lt;code&gt;SqlBean&lt;/code&gt; (SQL string + parameter list) and hand it to &lt;code&gt;SqlUtil&lt;/code&gt; — the same interface. The calling code doesn't change. This consistency is what makes the dynamic/static split feel natural rather than awkward.&lt;/p&gt;

&lt;p&gt;Writing SQL in Java also means XML syntax to learn disappears, and SQL, conditions, and bind values all live in the same file. The code flow is straightforward to follow.&lt;/p&gt;

&lt;p&gt;That advantage extends to AI-assisted development. In XML-based approaches, AI reading your Java code has to also load the XML file and interpret its tag syntax before understanding the query. With Java-inline SQL, SQL, branching, and bind values all live in the same file — one file read is enough to understand the entire operation, which leads to more accurate completions and refactoring suggestions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SqlBuilder example: &lt;a href="https://github.com/sugaiketadao/sicore/blob/main/src/com/example/app/service/exmodule/ExampleListSearch.java" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore/blob/main/src/com/example/app/service/exmodule/ExampleListSearch.java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SqlConst example: &lt;a href="https://github.com/sugaiketadao/sicore/blob/main/src/com/example/app/bat/exmodule/ExampleImport.java" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore/blob/main/src/com/example/app/bat/exmodule/ExampleImport.java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Auto-generated SQL example: &lt;a href="https://github.com/sugaiketadao/sicore/blob/main/src/com/example/app/service/exmodule/ExampleUpsert.java" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore/blob/main/src/com/example/app/service/exmodule/ExampleUpsert.java&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;02 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL-to-Class Mapping&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;03 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-Only Communication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;04 &lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Mockup = Implementation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;05 &lt;a href="https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b"&gt;Dynamic List Rendering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;06 &lt;a href="https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo"&gt;Custom HTML Attributes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;07 &lt;a href="https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i"&gt;Map-Based Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;08 &lt;a href="https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3"&gt;Single-File CSS Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;09 &lt;a href="https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3"&gt;Client-Side Data Management and JWT Auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;10 SQL Directly in Java (this article)&lt;/li&gt;
&lt;li&gt;11 &lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>java</category>
      <category>sql</category>
      <category>framework</category>
      <category>ai</category>
    </item>
    <item>
      <title>Client-Side Data Management - Why I Built a Java Framework (third attempt in 10 years) #09</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Sat, 07 Mar 2026 04:58:54 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3</link>
      <guid>https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you've ever built business web applications, you've probably hit these friction points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Passing large amounts of data between pages&lt;/strong&gt; — do you really need to round-trip through the server?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side sessions are heavy&lt;/strong&gt; — resource consumption, session sharing across scaled-out nodes, timeout management…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building an auth system from scratch is painful&lt;/strong&gt; — LDAP integration, JWT issuance and validation, deciding where to store tokens…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article presents &lt;strong&gt;three design approaches&lt;/strong&gt; that tackle these problems together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scoped browser storage&lt;/strong&gt; — pass data between pages safely, organized by URL scope&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JS-variable session management&lt;/strong&gt; — client-side session that auto-syncs with the server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent JWT authentication&lt;/strong&gt; — LDAP + JWT with zero authentication code in business logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These three are tightly coupled. Combined, they produce a state where &lt;strong&gt;no auth or data-management code leaks into your business layer&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Scoped Browser Storage — Let Directory Structure Be Your Scope
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem: Page-to-Page Data Passing Gets Messy
&lt;/h3&gt;

&lt;p&gt;Without a single-page app, every page transition requires a data handoff. Common approaches and their drawbacks:&lt;/p&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;Problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL parameters&lt;/td&gt;
&lt;td&gt;Explodes in length with many fields. Timestamps and binary data are awkward.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidden fields&lt;/td&gt;
&lt;td&gt;Embedded in HTML, hard to manage. Accidentally receiving stale data is easy. Detail lists are especially painful.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cookies&lt;/td&gt;
&lt;td&gt;Size-limited. Sent to the server on every request.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw &lt;code&gt;sessionStorage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Keys collide easily. No way to tell which page's data is which.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Solution: URL-Scoped Storage
&lt;/h3&gt;

&lt;p&gt;Rather than using &lt;code&gt;sessionStorage&lt;/code&gt; directly, SIcore wraps it with &lt;strong&gt;automatic scope detection derived from the URL path&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Page scope&lt;/td&gt;
&lt;td&gt;Data valid only within the current page&lt;/td&gt;
&lt;td&gt;Temporary search conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module scope&lt;/td&gt;
&lt;td&gt;Shared across pages in the same directory&lt;/td&gt;
&lt;td&gt;Passing header data to a detail-edit page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System scope&lt;/td&gt;
&lt;td&gt;Shared across the entire application&lt;/td&gt;
&lt;td&gt;User preferences&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/app/exmodule/head-edit.html    →  module key: "/app/exmodule/"
/app/exmodule/detail-edit.html  →  module key: "/app/exmodule/"  ← same!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pages in the same directory automatically share data. There's no naming convention to memorize — your directory layout &lt;em&gt;is&lt;/em&gt; your scope design.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works in SIcore
&lt;/h3&gt;

&lt;p&gt;SIcore provides the &lt;code&gt;StorageUtil&lt;/code&gt; class for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Header edit page (head-edit.js):&lt;/strong&gt;&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="c1"&gt;// Save the whole form to storage, then navigate to the next page&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;StorageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setModuleObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;headData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;HttpUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;movePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail-edit.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Detail edit page (detail-edit.js):&lt;/strong&gt;&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="c1"&gt;// Retrieve from storage and populate the form&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StorageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getModuleObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;headData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caller doesn't need to know which page stored the data. As long as both pages live in the same module directory, data is naturally shared. The actual storage key becomes something like &lt;code&gt;_module@/app/exmodule/headData&lt;/code&gt; — the scope and URL path are injected automatically, so different modules never collide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No key collisions&lt;/strong&gt; — scopes are automatically isolated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory design = data design&lt;/strong&gt; — split by feature directory and scopes fall into place&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No URL parameters needed&lt;/strong&gt; — works cleanly regardless of field count&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. JS-Variable Session Management — Auto-Sync With the Server
&lt;/h2&gt;

&lt;p&gt;Session data is held in &lt;strong&gt;JavaScript variables&lt;/strong&gt;, not in &lt;code&gt;localStorage&lt;/code&gt; or &lt;code&gt;sessionStorage&lt;/code&gt;. A page reload clears it — and that turns out to be a security feature, not just a limitation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Server-Side Sessions Are Heavy
&lt;/h3&gt;

&lt;p&gt;Traditional server-side sessions (&lt;code&gt;HttpSession&lt;/code&gt;, etc.) come with known costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consume server memory (OutOfMemoryError is a real concern at scale)&lt;/li&gt;
&lt;li&gt;Require session sharing mechanisms when scaling horizontally&lt;/li&gt;
&lt;li&gt;Need timeout management and cleanup logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution: JS Variables + Automatic Sync
&lt;/h3&gt;

&lt;p&gt;SIcore stores session data in a &lt;strong&gt;JS variable&lt;/strong&gt; and automatically sends and receives it under the reserved &lt;code&gt;_session&lt;/code&gt; key on every server call.&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="c1"&gt;// Inside the framework's server-call logic (simplified)&lt;/span&gt;

&lt;span class="c1"&gt;// Before sending the request: attach session data automatically&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// After receiving the response: update session data automatically&lt;/span&gt;
&lt;span class="nx"&gt;sessionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the server side, business code simply reads from and writes to the &lt;code&gt;_session&lt;/code&gt; key in the JSON. &lt;strong&gt;The server holds no session state&lt;/strong&gt; — it's entirely client-driven.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Storage vs. JS-Variable Session
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Browser Storage&lt;/th&gt;
&lt;th&gt;JS-Variable Session&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where stored&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sessionStorage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JS variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server sync&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Auto-synced on every call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survives reload&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No — cleared on reload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use for&lt;/td&gt;
&lt;td&gt;Page-to-page data handoff&lt;/td&gt;
&lt;td&gt;Data shared with the server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you want to carry data &lt;em&gt;between pages&lt;/em&gt;, use storage. If you want data &lt;em&gt;shared with the server&lt;/em&gt;, use session. The distinction is clear.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stateless server&lt;/strong&gt; — no session state on the server side; easy horizontal scaling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean business code&lt;/strong&gt; — the framework handles sync; business code never thinks about it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clears on reload&lt;/strong&gt; — JS variables vanish on page refresh; nothing lingers in storage, which is a security advantage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Transparent JWT Authentication — Business Code Doesn't Know Auth Exists
&lt;/h2&gt;

&lt;p&gt;Building auth from scratch — LDAP integration, JWT issuance and validation, choosing where to store tokens — is genuinely tedious. SIcore isolates authentication into &lt;strong&gt;three layers&lt;/strong&gt;, hiding it completely from business code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;th&gt;Business code involvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sign-in&lt;/td&gt;
&lt;td&gt;LDAP auth → JWT issuance → store in JS variable&lt;/td&gt;
&lt;td&gt;Sign-in page only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token management&lt;/td&gt;
&lt;td&gt;Hold token in JS variable; attach automatically to every request&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server validation&lt;/td&gt;
&lt;td&gt;Validate JWT on every request; return 401 on failure&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Business pages call the server with zero authentication code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Flow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;At sign-in:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Browser] User enters ID and password on the sign-in page
     ↓
[Server] Authenticate via LDAP → issue JWT on success
     ↓  Response JSON: { _session: { token: "hoge..." } }
[Browser] Receive _session.token
     ↓  Store token in JS variable
[Browser] Navigate to the business page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All subsequent business operations (fully automatic):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Browser] Business page calls callJsonService() — no auth code
     ↓  Framework automatically adds: Authorization: Bearer hoge...
[Server] Validate JWT → success → run business logic → return response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Store the Token in a JS Variable?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt; persist the token across page reloads, eliminating the need to re-authenticate. But both are &lt;strong&gt;readable by scripts via XSS attacks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Storing in a JS variable means the user re-authenticates on every reload — but it also eliminates the token theft vector that persisted storage introduces.&lt;/p&gt;

&lt;p&gt;In typical business app usage, page reloads are rare during normal operation. And in SI project environments, "closing the browser signs you out" is often the &lt;em&gt;expected&lt;/em&gt; behavior — many clients prefer it that way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Side JWT Validation (Java)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AbstractHttpHandler.java (excerpt)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;validateJwt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;HttpExchange&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequestHeaders&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFirst&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&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;authHeader&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="n"&gt;authHeader&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;"Bearer "&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="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;JwtUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validateToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authHeader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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;&lt;strong&gt;Sign-in service (Java)&lt;/strong&gt; — LDAP auth → JWT issuance:&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;// SigninService.java (excerpt)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&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;InitialDirContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// LDAP authentication&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NamingException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ValUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BLANK&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putMsg&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MsgType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"es001"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JwtUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Automatic token attachment (JavaScript)&lt;/strong&gt; — business code is not involved:&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="c1"&gt;// Inside the framework's server-call logic (simplified)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SessionUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ValUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBlank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;token&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;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clean business code&lt;/strong&gt; — token management, header injection, and 401 handling are all transparent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XSS resistance&lt;/strong&gt; — no persisted token in storage means no storage-based token theft&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateless&lt;/strong&gt; — JWT-based; no server-side session required&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here's a summary of the three approaches:&lt;/p&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;Problem&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scoped storage&lt;/td&gt;
&lt;td&gt;Messy page-to-page data passing&lt;/td&gt;
&lt;td&gt;Auto-detect scope from URL path&lt;/td&gt;
&lt;td&gt;No key collisions; directory = scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JS-variable session&lt;/td&gt;
&lt;td&gt;Heavy server-side sessions&lt;/td&gt;
&lt;td&gt;Client-side JS variable + auto-sync&lt;/td&gt;
&lt;td&gt;Stateless server; easy scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transparent JWT auth&lt;/td&gt;
&lt;td&gt;Auth boilerplate is painful&lt;/td&gt;
&lt;td&gt;Layer separation; hidden from business code&lt;/td&gt;
&lt;td&gt;Clean code; XSS resistance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three share the same philosophy: &lt;strong&gt;keep business code free from infrastructure concerns&lt;/strong&gt;. Data management, session sync, and authentication are all handled by the framework layer — so you can stay focused on the actual business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;02 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL-to-Class Mapping&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;03 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-Only Communication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;04 &lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Mockup = Implementation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;05 &lt;a href="https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b"&gt;Dynamic List Rendering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;06 &lt;a href="https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo"&gt;Custom HTML Attributes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;07 &lt;a href="https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i"&gt;Map-Based Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;08 &lt;a href="https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3"&gt;Single-File CSS Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;09 Client-Side Data Management and JWT Auth (this article)&lt;/li&gt;
&lt;li&gt;10  &lt;a href="https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54"&gt;SQL Directly in Java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;11 &lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>java</category>
      <category>jwt</category>
      <category>security</category>
    </item>
    <item>
      <title>What I Learned Developing a Custom Framework *with* Generative AI</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Mon, 23 Feb 2026 04:35:12 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/what-i-learned-developing-a-custom-framework-with-generative-ai-3npe</link>
      <guid>https://dev.to/sugaiketadao/what-i-learned-developing-a-custom-framework-with-generative-ai-3npe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Since around the summer of 2025, I've been building a business application framework for Japan's SI industry as a personal project. From the very start, I fully adopted &lt;strong&gt;GitHub Copilot (generative AI)&lt;/strong&gt; as a development partner.&lt;/p&gt;

&lt;p&gt;In this article, I'll share what I experienced while coding alongside generative AI — the fun moments, the frustrating ones, and the surprising discoveries.&lt;/p&gt;

&lt;p&gt;My development style: &lt;strong&gt;I write the code myself and have the AI review it.&lt;/strong&gt; Rather than having AI generate everything, I use it as a reviewer for code I've already written.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Joy of Being Code-Reviewed After 20 Years
&lt;/h2&gt;

&lt;p&gt;For nearly 20 years, I've always been on the &lt;em&gt;giving&lt;/em&gt; side of code reviews. I had no opportunity to have my own code reviewed.&lt;/p&gt;

&lt;p&gt;When you show your code to AI, it genuinely reviews it. And it &lt;strong&gt;lavishes you with praise&lt;/strong&gt;. "This design is excellent," "This is enterprise-level" — being on the receiving end of a review after 20 years was a genuinely fun experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My wife also chats with the AI (she calls it "Chad") and says it always praises her too. Maybe it has a "praise to encourage" policy?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course it doesn't just compliment you — it also suggests improvements. The catch is that those suggestions are &lt;strong&gt;occasionally wrong&lt;/strong&gt; (more on that below).&lt;/p&gt;

&lt;h2&gt;
  
  
  Strong with Common Specs — Tool Creation at Warp Speed
&lt;/h2&gt;

&lt;p&gt;AI shines brightest when working with &lt;strong&gt;widely known specifications and formats&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;During this framework project, I needed a tool to parse JavaDoc source and generate an HTML reference in a custom format. Since JavaDoc syntax is a well-known spec, I described the requirements and the AI wrote &lt;strong&gt;working code from parser to HTML generation almost immediately&lt;/strong&gt;. It's the same principle as "make me a Tetris game" — the more public the spec, the higher the AI's generation accuracy.&lt;/p&gt;

&lt;p&gt;On the flip side, accuracy drops with &lt;strong&gt;specs the AI doesn't know about&lt;/strong&gt;, like the custom APIs of a homegrown framework — and worse, it starts confidently using methods that don't exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Non-Existent Methods with Full Confidence
&lt;/h2&gt;

&lt;p&gt;With popular frameworks like Spring or React, AI can work correctly because it's been trained on massive amounts of data. But a custom framework's API simply doesn't exist in that training data. The result: it generates code that &lt;strong&gt;calls non-existent methods as if they already exist&lt;/strong&gt; — not creating new methods, but treating them as established API calls.&lt;/p&gt;

&lt;p&gt;This is the infamous phenomenon known as &lt;strong&gt;hallucination&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a countermeasure, I added to &lt;code&gt;copilot-instructions.md&lt;/code&gt; (GitHub Copilot's project-specific instruction file): "Do not use methods that don't exist in the API reference." But it still happened. When I pressed the AI on it, it replied: &lt;strong&gt;"Because you didn't write 'absolutely must not.'"&lt;/strong&gt; (Give me a break 👋)&lt;/p&gt;

&lt;p&gt;I later found out the AI model I was using had a relatively high creativity setting (temperature). For code generation, choosing a &lt;strong&gt;precision-focused model&lt;/strong&gt; is crucial. Now I explicitly list it under "Absolutely Prohibited" in &lt;code&gt;copilot-instructions.md&lt;/code&gt; and enforce a workflow that pre-loads the API reference document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrong Suggestions and "I'm Sorry"
&lt;/h2&gt;

&lt;p&gt;AI occasionally gives incorrect feedback. And when you say "Please check again," it initially insists it was right. After the second or third time asking, it finally admits the mistake and says &lt;strong&gt;"I'm sorry"&lt;/strong&gt; — which was honestly amusing.&lt;/p&gt;

&lt;p&gt;Here's a concrete example. The following is an implementation for thread-safe log output:&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;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Store in cache first, then output&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;lineCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;offer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nvl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&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="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPrinting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareAndSet&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="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Switch to printing mode and flush cache&lt;/span&gt;
      &lt;span class="n"&gt;cachePrint&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Reset flag even if an exception occurs&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;isPrinting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&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="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Cache output (serial).
 */&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cachePrint&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Only output what's cached at this moment.&lt;/span&gt;
  &lt;span class="c1"&gt;// Stop after current size to avoid long-running loops.&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cacheSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lineCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;cacheSize&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;cache&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;lineCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;poll&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;cache&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="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;super&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;cache&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;&lt;code&gt;ConcurrentLinkedQueue&lt;/code&gt; for cache storage, &lt;code&gt;AtomicBoolean&lt;/code&gt; for exclusion control, &lt;code&gt;synchronized&lt;/code&gt; for serialization — a fully thread-safe design. But no matter how many times I asked, the AI kept insisting &lt;strong&gt;"This will error with multiple threads."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It eventually acknowledged my design was correct, but if I had blindly followed its advice, I would have broken working code. &lt;strong&gt;You need to think for yourself: "Is that really true?" when the AI makes a claim.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Same Instruction, Different Results
&lt;/h2&gt;

&lt;p&gt;This framework maintains both a Japanese-commented source and an English-commented source (the Java logic is identical). I once gave the same modification instruction to both. The result: &lt;strong&gt;different code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It seems that the presence of Japanese vs. English comments subtly shifts the AI's interpretation and code generation tendencies. In the end, I settled on a workflow: modify the Japanese source first, copy it, then have the AI translate only the JavaDoc portions into English. &lt;strong&gt;Even with the same instruction, changing the context changes the result — don't expect too much reproducibility.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adopting a Reverse Proposal from the AI
&lt;/h2&gt;

&lt;p&gt;I use development documents for &lt;strong&gt;both human readers and AI&lt;/strong&gt;. Documents contain things like design rationale and background ("why we built it this way") — useful for humans, but irrelevant for code generation.&lt;/p&gt;

&lt;p&gt;While experimenting with &lt;code&gt;copilot-instructions.md&lt;/code&gt;, the AI proposed: &lt;strong&gt;"Why not add markers to skip human-facing supplementary content? It would reduce token consumption."&lt;/strong&gt; I thought it was a great idea and adopted it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- AI_SKIP_START --&amp;gt;&lt;/span&gt;
(Design rationale, benefits, and other human-facing supplementary content goes here)
&lt;span class="c"&gt;&amp;lt;!-- AI_SKIP_END --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With an instruction in &lt;code&gt;copilot-instructions.md&lt;/code&gt; to "skip sections surrounded by these markers," the AI skips that content and focuses on the parts relevant to code generation.&lt;/p&gt;

&lt;p&gt;Actual document using this: &lt;a href="https://sugaiketadao.github.io/sicore/02-develop-standards/11-web-service-structure.md" rel="noopener noreferrer"&gt;https://sugaiketadao.github.io/sicore/02-develop-standards/11-web-service-structure.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another adopted proposal: create a &lt;strong&gt;AI-only reference&lt;/strong&gt; that extracts just method names and signatures from the full JavaDoc. Humans need detailed documentation with descriptions and examples, but AI only needs to know "which methods exist." A streamlined AI-only reference reduced token consumption while also preventing hallucinations of non-existent methods.&lt;/p&gt;

&lt;p&gt;Human JavaDoc: &lt;a href="https://sugaiketadao.github.io/sicore/11-api-references/11-javadoc" rel="noopener noreferrer"&gt;https://sugaiketadao.github.io/sicore/11-api-references/11-javadoc&lt;/a&gt;&lt;br&gt;&lt;br&gt;
AI JavaDoc: &lt;a href="https://sugaiketadao.github.io/sicore/31-ai-api-references/11-java-doc.md" rel="noopener noreferrer"&gt;https://sugaiketadao.github.io/sicore/31-ai-api-references/11-java-doc.md&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Eventually: Full Feature Generation in One Shot
&lt;/h2&gt;

&lt;p&gt;After all that trial and error, I finally reached the point where I could have AI generate &lt;strong&gt;an entire feature (UI + server logic) on a custom framework&lt;/strong&gt; from a single prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML (screen layout, input forms)&lt;/li&gt;
&lt;li&gt;JavaScript (UI event handling, validation, server communication)&lt;/li&gt;
&lt;li&gt;Java (web service, DB operations, business logic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What made it possible: maintaining &lt;code&gt;copilot-instructions.md&lt;/code&gt;, establishing the API reference loading workflow, and documenting coding patterns. &lt;strong&gt;Building the system to communicate correctly with AI is the real essence of AI-assisted development.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;After developing with AI, my takeaway is: &lt;strong&gt;AI is excellent but not omnipotent.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Praise feels nice, but don't take suggestions at face value&lt;/li&gt;
&lt;li&gt;Hallucinations are inevitable&lt;/li&gt;
&lt;li&gt;Preparation to communicate clearly (instructions, references, pattern docs) determines outcomes&lt;/li&gt;
&lt;li&gt;Treat AI as an equal development partner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're interested in the approach of "making a framework AI-development-ready," please check out this framework (&lt;strong&gt;SIcore&lt;/strong&gt;). It includes everything you need for AI-assisted development: prompt examples, AI references, and coding patterns — all included.&lt;/p&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/when-is-it-ok-to-build-a-system-with-ai-alone-a-framework-for-thinking-about-responsibility-39on"&gt;When Is It OK to Build a System with AI Alone? A Framework for Thinking About Responsibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla? — The Design Philosophy Behind SIcore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>githubcopilot</category>
      <category>java</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Single-File CSS Design - Why I Built a Java Framework (third attempt in 10 years) #08</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Sun, 22 Feb 2026 11:09:41 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3</link>
      <guid>https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In previous articles I covered the Java and JavaScript side of SIcore. This time, it's all about &lt;strong&gt;CSS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Have you ever spent hours fighting a CSS framework just to apply your own design? You add &lt;code&gt;!important&lt;/code&gt;, crank up selector specificity, and still lose — all because the framework's defaults are baked in too deep.&lt;/p&gt;

&lt;p&gt;In Java and JavaScript, "correct" is largely objective. In CSS, &lt;strong&gt;there is no single correct answer&lt;/strong&gt; — design varies from project to project. Adopting a large CSS framework means inheriting someone else's design decisions, and every time your project's look diverges from those defaults, you end up in an override war.&lt;/p&gt;

&lt;p&gt;SIcore takes the opposite approach: &lt;strong&gt;start small and build only what you need&lt;/strong&gt;. The entire styling for a business application is covered by a &lt;strong&gt;single CSS file of about 400 lines&lt;/strong&gt;. This article explains the philosophy and structure behind that design.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Article Covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why "start minimal" makes sense for business apps&lt;/li&gt;
&lt;li&gt;What one file covers&lt;/li&gt;
&lt;li&gt;12-column grid and 3-stage responsive behavior&lt;/li&gt;
&lt;li&gt;Unified form element styling&lt;/li&gt;
&lt;li&gt;CSS custom properties for project-wide customization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Start Minimal?
&lt;/h2&gt;

&lt;p&gt;The biggest cost of adopting a CSS framework isn't the file size — it's &lt;strong&gt;learning what is where&lt;/strong&gt;, combined with a cost that is unique to CSS: &lt;strong&gt;inheriting someone else's design decisions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Bootstrap's button border-radius is &lt;code&gt;4px&lt;/code&gt; or &lt;code&gt;6px&lt;/code&gt; — irrelevant to your project, yet it exists as a "default" that you must override. Tailwind CSS avoids the override problem with utility classes, but changing colors or sizes requires editing &lt;code&gt;tailwind.config.js&lt;/code&gt; and running a build. One framework forces overrides; the other forces builds. Either way, you're adapting to the framework's rules rather than your own.&lt;/p&gt;

&lt;p&gt;Starting minimal avoids this entirely:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;① No upfront learning curve&lt;/strong&gt; — You understand everything as you use it, gradually building a full picture.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;② All design decisions are yours&lt;/strong&gt; — No "override something I never asked for" situations.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;③ AI-friendly&lt;/strong&gt; — The entire file can be read in one pass before generating HTML, eliminating the risk of hallucinated class names.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;④ No build tools, no dependencies&lt;/strong&gt; — No Sass, PostCSS, or Tailwind pipeline. Just an editor and browser DevTools.&lt;/p&gt;
&lt;h2&gt;
  
  
  What One File Covers
&lt;/h2&gt;

&lt;p&gt;"Starting minimal" doesn't mean leaving things out. &lt;code&gt;onepg-base.css&lt;/code&gt; covers everything a business application screen needs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSS Reset&lt;/td&gt;
&lt;td&gt;Unified &lt;code&gt;box-sizing&lt;/code&gt;, font inheritance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page Layout&lt;/td&gt;
&lt;td&gt;Fixed header/footer, fluid main content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grid Layout&lt;/td&gt;
&lt;td&gt;12-column system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Responsive&lt;/td&gt;
&lt;td&gt;3 breakpoints: PC, tablet, smartphone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Elements&lt;/td&gt;
&lt;td&gt;Text, select, checkbox, radio, button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table&lt;/td&gt;
&lt;td&gt;Borders, striped rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;Text alignment, alert colors, link-style buttons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Item Highlight&lt;/td&gt;
&lt;td&gt;Warning/error background colors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bootstrap is roughly 10,000 lines. Tailwind's output varies but reaches thousands of lines for a typical project. When you keep only what a business app actually uses, &lt;strong&gt;400 lines is enough&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  12-Column Grid and Responsive Behavior
&lt;/h2&gt;

&lt;p&gt;Place a label in &lt;code&gt;item-head&lt;/code&gt; and an input element in &lt;code&gt;item-body&lt;/code&gt;. On PC, they render side by side. Arrange &lt;code&gt;grid-col-N&lt;/code&gt; divs so the N values sum to 12 — the same 12-column layout familiar from Bootstrap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-col-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-head"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;label&amp;gt;&lt;/span&gt;Full Name&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-col-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-head"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;label&amp;gt;&lt;/span&gt;Country&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"country_cs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-col-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-head"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;label&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two media queries produce three layout stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PC (side by side):&lt;/strong&gt; Label and input render horizontally. The example above shows a 3-column layout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|     grid-col-3    |     grid-col-3    |              grid-col-6                 |
| Full Name [_____] | Country [______▼] | Email [_______________________________] |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tablet (stacked within columns):&lt;/strong&gt; Column widths are preserved, but the label stacks above the input inside each column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Full Name  | Country    | Email                           |
| [_________]| [_______▼] | [_____________________________] |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Smartphone (full-width stack):&lt;/strong&gt; All columns expand to full width, resulting in a single-column vertical layout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Full Name
[_________]
Country
[_______▼]
Email
[_____________________________]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unified Form Element Styling
&lt;/h2&gt;

&lt;p&gt;Business apps display large numbers of text boxes, select boxes, and buttons side by side. SIcore unifies their &lt;strong&gt;height, border, border-radius, padding, and focus style&lt;/strong&gt; across all form elements so the screen looks consistent without per-element overrides. Disabled and focused states are also covered by default — no extra work needed.&lt;/p&gt;

&lt;p&gt;Validation error highlights are applied automatically by the JavaScript framework, using dedicated CSS classes. See the CSS class reference for details on each class.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Custom Properties for Project-Wide Customization
&lt;/h2&gt;

&lt;p&gt;All style values (colors, sizes, spacing) are consolidated in CSS custom properties (&lt;code&gt;:root&lt;/code&gt;). Changing the color scheme for a new project is a single block of variable overrides — no hunting through scattered selectors.&lt;/p&gt;

&lt;p&gt;Variable names follow the convention &lt;code&gt;--onepg-base--category--purpose&lt;/code&gt;, so the intent is clear from the name alone. See the CSS class reference for the full list.&lt;/p&gt;

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

&lt;p&gt;When you enumerate only the styles a business app actually needs, 400 lines turns out to be surprisingly sufficient.&lt;/p&gt;

&lt;p&gt;CSS is where design gets personal, far more so than Java or JavaScript. That's exactly why &lt;strong&gt;starting minimal matters most in CSS&lt;/strong&gt; — you keep every design decision in your own hands. Start small, grow only what you need. This approach is especially effective in SI projects and AI-assisted development.&lt;/p&gt;

&lt;p&gt;A single 400-line file, simple class names, zero build steps. Read it once and you'll likely think: "I can customize this."&lt;/p&gt;

&lt;p&gt;The files are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS file (including grid media queries): &lt;a href="https://github.com/sugaiketadao/sicore/blob/main/pages/lib/css/onepg-base.css" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore/blob/main/pages/lib/css/onepg-base.css&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CSS class reference: &lt;a href="https://sugaiketadao.github.io/sicore/11-api-references/02-cssdoc/" rel="noopener noreferrer"&gt;https://sugaiketadao.github.io/sicore/11-api-references/02-cssdoc/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;02 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL-to-Class Mapping&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;03 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-Only Communication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;04 &lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Mockup = Implementation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;05 &lt;a href="https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b"&gt;Dynamic List Rendering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;06 &lt;a href="https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo"&gt;Custom HTML Attributes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;07 &lt;a href="https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i"&gt;Map-Based Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;08 Single-File CSS Design (this article)&lt;/li&gt;
&lt;li&gt;09 &lt;a href="https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3"&gt;Client-Side Data Management and JWT Auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;10  &lt;a href="https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54"&gt;SQL Directly in Java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;11 &lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>css</category>
      <category>framework</category>
      <category>responsive</category>
      <category>gridlayout</category>
    </item>
    <item>
      <title>Map-Based Design - Why I Built a Java Framework (third attempt in 10 years) #07</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Thu, 12 Feb 2026 12:29:47 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i</link>
      <guid>https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This time, I'll cover &lt;strong&gt;"Map-Based Design,"&lt;/strong&gt; which is at the core of SIcore's Java code architecture.&lt;/p&gt;

&lt;p&gt;As introduced in previous articles, SIcore uses "JSON-only communication." In the flow Browser → JSON → Java → JSON → Browser, we use the &lt;strong&gt;Map-based Io class&lt;/strong&gt; as the Java-side data container. Since JSON is an associative array (key-value pairs), receiving it as a Map on the Java side is the most natural approach.&lt;/p&gt;

&lt;p&gt;In SIcore, this Io class handles all requests, responses, and database operations, without using Entity (Bean) classes. This commitment enables reduced code volume, unified field naming, and strong compatibility with AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Article Covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What Map-based design is&lt;/li&gt;
&lt;li&gt;Why we use Map for everything&lt;/li&gt;
&lt;li&gt;Unified naming strategy&lt;/li&gt;
&lt;li&gt;Type-safe and null-safe data retrieval&lt;/li&gt;
&lt;li&gt;Bug prevention features&lt;/li&gt;
&lt;li&gt;Deep copy safety&lt;/li&gt;
&lt;li&gt;Benefits / Drawbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Map-Based Design?
&lt;/h2&gt;

&lt;p&gt;On SIcore's server side (Java), all requests, responses, and database operations are handled with the &lt;strong&gt;Io class&lt;/strong&gt; (a class that extends Map).&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="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doExecute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Io&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Retrieve values from request (key-based access)&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;incomeAm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Set database extraction result as response&lt;/span&gt;
  &lt;span class="nc"&gt;IoItems&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// The io object becomes the response as-is&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No Entity (Bean) classes are created&lt;/strong&gt;. There's no need to prepare classes like &lt;code&gt;UserEntity&lt;/code&gt; or &lt;code&gt;OrderDto&lt;/code&gt; for each table or screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Use Map for Everything
&lt;/h2&gt;

&lt;p&gt;In SIcore, the entire process from browser to database uses Map (Io class).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Browser]  JSON  →  [Java] Io(Map)  →  [DB] SQL
                  ←  [Java] Io(Map)  ←
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Web Page Data is "Text"
&lt;/h3&gt;

&lt;p&gt;Input values on web pages are &lt;strong&gt;all strings&lt;/strong&gt; (&lt;code&gt;String&lt;/code&gt;), even if they appear to be numbers or dates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Appears as numbers or dates on the browser, but... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1200000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"20250101"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These remain strings throughout the journey JavaScript → JSON → Java. Type conversion is only needed at &lt;strong&gt;the moment you use them in business logic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Also, numeric and date fields can have empty (blank) values. Storing as strings preserves empty values as-is, avoiding conversion errors that occur when Beans receive them as &lt;code&gt;int&lt;/code&gt; or &lt;code&gt;LocalDate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Io class stores everything internally as strings (&lt;code&gt;String&lt;/code&gt;) and retrieves them with type conversion as needed.&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;// Retrieve as string&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;incomeAmStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "1200000"&lt;/span&gt;

&lt;span class="c1"&gt;// Retrieve with type conversion when needed&lt;/span&gt;
&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;incomeAm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1200000&lt;/span&gt;
&lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;birthDt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDateNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 2025-01-01&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Increases When You Create Beans
&lt;/h3&gt;

&lt;p&gt;Imagine a business system with 50 tables and 30 screens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity classes per table × 50&lt;/li&gt;
&lt;li&gt;Form / DTO classes per screen × 30 (or more)&lt;/li&gt;
&lt;li&gt;Entity ⇔ DTO conversion logic&lt;/li&gt;
&lt;li&gt;Camel case conversion (&lt;code&gt;user_id&lt;/code&gt; → &lt;code&gt;userId&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Reflective getter / setter invocations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use Map for everything, &lt;strong&gt;all of these become unnecessary&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Same Approach as JavaScript
&lt;/h3&gt;

&lt;p&gt;JSON, JavaScript associative arrays, and Java Maps all share the same structure: "access by key."&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="c1"&gt;// JavaScript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Java (Io class)&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Data handling is unified from front to back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unified Naming Strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Database physical field name = HTML name attribute = Java Map key&lt;/strong&gt; are unified.&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="c1"&gt;-- Database&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;t_user&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;user_nm&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;income_am&lt;/span&gt; &lt;span class="nb"&gt;NUMERIC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;birth_dt&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- HTML --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Java&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userNm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Direct SQL Usage
&lt;/h3&gt;

&lt;p&gt;Since field names are unified, Map data can be used directly in SQL.&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="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doExecute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Io&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// io contents (request JSON as-is)&lt;/span&gt;
  &lt;span class="c1"&gt;// {&lt;/span&gt;
  &lt;span class="c1"&gt;//   "user_id"   : "U001",&lt;/span&gt;
  &lt;span class="c1"&gt;//   "user_nm"   : "Mike Davis",&lt;/span&gt;
  &lt;span class="c1"&gt;//   "income_am" : "1200000",&lt;/span&gt;
  &lt;span class="c1"&gt;//   "birth_dt"  : "20250101"&lt;/span&gt;
  &lt;span class="c1"&gt;// }&lt;/span&gt;

  &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"t_user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ↑ Key names = DB field names, so it becomes an INSERT statement directly&lt;/span&gt;

  &lt;span class="c1"&gt;// SQL executed:&lt;/span&gt;
  &lt;span class="c1"&gt;// INSERT INTO t_user (user_id, user_nm, income_am, birth_dt)&lt;/span&gt;
  &lt;span class="c1"&gt;//   VALUES ('U001', 'Mike Davis', 1200000, 2025-01-01)&lt;/span&gt;
  &lt;span class="c1"&gt;// ※SqlUtil automatically determines column types from DB metadata and binds with appropriate types&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Database retrieval to response display works the same way. SELECT results can be used directly for screen display.&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="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doExecute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Io&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Retrieve extraction key from request&lt;/span&gt;
  &lt;span class="nc"&gt;SqlBuilder&lt;/span&gt; &lt;span class="n"&gt;sb&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;SqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT * FROM t_user WHERE user_id = "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// "user_id": "U001"&lt;/span&gt;
  &lt;span class="c1"&gt;// Database extraction (result is IoItems = Map)&lt;/span&gt;
  &lt;span class="nc"&gt;IoItems&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// row contents:&lt;/span&gt;
  &lt;span class="c1"&gt;// { "user_id": "U001", "user_nm": "Mike Davis", "income_am": "1200000", "birth_dt": "20250101" }&lt;/span&gt;

  &lt;span class="c1"&gt;// Set to response → returns to browser as JSON as-is&lt;/span&gt;
  &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ↑ Key names = HTML name attributes, so they're automatically set to each screen field&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits of Unified Naming&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No conversion code needed&lt;/strong&gt;: No need for camel case conversion (&lt;code&gt;user_id&lt;/code&gt; → &lt;code&gt;userId&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced code volume&lt;/strong&gt;: No need to write mapping logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced bugs&lt;/strong&gt;: No conversion mistakes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved maintainability&lt;/strong&gt;: Database design documents function as specifications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Type-Safe and Null-Safe Data Retrieval
&lt;/h2&gt;

&lt;p&gt;You might be concerned: "Isn't it not type-safe without Beans?"&lt;/p&gt;

&lt;p&gt;The Io class addresses this by providing type-safe and null-safe get methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Null Safety
&lt;/h3&gt;

&lt;p&gt;In regular Maps, &lt;code&gt;get()&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt;, which causes NullPointerException.&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;// Regular Map&lt;/span&gt;
&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&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;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&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="s"&gt;"key"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// null → causes NullPointerException&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Io class, basic methods do not return null. To retrieve null, explicitly use &lt;code&gt;Nullable&lt;/code&gt; methods.&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;// Io class&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// "" (blank instead of null)&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStringNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// null (explicitly retrieve null)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Type Safety
&lt;/h3&gt;

&lt;p&gt;Type conversion methods are provided, and when type conversion errors occur, they log the key and value.&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="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"age"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;                       &lt;span class="c1"&gt;// Blank converts to zero&lt;/span&gt;
&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;income&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Preserves precision&lt;/span&gt;
&lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;birthDt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDateNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Date format check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even when type conversion errors occur, the source is always consolidated in the Io class's get methods. Since the error log outputs the key and value, when giving correction instructions to AI, "which key's value is invalid" is clear, making validation and other countermeasures easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Prevention Features
&lt;/h2&gt;

&lt;p&gt;The Io class has features that prevent bugs commonly found in regular Maps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strict Key Duplication Check
&lt;/h3&gt;

&lt;p&gt;Detects unintended value overwrites.&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;// Regular Map&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U001"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U002"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Overwritten (no warning)&lt;/span&gt;

&lt;span class="c1"&gt;// Io class&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U001"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U002"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// Error (logs the key)&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putForce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U002"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Use intentionally when overwriting&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Non-Existent Key Retrieval Error
&lt;/h3&gt;

&lt;p&gt;Detects typo mistakes.&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;// Regular Map&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U001"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&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="s"&gt;"userid"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// null (doesn't notice typo)&lt;/span&gt;

&lt;span class="c1"&gt;// Io class&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"U001"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userid"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Error (logs non-existent key)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These checks provide &lt;strong&gt;"declared variable"-like safety&lt;/strong&gt; while being a Map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Copy Safety
&lt;/h2&gt;

&lt;p&gt;The Io class performs deep copies when storing and retrieving lists and nested maps.&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;// Deep copy on storage&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;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;srcList&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"items"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;srcList&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;srcList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Modify original list&lt;/span&gt;
&lt;span class="c1"&gt;// io.getList("items") remains ["A", "B"] (no effect)&lt;/span&gt;

&lt;span class="c1"&gt;// Deep copy on retrieval&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;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gotList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"items"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;gotList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Modify retrieved list&lt;/span&gt;
&lt;span class="c1"&gt;// io.getList("items") remains ["A", "B"] (no effect)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents unexpected side effects from reference sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Entity / DTO / Form classes needed&lt;/strong&gt;: Code volume is significantly reduced&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No conversion code with unified naming&lt;/strong&gt;: HTML → Java → SQL connects seamlessly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same approach as JavaScript&lt;/strong&gt;: Unified key-based access from front to back&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in bug prevention&lt;/strong&gt;: Null-safe, type-safe, key duplication check, existence check&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe with deep copy&lt;/strong&gt;: Prevents side effects from reference sharing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy for AI to generate code&lt;/strong&gt;: Patterns are simple and consistent&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Drawbacks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IDE code completion doesn't work&lt;/strong&gt;: Beans offer field name completion, but Map key strings don't get completed

&lt;ul&gt;
&lt;li&gt;However, bug prevention features (error on non-existent key) cover this&lt;/li&gt;
&lt;li&gt;Also, AI generates code including key names, which covers this&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;No compile-time type checking&lt;/strong&gt;: Beans guarantee field types at compile time, but Maps check at runtime

&lt;ul&gt;
&lt;li&gt;However, Io class's type conversion methods safely convert at runtime&lt;/li&gt;
&lt;li&gt;Supplement with validation logic beforehand&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;Some may feel uneasy hearing "no Beans."&lt;/p&gt;

&lt;p&gt;However, by &lt;strong&gt;eliminating the Entity / DTO / Form Bean class layer&lt;/strong&gt;, code volume decreases, bugs from field name mismatches disappear, and work volume when adding new screens or tables is significantly reduced.&lt;/p&gt;

&lt;p&gt;Combined with the Io class's bug prevention features (null-safe, type-safe, key duplication check, existence check), I believe the weaknesses of Map are practically covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles too!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;02 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL-to-Class Mapping&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;03 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-Only Communication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;04 &lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Mockup = Implementation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;05 &lt;a href="https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b"&gt;Dynamic List Rendering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;06 &lt;a href="https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo"&gt;Custom HTML Attributes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;07 Map-Based Design (this article)&lt;/li&gt;
&lt;li&gt;08 &lt;a href="https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3"&gt;Single-File CSS Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;09 &lt;a href="https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3"&gt;Client-Side Data Management and JWT Auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;10  &lt;a href="https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54"&gt;SQL Directly in Java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;11 &lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>java</category>
      <category>framework</category>
      <category>json</category>
      <category>map</category>
    </item>
    <item>
      <title>Custom HTML Attributes - Why I Built a Java Framework (third attempt in 10 years) #06</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Tue, 10 Feb 2026 01:42:50 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo</link>
      <guid>https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This time, I'll cover &lt;strong&gt;"Custom HTML Attributes"&lt;/strong&gt; in SIcore.&lt;/p&gt;

&lt;p&gt;The framework's custom &lt;code&gt;data-*&lt;/code&gt; attributes enable concise implementation of common patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic HTML ⇔ JSON Mechanism
&lt;/h2&gt;

&lt;p&gt;In SIcore, the &lt;code&gt;name&lt;/code&gt; attribute (or &lt;code&gt;data-name&lt;/code&gt; attribute) of HTML elements serves as the key for exchanging data with the server in JSON format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;【Browser → Server】
Execute PageUtil.getValues()
    ↓
Retrieve values from HTML elements with name attributes to generate request JSON
    ↓
Send request JSON to server

【Server → Browser】
Receive response JSON from server
    ↓
Execute PageUtil.setValues(res)
    ↓
Set response JSON values to HTML elements with name/data-name attributes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During this process, custom HTML attributes (like &lt;code&gt;data-check-off-value&lt;/code&gt; and &lt;code&gt;data-value-format-type&lt;/code&gt;) automatically perform value conversion and formatting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Article Covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Three main custom HTML attributes

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data-name&lt;/code&gt; attribute (display-only elements)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data-check-off-value&lt;/code&gt; attribute (checkbox OFF values)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data-value-format-type&lt;/code&gt; attribute (automatic formatting)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Benefits of each&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  data-name Attribute: Display-Only Elements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;Used when you want to display values in elements other than form input elements (&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Input element (name attribute) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Display-only element (data-name attribute) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"user_nm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"list.pet_nm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript Behavior
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// On get: retrieve only name attributes (data-name not retrieved)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// On set: set values to both name and data-name&lt;/span&gt;
&lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Values can be set in non-input elements like &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Can use the same naming convention as &lt;code&gt;name&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;Clearly separates display-only fields from input fields&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  data-check-off-value Attribute: Checkbox OFF Values
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;Defines the value to include in the request when a checkbox is OFF.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- When checked: "1", when unchecked: "0" --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"is_dog"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;data-check-off-value=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- When checked: "true", when unchecked: "false" --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"is_cat"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;data-check-off-value=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Request JSON
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// When checked&lt;/span&gt;
&lt;span class="c1"&gt;// { "is_dog": "1", "is_cat": "true" }&lt;/span&gt;

&lt;span class="c1"&gt;// When unchecked (OFF value is automatically included)&lt;/span&gt;
&lt;span class="c1"&gt;// { "is_dog": "0", "is_cat": "false" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Normal &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; elements don't send values when unchecked, but this attribute includes the value&lt;/li&gt;
&lt;li&gt;Explicitly define the OFF value for checkboxes&lt;/li&gt;
&lt;li&gt;No need for server-side processing to supplement OFF values&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Note
&lt;/h3&gt;

&lt;p&gt;Do not use for non-mandatory database search conditions (since the common specification is to exclude conditions when there's no value).&lt;/p&gt;

&lt;h2&gt;
  
  
  data-value-format-type Attribute: Automatic Formatting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;Automatically formats values for display and automatically unformats (returns to original format) when sending requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Values
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Format Type&lt;/th&gt;
&lt;th&gt;Input Value (Example)&lt;/th&gt;
&lt;th&gt;Display Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;num&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number (comma-separated)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1000000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1,000,000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ymd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Date (YYYY/MM/DD)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20251231&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2025/12/31&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time (HH:MI:SS)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;123456&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;12:34:56&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Number (comma-separated) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt; &lt;span class="na"&gt;data-value-format-type=&lt;/span&gt;&lt;span class="s"&gt;"num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Date (YYYY/MM/DD format) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt; &lt;span class="na"&gt;data-value-format-type=&lt;/span&gt;&lt;span class="s"&gt;"ymd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Time (HH:MI:SS format) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"start_tm"&lt;/span&gt; &lt;span class="na"&gt;data-value-format-type=&lt;/span&gt;&lt;span class="s"&gt;"hms"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript Behavior
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Display response in browser: automatic formatting&lt;/span&gt;
&lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;income_am&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1200000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// → Display: "1,200,000"&lt;/span&gt;
  &lt;span class="na"&gt;birth_dt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;19870321&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// → Display: "1987/03/21"&lt;/span&gt;
  &lt;span class="na"&gt;start_tm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;093000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;    &lt;span class="c1"&gt;// → Display: "09:30:00"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Generate request from browser input: automatic unformatting&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   income_am: "1200000",  // Commas removed&lt;/span&gt;
&lt;span class="c1"&gt;//   birth_dt:  "19870321", // Slashes removed&lt;/span&gt;
&lt;span class="c1"&gt;//   start_tm:  "093000"    // Colons removed&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Java-Side Processing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// No unformatting needed on Java side (already unformatted values arrive)&lt;/span&gt;
&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;income&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"income_am"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// 1200000&lt;/span&gt;
&lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;birthDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDateNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"birth_dt"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 1987-03-21&lt;/span&gt;

&lt;span class="c1"&gt;// Can be registered directly to DB&lt;/span&gt;
&lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"t_user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automatic conversion between display format and database storage values&lt;/li&gt;
&lt;li&gt;No need to write formatting logic in Java or JavaScript&lt;/li&gt;
&lt;li&gt;Separates appearance from data&lt;/li&gt;
&lt;li&gt;Server API can return database format values directly without conversion to display format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other Custom Attributes
&lt;/h2&gt;

&lt;p&gt;The following attributes are also defined for internal framework use:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Custom Attribute&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data-style-display-backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;display&lt;/code&gt; style value before hiding&lt;/td&gt;
&lt;td&gt;Used when re-displaying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data-style-visibility-backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;visibility&lt;/code&gt; style value before hiding&lt;/td&gt;
&lt;td&gt;Used when re-displaying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data-title-backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;title&lt;/code&gt; attribute value before error message&lt;/td&gt;
&lt;td&gt;Used when clearing error display&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data-obj-row-idx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Array data index value&lt;/td&gt;
&lt;td&gt;Mainly used in &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;, set on request and used for error display on response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data-radio-obj-name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Original &lt;code&gt;name&lt;/code&gt; attribute value of radio button&lt;/td&gt;
&lt;td&gt;Mainly used in &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; for per-row grouping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are mainly used automatically within the framework, so you don't need to be aware of them in normal implementation.&lt;/p&gt;

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

&lt;p&gt;SIcore's custom HTML attributes enable concise implementation of common patterns.&lt;/p&gt;

&lt;p&gt;In particular, the &lt;code&gt;data-value-format-type&lt;/code&gt; attribute improves development efficiency by eliminating the need to write formatting logic in both Java and JavaScript.&lt;/p&gt;

&lt;p&gt;Next time, I plan to introduce Map-type design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;02 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL-to-Class Mapping&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;03 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-Only Communication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;04 &lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Mockup = Implementation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;05 &lt;a href="https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b"&gt;Dynamic List Rendering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;06 Custom HTML Attributes (this article)&lt;/li&gt;
&lt;li&gt;07 &lt;a href="https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i"&gt;Map-Based Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;08 &lt;a href="https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3"&gt;Single-File CSS Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;09 &lt;a href="https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3"&gt;Client-Side Data Management and JWT Auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;10  &lt;a href="https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54"&gt;SQL Directly in Java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;11 &lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
I'd appreciate it if you could give it a ❤️!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>framework</category>
      <category>html</category>
      <category>ajax</category>
    </item>
    <item>
      <title>Dynamic List Rendering - Why I Built a Java Framework (third attempt in 10 years) #05</title>
      <dc:creator>sugaiketadao</dc:creator>
      <pubDate>Sat, 07 Feb 2026 13:38:17 +0000</pubDate>
      <link>https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b</link>
      <guid>https://dev.to/sugaiketadao/dynamic-list-rendering-i-built-a-lightweight-java-framework-for-japans-si-projects-third-5d8b</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the previous article, I covered SIcore's "Mockup = Implementation" concept — how static HTML mockups become implementation code.&lt;/p&gt;

&lt;p&gt;This time, I'll explain &lt;strong&gt;"Dynamic List Rendering"&lt;/strong&gt; — how SIcore automatically expands array data from the server into table rows or list elements using templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Article Covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What dynamic list rendering is&lt;/li&gt;
&lt;li&gt;Automatic row generation using templates&lt;/li&gt;
&lt;li&gt;Implementation steps and code examples&lt;/li&gt;
&lt;li&gt;Benefits&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Dynamic List Rendering?
&lt;/h2&gt;

&lt;p&gt;In SIcore, array data &lt;code&gt;[{}, {}]&lt;/code&gt; returned from the server is automatically expanded into row elements using HTML templates.&lt;/p&gt;

&lt;p&gt;Traditional JSP requires loop constructs like &lt;code&gt;&amp;lt;c:forEach&amp;gt;&lt;/code&gt;, but SIcore only requires &lt;strong&gt;defining a template row&lt;/strong&gt; — the array data expands automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Steps and Code Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Define the Template Row
&lt;/h3&gt;

&lt;p&gt;Define a template row inside the &lt;code&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt; tag using &lt;code&gt;&amp;lt;script type="text/html"&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static HTML (template row only)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list.pet_nm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list.weight_kg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;id="list"&lt;/code&gt; to the &lt;code&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;Wrap the template row in &lt;code&gt;&amp;lt;script type="text/html"&amp;gt;&lt;/code&gt; (planned migration to &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use the naming pattern &lt;code&gt;list.field_name&lt;/code&gt; for the &lt;code&gt;name&lt;/code&gt; attributes (where &lt;code&gt;list&lt;/code&gt; matches the JSON key)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Fetch Array Data from Server and Display
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;:&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="c1"&gt;// Generate request JSON from browser input&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&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;res&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;HttpUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callJsonService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/services/exmodule/ExampleListSearch&lt;/span&gt;&lt;span class="dl"&gt;'&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="c1"&gt;// Response JSON&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   "list": [&lt;/span&gt;
&lt;span class="c1"&gt;//     {"pet_nm": "Pochi", "weight_kg": "5.0"},&lt;/span&gt;
&lt;span class="c1"&gt;//     {"pet_nm": "Tama", "weight_kg": "2.5"}&lt;/span&gt;
&lt;span class="c1"&gt;//   ]&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="c1"&gt;// Auto-generate rows from response JSON&lt;/span&gt;
&lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result: Rows Added to HTML
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Result HTML (template row + generated rows)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list.pet_nm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list.weight_kg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"list.pet_nm"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Pochi"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"list.weight_kg"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"5.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"list.pet_nm"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Tama"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"list.weight_kg"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"2.5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PageUtil.setValues(res)&lt;/code&gt; automatically expands the array data into table rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explicit Row Addition
&lt;/h2&gt;

&lt;p&gt;You can also explicitly add rows via JavaScript without receiving array data:&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="c1"&gt;// Add a single row&lt;/span&gt;
&lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&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;pet_nm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pochi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight_kg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add multiple rows&lt;/span&gt;
&lt;span class="nx"&gt;PageUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&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;pet_nm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pochi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight_kg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5.0&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;pet_nm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tama&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight_kg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.5&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;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. No Loop Construct Needed
&lt;/h3&gt;

&lt;p&gt;You don't need constructs like JSP's &lt;code&gt;&amp;lt;c:forEach&amp;gt;&lt;/code&gt; or Thymeleaf's &lt;code&gt;th:each&lt;/code&gt;. Just define a template row, and array data expands automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Easy Transition from HTML Mockups
&lt;/h3&gt;

&lt;p&gt;Simply wrap table rows created during the mockup phase in &lt;code&gt;&amp;lt;script type="text/html"&amp;gt;&lt;/code&gt; to turn them into implementation code.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Server Just Returns JSON
&lt;/h3&gt;

&lt;p&gt;The server (Java) doesn't need to worry about loop processing — just return array data as JSON.&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;// Java code&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doExecute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Io&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Database query&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;IoRows&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqlUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectBulk&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getDbConn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;SQL_SEL_PET&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Set result rows&lt;/span&gt;
  &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putRows&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rows&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SIcore's dynamic list rendering feature makes displaying array data simple and concise.&lt;/p&gt;

&lt;p&gt;Next time, I'll introduce SIcore's custom HTML attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;p&gt;Check out the other articles in this series!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-framework-for-japans-si-projects-third-attempt-in-10-years-1e6e"&gt;Why I Built a Java Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;02 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-002--175j"&gt;Direct URL-to-Class Mapping&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;03 &lt;a href="https://dev.to/sugaiketadao/i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-10-years-003--5fg"&gt;JSON-Only Communication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;04 &lt;a href="https://dev.to/sugaiketadao/static-html-implementation-i-built-a-lightweight-java-framework-for-japans-si-projects-third-3cdm"&gt;Static Mockup = Implementation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;05 Dynamic List Rendering (this article)&lt;/li&gt;
&lt;li&gt;06 &lt;a href="https://dev.to/sugaiketadao/custom-html-attributes-i-built-a-lightweight-java-framework-for-japans-si-projects-third-30lo"&gt;Custom HTML Attributes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;07 &lt;a href="https://dev.to/sugaiketadao/map-based-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-515i"&gt;Map-Based Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;08 &lt;a href="https://dev.to/sugaiketadao/single-file-css-design-i-built-a-lightweight-java-framework-for-japans-si-projects-third-kk3"&gt;Single-File CSS Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;09 &lt;a href="https://dev.to/sugaiketadao/client-side-data-management-i-built-a-lightweight-java-framework-for-japans-si-projects-third-21e3"&gt;Client-Side Data Management and JWT Auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;10  &lt;a href="https://dev.to/sugaiketadao/sql-directly-in-java-i-built-a-lightweight-java-framework-for-japans-si-projects-third-2k54"&gt;SQL Directly in Java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;11 &lt;a href="https://dev.to/sugaiketadao/why-go-vanilla-i-built-a-lightweight-java-framework-for-japans-si-projects-third-attempt-in-1bo5"&gt;Why Go Vanilla?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SIcore Framework Links
&lt;/h2&gt;

&lt;p&gt;All implementation code and documentation are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://onepg.com" rel="noopener noreferrer"&gt;https://onepg.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sugaiketadao/sicore" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to verify sample screens (VS Code): &lt;a href="https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#%EF%B8%8F-how-to-verify-sample-screens---vs-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting started with AI development: &lt;a href="https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development" rel="noopener noreferrer"&gt;https://github.com/sugaiketadao/sicore#-getting-started-with-ai-development&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading!&lt;br&gt;
💖 If you found this helpful, I'd appreciate a like!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>framework</category>
      <category>html</category>
      <category>ajax</category>
    </item>
  </channel>
</rss>
