Boxing, Unboxing, and Wrapper Classes in JPA DTO Creation - hmislk/hmis GitHub Wiki
Overview
When creating Data Transfer Objects (DTOs) in JPA, it is important to understand the concepts of boxing, unboxing, and wrapper classes. These concepts come into play whenever you are dealing with primitive types (e.g., int, double) and their corresponding object types (e.g., Integer, Double). Proper handling ensures type safety, prevents NullPointerExceptions, and improves compatibility with JPA queries.
Wrapper Classes
In Java, every primitive type has a corresponding wrapper class in the java.lang package:
| Primitive | Wrapper Class |
|---|---|
boolean |
Boolean |
byte |
Byte |
char |
Character |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
Wrapper classes allow primitives to be treated as objects, which is essential when working with collections, generics, or JPA DTO constructors, because JPA cannot directly map primitive types to null.
Boxing
Boxing is the automatic conversion of a primitive type into its corresponding wrapper class. Example:
int value = 10;
Integer boxedValue = value; // boxing
Unboxing
Unboxing is the reverse process — converting a wrapper class object back into its primitive type. Example:
Integer boxedValue = 10;
int unboxedValue = boxedValue; // unboxing
Why Wrapper Classes Are Preferred in JPA DTOs
When constructing DTOs using JPQL or Criteria queries, wrapper classes are preferred over primitives for several reasons:
-
Null Safety
- Primitives cannot hold
null. - JPA often returns
nullfor missing values in projections. - Using a primitive (e.g.,
int) can cause aNullPointerException.
// Risky: will throw NPE if db column is null public DTO(int count) { ... } // Safe: can accept null public DTO(Integer count) { ... } - Primitives cannot hold
-
Optional Values in Queries Wrapper classes make it easy to represent optional fields (e.g., a bill discount may or may not exist).
-
Consistency in Calculations Wrapper classes integrate better with methods like
BigDecimal.valueOf()and can be checked for null before calculations. -
JPQL Constructor Expressions JPQL requires wrapper classes when instantiating DTOs with possible
nullvalues.String jpql = "SELECT new com.divudi.dto.SaleSummaryDTO(" + " b.id, " + " COALESCE(b.total, 0) ) " + "FROM Bill b";
Best Practices for DTO Creation
-
Always use Wrapper Types in DTOs
public class LabIncomeReportDTO { private Long billId; // not long private BigDecimal total; // not double private Integer count; // not int } -
Use
COALESCEin JPQL Preventsnullvalues from causing runtime issues.SELECT new com.divudi.dto.LabIncomeReportDTO( b.id, COALESCE(b.netTotal, 0.0), COALESCE(b.discount, 0.0) ) FROM Bill b -
Guard Against Nulls in Controller Code Always check for
nullwhen doing calculations.BigDecimal safeTotal = dto.getTotal() != null ? dto.getTotal() : BigDecimal.ZERO; -
Prefer
BigDecimalfor Monetary Values Avoiddoubleorfloatin financial calculations to prevent rounding errors.
Example: DTO Constructor with Wrapper Classes
public class LabIncomeReportDTO {
private Long billId;
private String billNumber;
private Date billDate;
private String patientName;
private BigDecimal netTotal;
private BigDecimal discount;
public LabIncomeReportDTO(Long billId, String billNumber, Date billDate,
String patientName, BigDecimal netTotal, BigDecimal discount) {
this.billId = billId;
this.billNumber = billNumber;
this.billDate = billDate;
this.patientName = patientName;
this.netTotal = netTotal != null ? netTotal : BigDecimal.ZERO;
this.discount = discount != null ? discount : BigDecimal.ZERO;
}
}
Conclusion
Using wrapper classes with careful attention to boxing and unboxing is critical for stable and null-safe JPA DTO creation. Always avoid primitives in DTOs, use COALESCE in JPQL queries, and handle nulls defensively in code.
This practice ensures your HMIS DTOs are robust, performant, and safe against runtime errors.