How to Use Maven Plugins for Spring Boot CRUD Code Generation
Purpose
This post demonstrates how to automate Spring Boot CRUD code generation using Maven plugins, specifically the Spring CRUD Generator plugin.
When I started building Spring Boot applications, I spent hours writing the same repetitive code for each entity: entity classes, DTOs, mappers, services, controllers, repository interfaces, database migrations, API documentation, and Docker configuration. I wanted to find a way to generate this code from configuration instead of writing it manually.
Environment
- Spring Boot 3.2
- Maven 3.9
- Java 17
- MySQL 8.0 / PostgreSQL 14
- Spring CRUD Generator 1.3.0
The Problem: CRUD Boilerplate Bloat
When I build a Spring Boot CRUD application, I need to create multiple layers for each entity:
Entity → DTO → Mapper → Service → Controller → Repository → Migration → API Docs → DockerFor a simple User entity, I used to write:
- User entity with JPA annotations (50+ lines)
- UserDTO request/response classes (30+ lines)
- UserMapper interface with MapStruct (20+ lines)
- UserService interface and implementation (80+ lines)
- UserController with REST endpoints (60+ lines)
- UserRepository interface (10+ lines)
- Flyway migration SQL file (20+ lines)
- OpenAPI annotations on controller (15+ lines)
- Docker configuration in Dockerfile (30+ lines)
That’s over 300 lines of code per entity, most of it following the same pattern.
When I have 10 entities, that’s 3,000+ lines of repetitive code. If I make a mistake in one layer, I get runtime errors. If I want to change the architecture pattern, I have to update all 10 entities manually.
The Solution: Spring CRUD Generator
Spring CRUD Generator is a Maven plugin that generates all these layers from a single YAML or JSON configuration file. I define my data model once, and the plugin generates consistent code following Spring Boot best practices.
The plugin generates:
- JPA entities with validation annotations
- Request/response DTOs
- MapStruct mappers for entity-DTO conversion
- Service interfaces and implementations
- REST controllers with OpenAPI annotations
- Spring Data JPA repositories
- Flyway database migrations
- Dockerfile and docker-compose configuration
Configuration File
I create a file called spring-crud-generator.yml in my project root:
project: name: "User Management API" package: "com.example.usermanagement" database: "postgresql"
entities: - name: "User" table: "users" fields: - name: "id" type: "Long" primaryKey: true generated: true - name: "username" type: "String" nullable: false unique: true length: 50 - name: "email" type: "String" nullable: false unique: true length: 100 - name: "createdAt" type: "LocalDateTime" nullable: false insertable: false updatable: false
options: generateOpenApi: true generateFlyway: true generateDocker: true excludeNullsInResponse: trueI can explain the key parts:
- project: Defines package structure and database type
- entities: List of entities to generate
- fields: Column definitions with constraints
- options: Toggle OpenAPI, Flyway, Docker generation
- excludeNullsInResponse: Omits null fields in JSON responses (v1.3.0 feature)
Maven Plugin Configuration
I add the plugin to my pom.xml:
<build> <plugins> <plugin> <groupId>com.github.bwenskie</groupId> <artifactId>spring-crud-generator-maven-plugin</artifactId> <version>1.3.0</version> <configuration> <configFile>spring-crud-generator.yml</configFile> <outputDir>${project.basedir}/src/main/java</outputDir> </configuration> </plugin> </plugins></build>Running Code Generation
I run the Maven goal to generate code:
mvn spring-crud-generator:generateThe plugin reads my YAML configuration and generates all layers in seconds.
Generated Code Structure
Here’s what gets generated:
src/main/java/└── com/example/usermanagement/ ├── entity/ │ └── User.java ├── dto/ │ ├── UserDTO.java │ └── UserResponseDTO.java ├── mapper/ │ └── UserMapper.java ├── service/ │ ├── UserService.java │ └── impl/ │ └── UserServiceImpl.java ├── controller/ │ └── UserController.java └── repository/ └── UserRepository.java
src/main/resources/└── db/migration/ └── V1__create_users_table.sql
Dockerfiledocker-compose.ymlGenerated Entity Example
package com.example.usermanagement.entity;
import jakarta.persistence.*;import java.time.LocalDateTime;
@Entity@Table(name = "users", uniqueConstraints = { @UniqueConstraint(columnNames = "username"), @UniqueConstraint(columnNames = "email")})public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@Column(nullable = false, unique = true, length = 50) private String username;
@Column(nullable = false, unique = true, length = 100) private String email;
@Column(nullable = false, updatable = false) private LocalDateTime createdAt;
@PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); }
// Getters and setters}Generated Controller Example
package com.example.usermanagement.controller;
import com.example.usermanagement.dto.UserDTO;import com.example.usermanagement.service.UserService;import io.swagger.v3.oas.annotations.Operation;import io.swagger.v3.oas.annotations.responses.ApiResponse;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.*;
@RestController@RequestMapping("/api/users")public class UserController {
private final UserService userService;
public UserController(UserService userService) { this.userService = userService; }
@PostMapping @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Create a new user") @ApiResponse(responseCode = "201", description = "User created successfully") public UserDTO createUser(@RequestBody UserDTO userDTO) { return userService.create(userDTO); }
@GetMapping("/{id}") @Operation(summary = "Get user by ID") @ApiResponse(responseCode = "200", description = "User found") @ApiResponse(responseCode = "404", description = "User not found") public UserDTO getUser(@PathVariable Long id) { return userService.findById(id); }
@GetMapping @Operation(summary = "List all users with pagination") public Page<UserDTO> listUsers(Pageable pageable) { return userService.findAll(pageable); }
@PutMapping("/{id}") @Operation(summary = "Update an existing user") public UserDTO updateUser(@PathVariable Long id, @RequestBody UserDTO userDTO) { return userService.update(id, userDTO); }
@DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) @Operation(summary = "Delete a user") public void deleteUser(@PathVariable Long id) { userService.delete(id); }}Generated Flyway Migration
CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100) NOT NULL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);
CREATE INDEX idx_users_username ON users(username);CREATE INDEX idx_users_email ON users(email);Generated Dockerfile
FROM maven:3.9-eclipse-temurin-17 AS buildWORKDIR /appCOPY pom.xml .RUN mvn dependency:go-offlineCOPY src ./srcRUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jreWORKDIR /appCOPY --from=build /app/target/*.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"]Why This Approach Works
I think the key benefits are:
Consistency: All entities follow the same architecture pattern. When I add a new entity, I don’t need to remember which annotations go on the DTO or how to structure the service layer.
Speed: I generate full CRUD for 10 entities in under a minute. When I need to iterate on the schema, I modify the YAML file and regenerate.
Best practices: The generated code follows Spring Boot conventions—proper separation of concerns, validation annotations, pagination support, and OpenAPI documentation.
Database flexibility: I can switch from PostgreSQL to MySQL by changing one line in the YAML configuration. The plugin adjusts the generated SQL migrations accordingly.
Version control: Flyway migrations are generated automatically, so database schema changes are tracked from the start.
When to Use Code Generation
I use code generation when:
- Building CRUD-heavy applications (admin panels, internal tools)
- Working with well-defined domain models
- Need to prototype quickly
- Want consistent architecture across entities
I stick to manual coding when:
- Business logic doesn’t fit standard CRUD patterns
- I need highly customized endpoints or workflows
- Performance optimization requires specific query patterns
- I’m learning Spring Boot fundamentals
Other Maven Plugins for Code Generation
There are other options, but they serve different purposes:
Spring Boot Maven Plugin: Official plugin for packaging and running Spring Boot apps. Not a code generator—just a build tool.
jOOQ Code Generator: Generates Java classes from database schema. Good for database-first projects, but only generates DAOs, not services or controllers.
MapStruct Generator: Annotation processor that generates mapper interfaces. I use it for entity-DTO mapping, but it doesn’t generate full CRUD applications.
OpenAPI Generator: Generates Spring controllers from OpenAPI specs. Works for API-first development, but doesn’t generate entities or database layers.
Spring CRUD Generator stands out because it generates the full stack from configuration—entities, DTOs, mappers, services, controllers, migrations, and Docker resources.
Summary
In this post, I showed how to use Spring CRUD Generator to automate Spring Boot CRUD code generation. The key point is that defining entities in YAML configuration and generating all layers consistently saves time and reduces boilerplate errors.
Instead of writing 300+ lines of repetitive code per entity, I define the data model once and let the Maven plugin generate entities, DTOs, services, controllers, migrations, API documentation, and Docker configuration in seconds.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Spring CRUD Generator GitHub
- 👨💻 MapStruct Documentation
- 👨💻 Flyway Database Migrations
- 👨💻 OpenAPI 3.0 Specification
- 👨💻 Spring Boot Official Documentation
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments