Level 1: Core Fundamentals

Sub-topic 1.4: Data Pipeline, Variables & Two-Pass Evaluation Time Engine

สถาปัตยกรรม Two-Pass Fill Stage, Lookahead Buffering, วงจรชีวิตของ Variables และ Type-Safe Data Pipeline บน Java 17/21

Step 1: Deep Dive (Two-Pass Evaluation Mechanics & Lookahead Buffers)

ในสเตจ Fill Stage เครื่องยนต์ JRVerticalFiller ทำงานประมวลผลข้อมูลในลักษณะ Streaming Single-Pass ผ่าน JRDataSource แต่เมื่อพบองค์ประกอบที่ต้องการผลลัพธ์ล่วงหน้า (เช่น ยอดรวมสุทธิบน Title Band หรือเลขหน้า "Page X of Y") เครื่องยนต์จะเปิดใช้งานกลไก Two-Pass Delayed Evaluation โดยหน่วงเวลาการคำนวณและกักเก็บ Reference ลงใน Lookahead Fill Buffer จนกระทั่งถึงจุดสิ้นสุดของ Scope ที่กำหนด

Two-Pass Evaluation Timing & Lookahead Buffer Diagram
รูปที่ 1: แผนผังการทำงานของ Lookahead Buffer และ Two-Pass Evaluation Engine ใน Fill Stage 7.0.x

เมทริกซ์การทำงานของ evaluationTime:

ค่า evaluationTimeจังหวะเวลาการประเมินผลค่า (Timing)
Now (Default) ประเมินผลทันที ณ เสี้ยววินาทีที่ Band นั้นถูกวาดลงบน Canvas
Report หน่วงเวลาประเมินผลจนกว่า DataSource จะวิ่งจบ Record สุดท้ายของเอกสาร
Page หน่วงเวลาประเมินผลจนกระทั่งจบหน้าปัจจุบัน (ก่อนวาด Page Footer)
Column หน่วงเวลาประเมินผลจนกว่าจะจบคอลัมน์ปัจจุบันในรายงานแบบ Multi-Column
Group หน่วงเวลาประเมินผลจนกระทั่ง Group สิ้นสุดลง (พร้อม Group Footer)
Band ประเมินผลหลังจากทุก Element ภายใน Band นั้นยืดขยายตัวเสร็จสิ้น
Auto ประเมินผลแบบผสมอัตโนมัติ (เช่น Text รวม Field กับ Variable ที่มีจังหวะต่างกัน)

การควบคุม Variable Calculation & Reset Scope:

  • calculation="Sum": สะสมผลบวกอัตโนมัติ ควบคู่กับ initialValueExpression เพื่อป้องกันค่าเริ่มต้นเป็น null
  • resetType="Report": สะสมต่อเนื่องตลอดเอกสาร (ไม่ Reset ค่ากลับ)
  • resetType="None": รับค่า Expression ล่าสุดของแต่ละ Record โดยตรงโดยไม่ผ่านฟังก์ชันสะสม
จุดตายบน Production (Delayed Evaluation Memory Pressure & Null Pointer Disaster):
1. Lookahead Buffer Bloat: หากคุณใช้งาน evaluationTime="Report" กับ Elements จำนวนมหาศาลบนรายงานระดับแสนแถว JVM จะต้องตรึง Object References ไว้ใน Heap Memory โดยไม่ยอมให้ Garbage Collector เก็บกวาด จนกว่าเอกสารจะ Fill เสร็จสมบูรณ์ ส่งผลให้ Heap บวมและเกิด OutOfMemoryError ได้ง่ายขึ้น
2. ClassCastException: หากประเภทข้อมูลของ <field class="..."> ใน JRXML ไม่ตรงกับ Java Class จริงที่ DataSource ฉีดเข้ามา JVM 17/21 จะ Crash ทันทีระหว่าง Runtime

Step 2: Real-world 7.0.x Architecture Example

Two-Pass Evaluation Rendered Preview
รูปที่ 2: Visual Materialization แสดงผลการแสดงยอดรวมสุทธิบน Title Band และการคำนวณคณิตศาสตร์แบบ Type-Safe
<!-- JRXML 7.0.x Schema: Two-Pass & Type-Safe Pipeline -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
              name="TwoPassInvoice" pageWidth="595" pageHeight="842"
              columnWidth="555" leftMargin="20" rightMargin="20"
              topMargin="20" bottomMargin="20"
              whenNoDataType="AllSectionsNoDetail">

    <!-- Ingestion Pipeline: ประกาศ Explicit Java Types -->
    <field name="item_name" class="java.lang.String"/>
    <field name="unit_price" class="java.math.BigDecimal"/>
    <field name="quantity" class="java.lang.Integer"/>
    <field name="discount_rate" class="java.lang.Double"/>

    <!-- Variable: สะสมยอดรวมทั้งฉบับ -->
    <variable name="TOTAL_NET" calculation="Sum" resetType="Report"
              class="java.math.BigDecimal">
        <expression><![CDATA[
            $F{unit_price}.multiply(new java.math.BigDecimal($F{quantity}))
            .multiply(java.math.BigDecimal.valueOf(1.0 - ($F{discount_rate} != null ? $F{discount_rate}.doubleValue() : 0.0)))
        ]]></expression>
        <initialValueExpression><![CDATA[java.math.BigDecimal.ZERO]]></initialValueExpression>
    </variable>

    <title>
        <band height="60">
            <!-- ยอดรวมทั้งฉบับแสดงบนหน้าแรกทันทีผ่าน evaluationTime="Report" -->
            <element kind="textField" x="355" y="15" width="200" height="25"
                     evaluationTime="Report" pattern="#,##0.00" blankWhenNull="true">
                <expression><![CDATA[$V{TOTAL_NET}]]></expression>
            </element>
        </band>
    </title>

    <pageFooter>
        <band height="25">
            <!-- สถาปัตยกรรม Page X of Y สองจังหวะ -->
            <element kind="textField" x="380" y="5" width="80" height="18" evaluationTime="Now">
                <expression><![CDATA["Page " + $V{PAGE_NUMBER} + " of "]]></expression>
            </element>
            <element kind="textField" x="460" y="5" width="50" height="18" evaluationTime="Report">
                <expression><![CDATA[$V{PAGE_NUMBER}]]></expression>
            </element>
        </band>
    </pageFooter>
</jasperReport>

กลไกเบื้องหลัง:
pattern="#,##0.00": จัดรูปแบบตัวเลขทศนิยมทางการเงิน 2 ตำแหน่งอย่างเป็นทางการ
blankWhenNull="true": ดักจับค่า null ไม่ให้พิมพ์ข้อความ "null" หรือเกิด NPE กลางคัน

Hands-on Engineering Lab 1.4.1: Two-Pass Evaluation & Delayed Total Aggregation

🛠️ Lab 1.4.1: Lookahead Aggregation & Dynamic Page Numbering

เงื่อนไขทางเทคนิค (Full-Coverage 100%):
1. กำหนดตัวแปรสะสมยอดเงินรวม <variable name="TOTAL_AMOUNT" ...> โดยระบุ calculation="Sum" และ resetType="Report"
2. กำหนด initialValueExpression ของตัวแปรเป็น BigDecimal.ZERO ป้องกันค่า null เริ่มต้น
3. วาง TextField แสดงยอดรวมใน <title> Band โดยระบุ evaluationTime="Report" เพื่อดึงยอดรวมข้ามหน้ามาแสดงที่หัวกระดาษ
4. ออกแบบโครงสร้างเลขหน้า "Page X of Y" ใน <pageFooter> โดยกล่องแรกมี evaluationTime="Now" และกล่องหลังมี evaluationTime="Report"
5. กำหนด pattern="#,##0.00" สำหรับตัวเลขยอดเงิน และใช้ไวยากรณ์ 7.0.x Unified Model (<element kind="textField">)

Hands-on Engineering Lab 1.4.2: Data Pipeline Type Safety & In-Memory Transformations

🛠️ Lab 1.4.2: Type-Safe Java 17/21 Expressions & Null Defense

เงื่อนไขทางเทคนิค (Full-Coverage 100%):
1. กำหนด Class Types ของฟิลด์ให้ถูกต้องตามข้อกำหนดการเงิน: BigDecimal สำหรับ unit_price, Integer สำหรับ quantity, และ Double สำหรับ discount_rate
2. ป้องกัน ClassCastException และ Precision Loss โดยใช้เมธอด .multiply() บน BigDecimal แทนเครื่องหมายคูณ *
3. สร้าง Ternary Operator ป้องกัน NullPointerException บนฟิลด์ส่วนลด: ($F{discount_rate} != null ? $F{discount_rate}.doubleValue() : 0.0)
4. ใส่ blankWhenNull="true" บนทุก TextField ที่ทำการแสดงผลข้อมูล
5. กำหนด pattern="#,##0.00" เพื่อให้การแสดงผลทศนิยมทางการเงินสมบูรณ์แบบ

Step 3: Interactive Checkpoint Quiz (100% In-Content Guarantee)

แบบทดสอบ 15 ข้อเชิงลึก อ้างอิงจากหลักการ Data Pipeline, Evaluation Timing และการจัดการหน่วยความจำบน Production:

⚡ Real-time Score Tracker (Level 1.4)
คะแนน: 0 / 15 (ตอบแล้ว: 0 / 15)