VuLinhJava Blog

website·Indexed 29 days ago

https://vulinhjava.io.vn

A technical blog focusing on Java development, Spring Boot, and JPA. Content covers JDK features from 8 through 21 and 25, thread safety strategies (synchronized, volatile, ReentrantLock), Project Lombok best practices, and performance optimization such as solving the N+1 query problem. It also provides guides on integrating KeyCloak, P6Spy, and OpenFeign with QueryDSL, as well as discussions on design patterns like Singleton and the use of Stream Gatherers (JEP 485).

Tokens
44.3K
Snippets
211
Records
283
Agent score
46%

What's inside VuLinhJava

  1. Overview of Stream Gatherers in JDK 25

    Stream Gatherers introduce a way to create custom intermediate operations for the Stream API. Unlike standard intermediate operations, Gatherers allow developers to define more complex stream transformations that return another Stream and remain lazy until a terminal operation is invoked.
  2. Understand time zone complexities in global applications

    Handling time globally requires accounting for political and geographical anomalies. Key examples include:

    • Multi-zone countries: Russia spans 11 time zones; France spans 12 due to overseas territories.
    • Political time zones: China geographically spans several zones but mandates a single time zone (UTC+8) nationwide, leading to significant discrepancies between local solar time and official clock time in western regions.
  3. Understand Java's pass-by-value mechanism

    Java is strictly pass-by-value. This means that when a method is called, a copy of the argument's value is passed to the method. This applies to both primitive types and object types:

    1. Primitive Types: The actual value (e.g., an integer) is copied. Changes made to the parameter inside the method do not affect the original variable.
    2. Object Types: The value being passed is the reference (the memory address) to the object. A copy of this reference is passed. Therefore, reassigning the parameter to a new object inside the method does not change the original reference in the calling code.
  4. Use LinkedHashMap to preserve insertion order

    LinkedHashMap extends HashMap and maintains a doubly-linked list running through all its entries. This allows it to remember the exact order in which keys were inserted while maintaining the same O(1) performance characteristics as a standard HashMap.
  5. Avoid synchronizing on null objects or value classes

    Attempting to synchronize on a null object will result in a NullPointerException. Additionally, with the introduction of value classes (Project Valhalla), synchronizing on value class instances will result in a compile error or an IdentityException at runtime because value classes do not have monitors.
    Object lock = null;
    synchronized(lock) {  // Throws NullPointerException
      System.out.println("This will never print");
    }
  6. Understand the role of AI in software development

    AI should be viewed as a high-speed assistant (similar to a fast intern) rather than an infallible source of truth. While AI can rapidly generate prototypes and proof-of-concept (PoC) projects, it is trained on human-written code, which may contain security vulnerabilities, performance bottlenecks, and bugs. Developers must maintain a strong grasp of programming fundamentals to identify and correct AI-generated errors that may only surface under real-world load, edge cases, or security audits.
  7. Handle missing values with orElseThrow

    Avoid using the generic .get() method, as it is not explicit about failure. Use orElseThrow() to either retrieve the value or throw a NoSuchElementException, or provide a custom exception supplier for better error reporting.
    // Basic usage: throws NoSuchElementException if empty
    var user = findUser(id).orElseThrow(); 
    
    // Premium usage: throws a specific custom exception
    var user = findUser(id).orElseThrow(
        () -> new UserNotFoundException("User ID not found: " + id)
    );
  8. Integrate AI prompting with fundamental programming knowledge

    The most effective development workflow is a symbiosis of prompt engineering and core computer science knowledge. Use AI to accelerate the coding phase, but apply manual oversight for the following critical software engineering stages:

    • Requirement definition
    • Rigorous testing
    • Code review
    • Scaling and reliability planning
    • Production incident response and debugging
  9. Implement a time zone-neutral architecture in Java

    To avoid bugs related to Daylight Saving Time (DST) and political time zone shifts, follow this architectural pattern: store all timestamps in UTC, perform business logic using time zone-independent types, and only convert to local time zones at the presentation layer for the user.
  10. Understand the N+1 Query Problem in Spring JPA

    The N+1 query problem occurs when a JPA entity has a lazy-loaded relationship (e.g., @OneToMany or @ManyToMany). When fetching a list of parent entities and iterating over their children, JPA executes one query to fetch the parents (the '1') and then one additional query for each parent to fetch its children (the 'N'). This leads to poor scalability and high database I/O latency as the number of parent records increases. If an entity has multiple lazy collections, the problem compounds (e.g., 1 + 2N queries).
  11. Understand Java HashMap internal storage and bucket behavior

    Java's HashMap uses an internal array of buckets to store key-value pairs. Depending on the number of items in a bucket, it switches storage strategies to maintain performance:

    • LinkedList: Used when a bucket contains fewer than 8 items.
    • Red-Black Tree: Used when a bucket contains 8 or more items AND the total number of buckets in the map is at least 64 (introduced in JDK 8).
  12. Choose the right Singleton implementation strategy in Java

    Depending on the requirements for lazy loading and thread safety, use the following guidelines for implementing the Singleton pattern in modern Java:

    • Enum Singletons: The recommended default for 90% of use cases. They provide the most robust protection against reflection and serialization issues.
    • Bill Pugh Singleton: Use this for the remaining 10% of cases where lazy loading is critical and an Enum is not suitable.
    • Avoid: Manual synchronized blocks and double-checked locking patterns, as they are largely considered historical curiosities and are prone to implementation errors.