Skip to content
Back to skills

Project Lombok Skill

ASecurity

Comprehensive Project Lombok annotations and patterns for reducing Java boilerplate. Covers @Data, @Builder, @Slf4j, @RequiredArgsConstructor, @Value, custom annotations, and best practices for clean, maintainable code.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 5, 2026
devopsjavaspringapidatabaseperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 21 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add sharmapuneet1510/awesome-prompts --skill skills --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Project Lombok Skill?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Project Lombok Skill
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sharmapuneet1510-project-lombok-skill/badge)](https://www.skillsdirectory.com/skills/sharmapuneet1510-project-lombok-skill)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: Project Lombok Skill
version: 1.1
description: >
  Comprehensive Project Lombok annotations and patterns for reducing Java boilerplate.
  Covers @Data, @Builder, @Slf4j, @RequiredArgsConstructor, @Value, custom annotations,
  and best practices for clean, maintainable code.
applies_to: [java, lombok, spring-boot, maven, gradle, code-generation]
tags: [lombok, boilerplate, annotations, builders, logging, constructors]
---

# Project Lombok Skill — v1.1

## Quick Card

> Read this card first. Load a section below only when the task needs it.

| | |
|---|---|
| **Use when** | Reducing Java boilerplate with Lombok |
| **Skip when** | Java 17+ immutable data carriers — a `record` needs no Lombok |
| **Inputs** | Build tool, JDK version, entity vs DTO vs service |
| **Produces** | Annotated classes, Maven/Gradle setup |
| **Steps** | 1. Add dependency + annotation processing → 2. Pick annotations per class role → 3. Exclude relations from `toString`/`equals` → 4. Test the generated behaviour |
| **Done when** | §11 checklist passes; no `@Data` on an `@Entity` |
| **Senior defaults** | Never `@Data` on a JPA entity — `@Getter`/`@Setter` + id-based `equals`, constant `hashCode` · `@RequiredArgsConstructor` + `final` fields for injection · `@With` (not the deprecated `@Wither`) returns a copy · `@Builder.Default` for field defaults · prefer `record` over `@Value` on Java 17+ |
| **Load on demand** | §1 setup · §3 @Data/@Value · §4 constructors · §5 @Builder · §9 exclusions · §10 best practices |
| **Run report** | `html_report_skill` — adds: Annotations applied per class |
| **Pairs with** | `java_advanced_skill`, `spring_advanced_skill` |

---

## 1. Maven & Gradle Setup

### 1.1 Maven Configuration

```xml
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

<!-- Maven compiler plugin configuration -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.10.1</version>
    <configuration>
        <source>17</source>
        <target>17</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.30</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
```

### 1.2 Gradle Configuration

```gradle
dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
}
```

### 1.3 IDE Configuration

```text
IntelliJ IDEA:
  Settings → Plugins → Search "Lombok" → Install
  Settings → Build, Execution, Deployment → Compiler →
    Annotation Processors → Enable annotation processing

VS Code:
  Install "Lombok Annotations Support for VS Code"
  Install "Language Support for Java (Red Hat)"

Eclipse:
  Run: java -jar lombok.jar install eclipse.exe
```

---

## 2. Core Annotations

### 2.1 @Getter & @Setter

```java
/**
 * Generate getters and setters automatically.
 */
@Getter
@Setter
public class Order {
    private Long id;
    private Long customerId;
    private BigDecimal totalAmount;
    private String status;

    // Generates:
    // public Long getId() { return this.id; }
    // public void setId(Long id) { this.id = id; }
    // ... for all fields
}

/**
 * Selective generation: @Getter on class, @Setter on field.
 */
@Getter
public class ImmutableOrder {
    private Long id;
    private Long customerId;
    private BigDecimal totalAmount;

    @Setter
    private String status;  // Only this field has setter
}
```

### 2.2 @ToString & @EqualsAndHashCode

```java
/**
 * Generate toString, equals, and hashCode.
 */
@ToString
@EqualsAndHashCode
public class Product {
    private Long id;
    private String name;
    private BigDecimal price;

    // Generates:
    // public String toString() { return "Product(...)"; }
    // public boolean equals(Object o) { ... }
    // public int hashCode() { ... }
}

/**
 * Selective field exclusion.
 */
@ToString(exclude = {"internalId", "temp"})
@EqualsAndHashCode(exclude = {"createdAt"})
public class Order {
    private Long id;
    private String internalId;  // Won't be included in toString/equals
    private LocalDateTime createdAt;  // Won't be in equals/hashCode
    private BigDecimal totalAmount;
}
```

---

## 3. @Data & @Value

### 3.1 @Data: All-In-One Annotation

```java
/**
 * @Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor
 *
 * ✓ RECOMMENDED for mutable DTOs and domain objects
 */
@Data
public class CustomerDTO {
    private Long id;
    private String name;
    private String email;
    private List<OrderDTO> orders;

    // Generates:
    // - Getters for all fields
    // - Setters for all fields
    // - toString()
    // - equals() and hashCode()
    // - Constructor with required fields (final + @NonNull)
}
```

### 3.2 @Value: Immutable Variant

```java
/**
 * @Value = immutable version of @Data
 *
 * ✓ RECOMMENDED for value objects, DTOs that should be immutable
 */
@Value
public class Money {
    BigDecimal amount;  // All fields become final
    String currency;    // All fields become private

    // Generates:
    // - Getters only (no setters)
    // - toString()
    // - equals() and hashCode()
    // - Constructor with all fields
    // - All fields are effectively immutable
}

/**
 * "Changing" a field of an immutable @Value with @With.
 * (@Wither is the deprecated pre-1.18.10 name — use @With.)
 */
@Value
public class OrderAmount {
    BigDecimal amount;
    String currency;

    @With
    LocalDateTime updatedAt;  // .withUpdatedAt(t) returns a NEW copy; the original is unchanged
}
```

---

## 4. Constructors: @RequiredArgsConstructor & @AllArgsConstructor

### 4.1 Constructor Generation

```java
/**
 * @RequiredArgsConstructor: Constructor for final + @NonNull fields.
 *
 * BEST PRACTICE for dependency injection.
 */
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;
    private final EmailService emailService;

    // Generates:
    // public OrderService(OrderRepository orderRepository, PaymentGateway paymentGateway, EmailService emailService) {
    //     this.orderRepository = orderRepository;
    //     this.paymentGateway = paymentGateway;
    //     this.emailService = emailService;
    // }
}

/**
 * Using with @Autowired (Spring).
 */
@Service
@RequiredArgsConstructor
public class CustomerService {
    private final CustomerRepository customerRepository;
    private final CustomerMapper customerMapper;

    public CustomerDTO getCustomer(Long id) {
        Customer customer = customerRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("Customer not found"));
        return customerMapper.toDTO(customer);
    }
}

/**
 * @AllArgsConstructor: Constructor for ALL fields.
 */
@AllArgsConstructor
@Data
public class Order {
    private Long id;
    private Long customerId;
    private BigDecimal totalAmount;
    private String status;

    // Generates constructor with all fields in order
}
```

---

## 5. Builder Pattern: @Builder

### 5.1 Simple Builder

```java
/**
 * @Builder: Generates Builder pattern automatically.
 */
@Builder
@Data
public class Order {
    private Long id;
    private Long customerId;
    private BigDecimal totalAmount;
    private String status;
    private LocalDateTime createdAt;
    private List<OrderItem> items;

    // Usage:
    // Order order = Order.builder()
    //     .customerId(123L)
    //     .totalAmount(BigDecimal.valueOf(99.99))
    //     .status("PENDING")
    //     .items(List.of(...))
    //     .build();
}
```

### 5.2 Builder with Defaults & Customization

```java
/**
 * Builder with custom defaults and validation.
 */
@Builder(toBuilder = true)  // Enable .toBuilder() to copy and modify
@Data
public class Order {
    private Long id;
    private Long customerId;

    @Builder.Default
    private BigDecimal totalAmount = BigDecimal.ZERO;

    @Builder.Default
    private String status = "PENDING";

    @Builder.Default
    private LocalDateTime createdAt = LocalDateTime.now();

    @Builder.Default
    private List<OrderItem> items = new ArrayList<>();

    // Usage:
    Order order = Order.builder()
            .customerId(123L)
            .build();  // Uses defaults

    // Copy and modify:
    Order updatedOrder = order.toBuilder()
            .status("CONFIRMED")
            .build();
}
```

### 5.3 Builder with Validation

```java
/**
 * Custom builder with validation.
 */
@Builder(toBuilder = true)
@Data
public class PaymentRequest {
    private BigDecimal amount;
    private String currency;
    private String cardToken;

    /**
     * Custom validation in builder.
     */
    public static class PaymentRequestBuilder {
        public PaymentRequest build() {
            if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
                throw new ValidationException("Amount must be positive");
            }
            if (currency == null || currency.isEmpty()) {
                throw new ValidationException("Currency is required");
            }
            if (cardToken == null || cardToken.isEmpty()) {
                throw new ValidationException("Card token is required");
            }
            return new PaymentRequest(amount, currency, cardToken);
        }
    }

    // Usage:
    try {
        PaymentRequest request = PaymentRequest.builder()
                .amount(BigDecimal.valueOf(99.99))
                .currency("USD")
                .cardToken("tok-123")
                .build();  // Validated
    } catch (ValidationException e) {
        log.error("Validation failed: {}", e.getMessage());
    }
}
```

---

## 6. Logging: @Slf4j & Variants

### 6.1 @Slf4j (SLF4J)

```java
/**
 * @Slf4j: Generates SLF4J logger named 'log'.
 *
 * RECOMMENDED: Industry standard, works with any implementation
 */
@Service
@Slf4j
public class OrderService {
    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public Order createOrder(OrderRequest request) {
        log.debug("Creating order for customer: {}", request.getCustomerId());

        Order order = new Order();
        order.setCustomerId(request.getCustomerId());

        try {
            Order saved = orderRepository.save(order);
            log.info("Order created successfully with ID: {}", saved.getId());
            return saved;

        } catch (Exception e) {
            log.error("Failed to create order for customer: {}", request.getCustomerId(), e);
            throw e;
        }
    }
}
```

### 6.2 Other Logger Annotations

```java
/**
 * @Log (java.util.logging)
 */
@Log
public class LegacyService {
    void doSomething() {
        log.info("Using java.util.logging");
    }
}

/**
 * @Log4j2 (Apache Log4j 2)
 */
@Log4j2
public class HighPerformanceService {
    void doSomething() {
        logger.info("Using Log4j2");
    }
}

/**
 * @CommonsLog (Apache Commons Logging)
 */
@CommonsLog
public class LegacyApacheService {
    void doSomething() {
        log.info("Using Commons Logging");
    }
}
```

---

## 7. Null Checking: @NonNull

### 7.1 Parameter Validation

```java
/**
 * @NonNull on constructor parameter: generates null check.
 */
@RequiredArgsConstructor
public class PaymentProcessor {
    private final PaymentGateway paymentGateway;
    private final AuditService auditService;

    public PaymentResult processPayment(
            @NonNull BigDecimal amount,
            @NonNull String cardToken) {

        // Generates:
        // if (amount == null) throw new NullPointerException("amount is marked non-null but is null");
        // if (cardToken == null) throw new NullPointerException("cardToken is marked non-null but is null");

        return paymentGateway.charge(amount, cardToken);
    }

    /**
     * @NonNull on field: generates null check in constructor + setter.
     */
    @NonNull
    private String merchantId;

    public void setMerchantId(@NonNull String merchantId) {
        this.merchantId = merchantId;  // Null check generated
    }
}
```

---

## 8. Accessors: @Accessors

### 8.1 Fluent API

```java
/**
 * @Accessors(fluent = true): Getters/setters without get/set prefix.
 */
@Data
@Accessors(fluent = true)
public class Order {
    private Long id;
    private BigDecimal amount;
    private String status;

    // Generates:
    // public Long id() { return this.id; }
    // public Order id(Long id) { this.id = id; return this; }
    // public BigDecimal amount() { return this.amount; }
    // public Order amount(BigDecimal amount) { this.amount = amount; return this; }

    // Usage:
    Order order = new Order()
            .id(123L)
            .amount(BigDecimal.valueOf(99.99))
            .status("PENDING");
}

/**
 * @Accessors(chain = true): Setters return this for chaining.
 */
@Data
@Accessors(chain = true)
public class QueryBuilder {
    private String table;
    private String where;
    private int limit;

    // Usage:
    QueryBuilder query = new QueryBuilder()
            .setTable("orders")
            .setWhere("customer_id = 123")
            .setLimit(10);
}
```

---

## 9. Annotations with Exclusions

### 9.1 Field-Level Control

```java
/**
 * @Exclude fields from generation.
 */
@Data
@ToString(exclude = {"password", "apiKey"})
@EqualsAndHashCode(exclude = {"updatedAt"})
public class User {
    private Long id;
    private String username;

    @ToString.Exclude
    private String password;  // Won't appear in toString()

    @EqualsAndHashCode.Exclude
    private String apiKey;  // Won't affect equals/hashCode

    @EqualsAndHashCode.Exclude
    private LocalDateTime updatedAt;
}
```

---

## 10. Best Practices

### 10.1 Recommended Pattern

```java
/**
 * ✓ BEST PRACTICE: Entity with Lombok.
 *
 * Never @Data on an @Entity: its equals/hashCode cover every mutable field,
 * so an entity's hash changes when the database assigns its id (it vanishes
 * from any HashSet it was in), and toString can trigger lazy loading.
 * Use @Getter/@Setter and identity-based equality instead.
 */
@Entity
@Table(name = "orders")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)  // for JPA; callers use the builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)   // for @Builder only
@Builder
@ToString(onlyExplicitlyIncluded = true)
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @ToString.Include
    private Long id;

    @Column(nullable = false)
    private Long customerId;

    @Column(nullable = false)
    private BigDecimal totalAmount;

    @Enumerated(EnumType.STRING)  // needs an enum type — never a String field
    @Column(nullable = false)
    @Builder.Default
    @ToString.Include
    private OrderStatus status = OrderStatus.PENDING;

    @CreationTimestamp
    private LocalDateTime createdAt;

    @UpdateTimestamp
    private LocalDateTime updatedAt;

    /** Equal only when both are persisted with the same id; works with Hibernate proxies. */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Order other)) return false;
        return id != null && id.equals(other.getId());
    }

    /** Constant per class, so the hash does not change when the id is assigned. */
    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
}

/**
 * ✓ BEST PRACTICE: Service with Lombok.
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;
    private final NotificationService notificationService;

    public Order createOrder(@NonNull CreateOrderRequest request) {
        log.info("Creating order for customer: {}", request.getCustomerId());

        Order order = Order.builder()
                .customerId(request.getCustomerId())
                .totalAmount(request.getTotalAmount())
                .build();                     // status defaults to PENDING

        Order saved = orderRepository.save(order);
        log.info("Order saved with ID: {}", saved.getId());

        return saved;
    }
}
```

### 10.2 Anti-Patterns to Avoid

```java
/**
 * ✗ AVOID: Lombok on interfaces or abstract classes
 * (doesn't make sense, doesn't generate anything)
 */
@Data  // ✗ Pointless
public interface OrderService {
    Order createOrder(CreateOrderRequest request);
}

/**
 * ✗ AVOID: Using @Data on classes with @Entity + bidirectional relationships
 * (generates .equals() that causes infinite recursion)
 */
@Entity
@Data  // ✗ Problematic with relationships
public class Order {
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;  // ✗ Causes recursion in equals()
}

/**
 * ✓ BETTER: @Getter/@Setter, identity equals/hashCode (see §10.1), and
 * relationships left out of toString.
 */
@Entity
@Getter
@Setter
@ToString(onlyExplicitlyIncluded = true)
public class Order {
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;  // ✓ not in toString; equals uses id only
}
```

---

## 11. Lombok Checklist

✅ Add dependency to Maven/Gradle
✅ Configure IDE annotation processing
✅ Use @Data for mutable DTOs — never on a JPA @Entity (use @Getter/@Setter + id-based equals)
✅ Use @Value for immutable objects
✅ Use @RequiredArgsConstructor for DI
✅ Use @Builder for complex object creation
✅ Use @Slf4j for logging
✅ Use @NonNull for parameter validation
✅ Exclude JPA relationship fields from equals/hashCode
✅ Keep using Javadoc (Lombok generates code, not docs)
✅ Be aware of @Data limitations (avoid with bidirectional relationships)
✅ Use @Accessors(fluent=true) for fluent APIs
✅ Generate boilerplate, never lose readability
✅ Don't over-engineer: simple POJOs might be clearer
✅ Test generated code like any other code

Files in this skill

  • README.md10.2 KB
  • adr_skill.md8.4 KB
  • agent_skill_design_skill.md3.1 KB
  • apache_camel_skill.md15.5 KB
  • apache_pulsar_skill.md17.1 KB
  • ba_create_skill.md18.9 KB
  • backend_skill.md22.1 KB
  • code_documentation_skill.md13.4 KB
  • code_formatting_skill.md11.7 KB
  • code_health_skill.md9.8 KB
  • code_review_skill.md36.7 KB
  • context_builder_skill.md11.7 KB
  • current_tech_spec_skill.md6.3 KB
  • database_skill.md18.4 KB
  • debugging_skill.md3.4 KB
  • error_handling_skill.md18.4 KB
  • frontend_skill.md23.6 KB
  • java_advanced_skill.md15 KB
  • jira_html_report_skill.md15.5 KB
  • jira_incremental_spec_generator_skill.md19.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…