Lombok Patterns
This skill provides guidance on using Project Lombok to reduce boilerplate code in Java applications, covering annotations, best practices, and integration patterns.
Note: This guide targets Java 21+ projects. Some Lombok features are now obsolete due to modern Java features (Records,
var, try-with-resources). See Java 21+ Considerations below.
Overview
Lombok is a Java library that automatically generates common code like getters, setters, constructors, builders, and more during compilation. It helps create cleaner, more maintainable code.
Java 21+ Considerations
Some Lombok features are now redundant with modern Java:
| Lombok Feature | Java Alternative | Recommendation |
| -------------- | ---------------------------- | --------------------------------------- |
| val/var | var (Java 10+) | Use Java var |
| @Value | record (Java 16+) | Prefer records for simple value objects |
| @Data | record (Java 16+) | Prefer records for simple DTOs |
| @Cleanup | try-with-resources (Java 7+) | Use try-with-resources |
| @With | record wither methods | Records can define wither methods |
When Lombok Still Adds Value
- JPA Entities: Records don't work with JPA (need no-arg constructor, mutable state)
@Builder: Records don't have built-in builder support@Slf4j: No Java equivalent for logger generation@RequiredArgsConstructor: Useful for Spring dependency injection- Inheritance: Records can't extend classes
Annotation Categories
Core Annotations (Still Valuable)
| Annotation | Purpose | Use Case |
| -------------------------- | ------------------------------------- | ----------------------------- |
| @Getter/@Setter | Accessor methods | JPA entities, mutable classes |
| @NoArgsConstructor | No-argument constructor | JPA entities, serialization |
| @AllArgsConstructor | All-fields constructor | Dependency injection |
| @RequiredArgsConstructor | Constructor for final/@NonNull fields | Spring services |
| @Builder | Builder pattern implementation | Complex object construction |
Consider Java Records Instead
| Lombok | Use Java Record When |
| -------- | ------------------------------- |
| @Value | Simple immutable data carriers |
| @Data | Simple DTOs without inheritance |
// ✅ Prefer Java Record for simple value objects
public record Money(BigDecimal amount, String currency) {}
// ✅ Prefer Java Record for simple DTOs
public record CustomerDTO(String name, String email) {}
// ✅ Use Lombok for JPA entities (records don't work with JPA)
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Customer {
@Id private Long id;
private String name;
}
Logging Annotations
| Annotation | Logger Type |
| ------------- | ---------------------- |
| @Slf4j | SLF4J (recommended) |
| @Log4j2 | Log4j 2 |
| @Log | java.util.logging |
| @CommonsLog | Apache Commons Logging |
Field & Method Annotations
| Annotation | Purpose |
| -------------------- | -------------------------------------------- |
| @NonNull | Null checks on parameters/fields |
| @With | Immutable setters (creates new instance) |
| @SneakyThrows | Throw checked exceptions without declaration |
| @Synchronized | Thread-safe method synchronization |
| @ToString | Customizable toString() generation |
| @EqualsAndHashCode | Customizable equals/hashCode |
Quick Reference
JPA Entities (Lombok Recommended)
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Product {
@Id
@EqualsAndHashCode.Include
private Long id;
private String name;
private BigDecimal price;
}
Spring Services
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
public Order process(Order order) {
log.info("Processing order: {}", order.getId());
return orderRepository.save(order);
}
}
Builder Pattern (Lombok Adds Value)
// Records don't have builder support - use Lombok
@Builder
public record Email(
String to,
String subject,
String body,
@Singular List<String> attachments
) {}
// Usage
Email email = Email.builder()
.to("user@example.com")
.subject("Hello")
.body("Content")
.attachment("file1.pdf")
.build();
Best Practices
✅ Do
- Use Java Records for simple value objects and DTOs
- Use
@RequiredArgsConstructorwithfinalfields for dependency injection - Use
@Slf4jfor logging instead of manual logger creation - Use
@Builderfor classes with many optional fields - Be explicit with
@EqualsAndHashCodeon JPA entities
❌ Avoid
- Using
@Dataor@Valuewhen a Java Record would suffice - Using
@Dataon JPA entities (use individual annotations) - Using
@EqualsAndHashCodewith lazy-loaded JPA relationships - Overusing
@SneakyThrows(makes exception handling unclear)
Common Pitfalls
| Pitfall | Problem | Solution |
| ---------------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
| @Data on entities | Includes mutable setters, problematic equals/hashCode | Use @Getter, @Setter, explicit @EqualsAndHashCode |
| Missing @NoArgsConstructor | JPA/Jackson need no-arg constructor | Always add with @AllArgsConstructor |
| Circular @ToString | StackOverflow with bidirectional relationships | Use @ToString.Exclude on one side |
| Using Lombok for simple DTOs | Adds unnecessary dependency | Use Java Records instead |
Cookbook Index
Core Annotations
- data-annotation -
@Data(consider Records instead) - value-annotation -
@Value(consider Records instead) - getter-setter -
@Getterand@Setter - constructor-annotations - Constructor generation
- builder-annotation - Builder pattern with
@Builder - logging-annotations - Logger generation
Field & Method Annotations
- non-null - Null checking with
@NonNull - with-annotation - Immutable setters with
@With - sneaky-throws - Checked exception handling
- synchronized-annotation - Thread safety
Integration Patterns
- entity-patterns - JPA/Hibernate best practices
- equals-hashcode-jpa - Entity identity
- dependency-injection - Spring constructor injection
- configuration-properties - Spring Boot configuration