
Crud Rest Controller
- 205 installs
- 105 repo stars
- Updated July 27, 2026
- amplicode/spring-skills
Drop in correct Spring constructor or field injection snippets when scaffolding CRUD REST controllers in Java or Kotlin.
About
crud-rest-controller is a template skill from the Amplicode Spring skills pack. Solo builders shipping Spring Boot APIs use it when an agent is generating or extending a CRUD REST controller and needs consistent dependency injection without guessing Java versus Kotlin idioms. The skill specifies insert points—constructor parameters in Kotlin, fields and constructors in Java—and supplies copy-ready blocks for one bean, multiple beans, or field injection. It pairs naturally with other Spring CRUD skills in the same repo (endpoints, DTO mapping, exception handling) but stands alone as injection boilerplate. Use it whenever the controller class exists but beans are not yet wired; skip it if you already use a project-wide codegen or Lombok pattern that forbids manual snippets. Intermediate familiarity with Spring DI is assumed; the skill does not run tests or modify your build.
- Constructor injection templates for single or multiple beans (repository, mapper)
- Java and Kotlin variants with documented insert points in the controller body
- Optional @Autowired field-injection alternative when you explicitly want it
- Variable table for BEAN_FQN, fieldName, and ControllerName placeholders
Crud Rest Controller by the numbers
- 205 all-time installs (skills.sh)
- Ranked #26 of 89 Java & JVM skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/amplicode/spring-skills --skill crud-rest-controllerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 205 |
|---|---|
| repo stars | ★ 105 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | amplicode/spring-skills ↗ |
What it does
Drop in correct Spring constructor or field injection snippets when scaffolding CRUD REST controllers in Java or Kotlin.
Files
Preflight: Spring MCP
This skill is part of the Spring Agent Toolkit and is designed to work with the Spring MCP server (provided by the Amplicode IntelliJ plugin). Before doing anything else, check your tool list for any Spring MCP tool — they are exposed under the amplicode MCP server (e.g. get_project_summary, list_module_dependencies, get_entity_details); harnesses that flatten MCP tools into the tool list use the mcp__amplicode__ prefix on the same names.
- If at least one Amplicode tool is available — MCP is connected. Proceed with the skill below.
- If none are available — stop and invoke the `amplicode-install` skill (bundled with the Spring Agent Toolkit). It installs the Amplicode plugin and walks the user through the «Настроить Spring Agent» welcome-screen button + MCP-client restart. After it completes, the MCP tools become available — resume this skill.
- If
amplicode-installis not registered in your skill list, tell the user (in their language): "This skill needs the Amplicode IntelliJ plugin and its MCP server. Install it from https://amplicode.ru/marketplace into IntelliJ IDEA Ultimate/Community or GigaIDE, open any project, click «Настроить Spring Agent» on the Amplicode welcome screen, then restart your MCP client."
---
CRUD REST Controller
Generates a @RestController class with standard CRUD endpoints for an entity, using a Spring Data repository, optional DTO mapping, pagination, filtering, and patch support.
---
CRITICAL: Code ONLY from examples/ files. If no matching example -- STOP and ask user.
CRITICAL: For questions with a fixed set of choices, prefer `AskUserQuestion` > its analogue > plain text list. Plain numbered text lists are the last resort when no interactive tool is available.
CRITICAL: Read the conversation context BEFORE running Step 1. Half the questions in Steps 2–4 may already be answered by the user's prompt and prior turns. Re-asking what was already said is the #1 reason this skill feels slow.
---
Defaults
The options below are grouped by topic. language and bootVersion are auto-detected; all other options are resolved via the Decision-making principle (derive from context → confirm → ask).
Block 1 — Entity & repository
| Option | Default | Notes |
|---|---|---|
| entity | -- | which entity to create controller for |
| repository | first existing for entity | which repository to use |
Block 2 — DTO mode
| Option | Default | Notes |
|---|---|---|
| DTO mode | DTO (recommended) | if no existing DTO for entity, delegate to dto-creator; user can opt out to use entity directly |
Block 3 — Controller naming & paths
| Option | Default | Notes |
|---|---|---|
| controllerName | {EntityName}Controller | suggest based on project naming convention |
| controllerPackage | same as existing controllers or mainPackage | auto-detected from project |
| basePath | /rest | persistent base path |
| resourcePath | /{pluralizedEntityName} | auto from entity name |
Block 4 — Pagination & filtering
| Option | Default | Notes |
|---|---|---|
| pagination | true | enable pagination for GET_LIST |
| paginationType | PAGE | PAGE or WINDOW (WINDOW requires filter to be selected; avoids count query overhead) |
| filter | None | JPA Specification filter for GET_LIST |
Auto-detected (no questions)
| Option | Source |
|---|---|
| patchStrategy | ObjectMapper |
| language | get_project_summary |
| bootVersion | get_project_summary |
Smart defaults: If user says "use defaults", "all defaults", "default settings", or similar -- skip ALL questions where "Always ask?" = NO. Only ask mandatory questions.
Smart answer recognition: When user provides a value instead of choosing from a numbered list, accept it directly. Examples:
- Question "Which entity?" --> user answers "Product" --> this IS the entity, don't re-ask
- Question "Repository?" --> user answers "ProductRepository" --> this IS the choice
- If user provides multiple answers in one message --> accept all, skip answered questions
- NEVER ask a question that the user already answered (even implicitly)
Batch questions: When multiple questions must be asked (i.e. cannot be resolved by principles 1–2 of the Decision-making principle), group them into a single AskUserQuestion call (up to 4 questions per call) when they:
- Belong to the same logical section
- Don't depend on each other's answers
Rules:
- Maximum 3-4 questions per
AskUserQuestioncall - Mark the recommended option with
(Recommended)and place it first - Never batch questions from DIFFERENT decision branches
- The primary branching question (entity selection) is always asked ALONE
- Prefer
AskUserQuestionfor choices; fall back to plain text lists only if the tool is unavailable
---
Decision-making principle — context first, then ask
Before asking the user any question, attempt to derive the answer from the context already gathered: project summary, module dependencies, entity details, existing files in the package, prior turns of this conversation, and the user's original prompt. Only ask when the context yields no clear default or when the choice is genuinely user-specific (e.g. which entity, which repository).
Hierarchy of decisions:
1. Context is unambiguous → decide silently, do NOT ask. Examples: language and JDK from get_project_summary; repository when there is exactly one for the entity; controller package from existing controllers; basePath from existing endpoints; Jackson version from list_module_dependencies; pagination type when project already uses one consistently.
2. Context gives a strong signal → state the decision + alternatives in one line, let the user override or stay silent. Format:
Will create `ProductController` at `/rest/products` with Page pagination, no DTO.
Alternatives: use DTO, Window pagination, custom paths. OK?The user can answer "ok" / "yes" / silence → accept; or name an alternative → switch. This is not the same as the numbered question format — it is a single confirmation line.
3. Context yields no clear default → ask with `AskUserQuestion` (preferred) or its analogue, with the recommended option first. When AskUserQuestion is available, use it with the recommended option marked (Recommended) and placed first. If no interactive choice tool is available, fall back to a plain text list. Never ask iteratively ("which entity?" → user picks → "repository?" → …) when one batched call would do.
4. Context is fully empty for a critical input → ask plainly. This applies to: which entity (when not mentioned), the user's intent itself.
How to ask — prefer AskUserQuestion
When a question must be asked, prefer the `AskUserQuestion` tool (or its analogue) over writing a numbered list in the response body. Fall back to plain text only if no interactive choice tool is available.
Rules for AskUserQuestion calls in this skill:
- Each call may contain up to 4 questions that are independent of each
other (the tool will render them together). Use this to batch related decisions in one round-trip.
- Each question has 2–4 options. The tool auto-adds an "Other" choice
for free-form input — never include it manually.
- Mark the recommended option by putting it first with
(Recommended)
appended to the label.
headeris a 12-char chip label (e.g. "Entity", "DTO mode", "Paths").- Each option has a
descriptionexplaining what the choice means.
When AskUserQuestion is not the right tool:
- Free-form input where there is no enumerable set of options
(e.g. arbitrary class name, arbitrary path) — ask in plain text.
- The "single confirmation line" form from principle 2 — that is a plain
question with an obvious yes/no, not an enumerated choice.
The screen-driven question lists in Steps 2–4 below are a fallback for case 4. They are NOT a script to execute top-to-bottom. If a question's answer is already determined by principles 1–3, skip the question.
---
Step 0 -- Conversation context first (REQUIRED, no tool calls)
Before any MCP call, before any question, re-read the user's prompt and the prior turns of this conversation and extract whatever is already stated. This step costs nothing and prevents the most common failure mode of this skill — asking the user something they already said.
Build a mental checklist of inputs and tick off everything the user has already provided, explicitly or implicitly:
| Input | Look for in the prompt / context |
|---|---|
| entity | a class name (Product, Order, Vet); "for X"; an open file in the IDE; a file path; a recently discussed entity in this conversation |
| repository | "use ProductRepository", "with OrderRepo"; or implied — if only one repository exists for the entity |
| DTO mode | "with DTO", "without DTO", "use entity directly", "map to ProductDto" |
| controller name | "name it ProductResource", "class FooController" |
| paths | "at /api/products", "base path /rest", "resource path /items" |
| pagination | "with pagination", "no pagination", "use Window", "use Page" |
| filter | "with filter", "no filter", "use JPA Specification" |
| methods | "all CRUD", "only read", "read-only", "without delete", "GET + CREATE", "full CRUD" |
| smart defaults | "use defaults", "all defaults", "default settings", "as usual" |
| prior project facts | language, JDK, dependencies — already known if discussed earlier in this conversation; do not re-fetch |
For every input that is explicitly or strongly implicitly answered: mark it as decided and skip the corresponding question in Steps 2–4. Do NOT ask "which entity?" if the user wrote "create CRUD controller for Product" — Product is the answer. Do NOT ask "which repository?" if there is exactly one repository for the entity.
For every input that is not answered: defer to the Decision-making principle above — try to derive it from project context first (Step 1), and only then ask.
Step 0 is mental, not a tool call. Do not announce it to the user. Do not write "Step 0 done". Just internalize what the user already said before proceeding to Step 1.
---
Step 1 -- Gather minimal project context (automatic, no questions)
Call only the MCP tools whose result is actually consumed by a later step. Do not pre-fetch "in case we need it" — every variable here must have a concrete downstream user.
| Tool | Variable | Used for |
|---|---|---|
get_project_summary | language, bootMajor, moduleName, buildFile, mainPackage | language → reference file selection (java vs kotlin); bootMajor → jakarta vs javax, Jackson version; moduleName → multi-module disambiguation; buildFile → Step 6 dependency injection; mainPackage → controller package fallback |
list_module_dependencies(moduleName) | presentDeps | derived flags below — SpringDoc/Validation/Jackson feature gates, Step 6 dependency check |
list_project_endpoints | endpoints | Step 4 path conflict detection, existing basePath/naming convention detection |
That is the entire Step 1. Do NOT fetch:
list_all_domain_entities— needed only if the user did not name an
entity in their prompt. Defer to Step 2 as a lazy fallback.
list_entity_repositories— depends on knowing the entity, which
happens in Step 2. Defer to Step 3 as a lazy fallback.
get_entity_details— depends on knowing the entity. Defer to Step 2.list_entity_dtos/list_entity_mappers— depend on knowing the
entity AND the DTO decision. Defer to Step 4.
After calling get_project_summary, determine:
language: Java or KotlinbootMajor: 2, 3 or 4 (for jakarta vs javax, Jackson version)- Check if
org.springdoc:springdoc-openapiis inpresentDeps-->hasSpringDoc - Check if
org.hibernate.validator:hibernate-validatorororg.springframework.boot:spring-boot-starter-validationis inpresentDeps-->hasValidation - Jackson version detection: Check
presentDepsfortools.jackson:jackson-databindortools.jackson:jackson-core-->jacksonMajor = 3. Otherwise ifcom.fasterxml.jackson:jackson-databind-->jacksonMajor = 2. Spring Boot 4.x uses Jackson 3.x (tools.jacksonpackage), Spring Boot 2.x/3.x use Jackson 2.x (com.fasterxml.jacksonpackage). - If
jacksonMajor = 3:JsonNodeFqn=tools.jackson.databind.JsonNode,ObjectMapperFqn=tools.jackson.databind.ObjectMapper - If
jacksonMajor = 2:JsonNodeFqn=com.fasterxml.jackson.databind.JsonNode,ObjectMapperFqn=com.fasterxml.jackson.databind.ObjectMapper - PagedModel detection:
PagedModel(org.springframework.data.web.PagedModel) is available in Spring Data 3.2+. IfbootMajor >= 3(Spring Boot 3.2+) orbootMajor = 4, usePagedModelwrapper for GET_LIST with Page pagination. Otherwise returnPage<Entity>directly.
If multi-module project (multiple modules in get_project_summary): Ask which module to use. Then re-call module-specific MCP tools with that module.
---
Step 2 -- Select entity
By Step 0 you should already know the entity if the user mentioned it. Most common case: the user wrote "create CRUD controller for Product" → entity is Product, skip the question, go straight to the fetch below.
Lazy fallback — only when entity is unknown: call list_all_domain_entities(moduleName) → entities, then ask via AskUserQuestion (options = entity names from the list, max 4; if more than 4 entities, use the 4 most likely candidates based on context and add a note that the user can type a different name via "Other").
This is the only place list_all_domain_entities should be called. If the user named the entity in their prompt, do NOT call it.
After the entity FQN is known, call get_entity_details(entityFqn) to get:
IdType-- entity ID field type (e.g.java.lang.Long,java.util.UUID)entityVar-- decapitalized entity name (e.g.product)entityVarPlural-- pluralized decapitalized name (e.g.products)EntityName-- simple class name (e.g.Product)EntityNamePlural-- pluralized simple name (e.g.Products)- Whether entity has validation annotations -->
entityHasValidation
---
Step 3 -- Select repository
By Step 0 you may already know the repository if the user mentioned it.
Call list_entity_repositories(entityFqn) → repos. Then apply the Decision-making principle:
- If exactly one repository exists → select it silently (principle 1).
- If the user named a specific repository → use it directly, skip the question.
- If multiple repositories exist → ask via
AskUserQuestionwith the
options from repos.
- If no repository exists → inform the user and suggest creating one
manually or using a separate skill.
After selection:
repoFieldName-- decapitalized repository class name (e.g.productRepository)RepoFqn-- FQN of repository class
---
Step 4 -- DTO and customization questions
By Step 0 you may already know the DTO mode if the user mentioned it (e.g. "with DTO", "without DTO", "use entity directly", "map to ProductDto"). If so, skip the question and proceed.
If unknown, ask via AskUserQuestion:
| Question | Header | Options (first = recommended) |
|---|---|---|
| Use DTO for mapping? | DTO mode | Yes, use DTO (Recommended): select existing or create new via dto-creator / No, use entity directly |
If DTO selected:
Call list_entity_dtos(entityFqn) and list_entity_mappers(entityFqn).
If existing DTO + mapper found: ask user to select them. Extract:
DtoFqn-- DTO class FQNdtoVar-- decapitalized DTO namemapperFieldName-- decapitalized mapper class nametoDtoMethodName-- mapper method entity-->DTO (e.g.toDto)toEntityMethodName-- mapper method DTO-->entity (e.g.toEntity)updateMethodName-- mapper method for update (e.g.updateWithNullValues)
Warn: CREATE and PATCH operations require the mapper to have toEntity and updateWithNull methods.
If no DTO exists for the entity: delegate to dto-creator to create one. The dto-creator skill will handle DTO generation and will automatically delegate to mapper-creator for the mapper (since the DTO is for a REST controller — conversion is inevitable). After both are created, return here and continue with Step 4 path settings.
If DTO exists but no mapper: delegate to mapper-creator to create one. After the mapper is created, return here and continue.
Path, pagination & filter settings
Apply the Decision-making principle. These settings almost always have good defaults derivable from context — use principle 2 (one-line confirmation) unless the user explicitly asked for customization:
Will create `{EntityName}Controller` in `{controllerPackage}` at `{basePath}/{entityVarPlural}`, Page pagination, no filter. OK?The user can answer "ok" / "yes" / silence → accept all defaults; or override specific values → apply only those overrides.
Only fall back to individual questions when the user explicitly asked for fine-grained control ("custom paths", "configure pagination") or when context yields no clear defaults.
If the user selects a filter: ask to select existing filter class. Extract FilterFqn and toSpecificationMethodName. If the repository does not extend JpaSpecificationExecutor, add it (WA-repo-enhance).
Method selection
By Step 0 you may already know which methods the user wants (e.g. "read-only", "only GET and CREATE", "full CRUD"). If so, skip the question.
If unknown, ask via AskUserQuestion:
| Question | Header | Options (first = recommended) |
|---|---|---|
| Which CRUD methods to generate? | Methods | Full CRUD (Recommended): GET_LIST, GET_ONE, GET_MANY, CREATE, PATCH, PATCH_MANY, DELETE, DELETE_MANY / Standard CRUD: GET_LIST, GET_ONE, CREATE, PATCH, DELETE / Read-only: GET_LIST, GET_ONE / Custom: select individual methods |
Store the selected method set as selectedMethods. Step 5.3 generates only these methods, skipping the rest.
---
Step 5 -- Generate code
5.1 Create controller class (WA1)
Read examples/_skeletons/java.md or examples/_skeletons/kotlin.md based on language.
Apply variable substitutions:
{packageName}--> controllerPackage{className}--> controllerName{requestPath}--> basePath + resourcePath
Use Write tool to create src/main/{java,kotlin}/{package-path}/{controllerName}.{java,kt}.
5.2 Add bean injection (WA2)
Read examples/_beans/injection/java.md or kotlin.md.
Add constructor parameter for repository. If DTO with mapper: also inject mapper bean. Inject ${ObjectMapperFqn} field (resolved in Step 1: tools.jackson.databind.ObjectMapper for Jackson 3.x, com.fasterxml.jackson.databind.ObjectMapper for Jackson 2.x).
Use Edit tool to modify the controller class.
5.3 Add CRUD methods (WA3-WA11)
For each method in selectedMethods (from Step 4 method selection):
1. Determine the example file path: examples/_methods/{method-name}/{language}.md
2. Read the example file
3. Select the correct code variant based on:
- DTO mode (no DTO vs with DTO)
- For GET_LIST: pagination (PAGE / WINDOW / none) and filter (with/without)
- For PATCH/PATCH_MANY: ObjectMapper vs ObjectPatcher, and DTO vs no-DTO (4 combinations)
- For CREATE: with/without @Valid
4. Apply variable substitutions (ONLY variables declared in the Variables section)
5. FQN handling (CRITICAL): examples contain FQNs (e.g. org.springframework.web.bind.annotation.GetMapping, org.springframework.data.domain.Pageable, entity/DTO/repository FQNs). When writing the final file, you MUST: 1. Replace every FQN in the body with its short name 2. Collect every FQN you shortened and emit a corresponding import line right after the package statement, sorted, no duplicates 3. Classes from the same package as the controller must NOT be imported 4. Types from java.lang must NOT be imported 5. Kotlin: same rules — shorten in the body and add import lines at the top
6. Use Edit tool to insert the method into the controller class body
5.4 Add JpaSpecificationExecutor if needed (WA-repo-enhance)
If filter is selected and repository does not already extend JpaSpecificationExecutor:
Read examples/_beans/repo-enhance/java.md or kotlin.md. Use Edit tool to add org.springframework.data.jpa.repository.JpaSpecificationExecutor<{EntityFqn}> to the repository's extends list.
---
Step 6 -- Dependencies & properties (automatic)
1. Read examples/_dependencies/dependencies.md 2. For each artifact NOT in presentDeps:
- Use
buildFilefrom Step 1 - Edit the build file to add the dependency
3. Call refresh_build_system_model 4. No properties are written for this skill (controller has no application.properties entries) 5. Report: "Created controller {controllerName} with CRUD endpoints for {EntityName}. Added dependencies: [list]."
---
Anti-hallucination checklist
Before writing ANY code, verify:
- [ ] The code comes from an examples/ file (cite which one)
- [ ] Only declared variables were substituted
- [ ] No framework API calls were added "from knowledge"
- [ ] Import list covers all FQNs shortened in the body (no missing imports, no extras)
- [ ] Method signatures match the example exactly
- [ ] No comments or convenience methods were added
- [ ] FQNs from examples are shortened in the body AND corresponding
importlines were added afterpackage(IDE will NOT do this for you) - [ ]
@ParameterObjectannotation is only added whenhasSpringDoc = true - [ ]
@Validannotation is only added whenhasValidation = trueAND entity has validation annotations - [ ] PATCH and PATCH_MANY methods use ObjectMapper (fallback) or ObjectPatcher depending on project dependencies
- [ ] getId() in PATCH_MANY matches entity's actual ID accessor
- [ ] Jackson FQNs use
tools.jackson.databind.*for Jackson 3.x (Spring Boot 4.x) andcom.fasterxml.jackson.databind.*for Jackson 2.x (Spring Boot 2.x/3.x). NEVER hardcode one or the other — always resolve${JsonNodeFqn}and${ObjectMapperFqn}from Step 1. - [ ] GET_LIST with Page pagination uses
PagedModel<Entity>wrapper (Spring Data 3.2+ / Boot 3.2+/4.x) or returnsPage<Entity>directly (older versions). Select the correct example variant.
Bean injection into controller (Java)
Insert Point
Field and constructor parameter in the controller class body.
Code
defaults
Constructor injection (single bean -- repository):
private final ${BEAN_FQN} ${fieldName};
public ${ControllerName}(${BEAN_FQN} ${fieldName}) {
this.${fieldName} = ${fieldName};
}multiple beans (repository + mapper)
private final ${BEAN_FQN_1} ${fieldName1};
private final ${BEAN_FQN_2} ${fieldName2};
public ${ControllerName}(${BEAN_FQN_1} ${fieldName1}, ${BEAN_FQN_2} ${fieldName2}) {
this.${fieldName1} = ${fieldName1};
this.${fieldName2} = ${fieldName2};
}field injection (alternative)
@org.springframework.beans.factory.annotation.Autowired
private ${BEAN_FQN} ${fieldName};Variables
| Variable | Source | Default |
|---|---|---|
${BEAN_FQN} | FQN of the bean class (repository, mapper, ObjectPatcher, ObjectMapper) | -- |
${fieldName} | decapitalized class name | -- |
${ControllerName} | controller class name | -- |
Bean injection into controller (Kotlin)
Insert Point
Primary constructor parameter in the controller class declaration.
Code
defaults
Constructor injection (single bean -- repository):
class ${ControllerName}(
private val ${fieldName}: ${BEAN_FQN}
)multiple beans (repository + mapper)
class ${ControllerName}(
private val ${fieldName1}: ${BEAN_FQN_1},
private val ${fieldName2}: ${BEAN_FQN_2}
)Variables
| Variable | Source | Default |
|---|---|---|
${BEAN_FQN} | FQN of the bean class (repository, mapper, ObjectPatcher, ObjectMapper) | -- |
${fieldName} | decapitalized class name | -- |
${ControllerName} | controller class name | -- |
Add JpaSpecificationExecutor to repository (Java)
Insert Point
Add org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityFqn}> to the repository interface extends list.
Code
defaults
Skip this fragment -- only applied when ALL of: (1) filter is selected, (2) entity is a JPA entity (has @Entity annotation), (3) repo does not already extend JpaSpecificationExecutor.
when filter selected
public interface ${RepoName} extends org.springframework.data.jpa.repository.JpaRepository<${EntityFqn}, ${IdType}>, org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityFqn}> {
}Variables
| Variable | Source | Default |
|---|---|---|
${RepoName} | repository interface name | -- |
${EntityFqn} | entity FQN | -- |
${IdType} | entity ID type | -- |
Add JpaSpecificationExecutor to repository (Kotlin)
Insert Point
Add org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityFqn}> to the repository interface extends list.
Code
defaults
Skip this fragment -- only applied when ALL of: (1) filter is selected, (2) entity is a JPA entity (has @Entity annotation), (3) repo does not already extend JpaSpecificationExecutor.
when filter selected
interface ${RepoName} : org.springframework.data.jpa.repository.JpaRepository<${EntityFqn}, ${IdType}>, org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityFqn}>Variables
| Variable | Source | Default |
|---|---|---|
${RepoName} | repository interface name | -- |
${EntityFqn} | entity FQN | -- |
${IdType} | entity ID type | -- |
Dependencies
| Artifact ID | Group ID | Scope | Condition |
|---|---|---|---|
| spring-boot-starter-web | org.springframework.boot | implementation | always |
| spring-boot-starter-data-jpa | org.springframework.boot | implementation | always |
| amplicode-ra-utils | io.amplicode | implementation | when ObjectPatcher patch strategy selected |
| spring-boot-starter-validation | org.springframework.boot | implementation | when entity has validation annotations |
Gradle Kotlin DSL
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
// conditional:
implementation("io.amplicode:amplicode-ra-utils")
implementation("org.springframework.boot:spring-boot-starter-validation")Gradle Groovy
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// conditional:
implementation 'io.amplicode:amplicode-ra-utils'
implementation 'org.springframework.boot:spring-boot-starter-validation'Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- conditional: -->
<dependency>
<groupId>io.amplicode</groupId>
<artifactId>amplicode-ra-utils</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>CREATE method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO, no validation:
@org.springframework.web.bind.annotation.PostMapping
public ${EntityFqn} create(@org.springframework.web.bind.annotation.RequestBody ${EntityFqn} ${entityVar}) {
return ${repoFieldName}.save(${entityVar});
}no DTO, with @Valid
@org.springframework.web.bind.annotation.PostMapping
public ${EntityFqn} create(@org.springframework.web.bind.annotation.RequestBody @jakarta.validation.Valid ${EntityFqn} ${entityVar}) {
return ${repoFieldName}.save(${entityVar});
}with DTO
@org.springframework.web.bind.annotation.PostMapping
public ${DtoFqn} create(@org.springframework.web.bind.annotation.RequestBody ${DtoFqn} dto) {
${EntityFqn} ${entityVar} = ${mapperFieldName}.${toEntityMethodName}(dto);
${EntityFqn} result${EntityName} = ${repoFieldName}.save(${entityVar});
return ${mapperFieldName}.${toDtoMethodName}(result${EntityName});
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${EntityName} | entity simple name | -- |
${DtoFqn} | DTO FQN | -- |
${entityVar} | decapitalized entity name | -- |
${repoFieldName} | repository field name | -- |
${mapperFieldName} | mapper field name | -- |
${toEntityMethodName} | mapper DTO->entity method | toEntity |
${toDtoMethodName} | mapper entity->DTO method | toDto |
CREATE method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.PostMapping
fun create(@org.springframework.web.bind.annotation.RequestBody ${entityVar}: ${EntityFqn}): ${EntityFqn} =
${repoFieldName}.save(${entityVar})with @Valid (no DTO)
@org.springframework.web.bind.annotation.PostMapping
fun create(@org.springframework.web.bind.annotation.RequestBody @jakarta.validation.Valid ${entityVar}: ${EntityFqn}): ${EntityFqn} =
${repoFieldName}.save(${entityVar})with DTO
@org.springframework.web.bind.annotation.PostMapping
fun create(@org.springframework.web.bind.annotation.RequestBody dto: ${DtoFqn}): ${DtoFqn} {
var ${entityVar} = ${mapperFieldName}.${toEntityMethodName}(dto)
${entityVar} = ${repoFieldName}.save(${entityVar})
return ${mapperFieldName}.${toDtoMethodName}(${entityVar})
}with @Valid and DTO
@org.springframework.web.bind.annotation.PostMapping
fun create(@org.springframework.web.bind.annotation.RequestBody @jakarta.validation.Valid dto: ${DtoFqn}): ${DtoFqn} {
var ${entityVar} = ${mapperFieldName}.${toEntityMethodName}(dto)
${entityVar} = ${repoFieldName}.save(${entityVar})
return ${mapperFieldName}.${toDtoMethodName}(${entityVar})
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${entityVar} | decapitalized entity name | -- |
${repoFieldName} | repository field name | -- |
${mapperFieldName} | mapper field name | -- |
${toEntityMethodName} | mapper DTO->entity method | toEntity |
${toDtoMethodName} | mapper entity->DTO method | toDto |
DELETE_MANY method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
@org.springframework.web.bind.annotation.DeleteMapping
public void deleteMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids) {
${repoFieldName}.deleteAllById(ids);
}Variables
| Variable | Source | Default |
|---|---|---|
${IdType} | entity ID type (boxed) | -- |
${repoFieldName} | repository field name | -- |
DELETE_MANY method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
@org.springframework.web.bind.annotation.DeleteMapping
fun deleteMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>) {
${repoFieldName}.deleteAllById(ids)
}Variables
| Variable | Source | Default |
|---|---|---|
${IdType} | entity ID type (boxed) | -- |
${repoFieldName} | repository field name | -- |
DELETE method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.DeleteMapping("/{id}")
public ${EntityFqn} delete(@org.springframework.web.bind.annotation.PathVariable ${IdType} id) {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id).orElse(null);
if (${entityVar} != null) {
${repoFieldName}.delete(${entityVar});
}
return ${entityVar};
}with DTO
@org.springframework.web.bind.annotation.DeleteMapping("/{id}")
public ${DtoFqn} delete(@org.springframework.web.bind.annotation.PathVariable ${IdType} id) {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id).orElse(null);
if (${entityVar} != null) {
${repoFieldName}.delete(${entityVar});
}
return ${mapperFieldName}.${toDtoMethodName}(${entityVar});
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${IdType} | entity ID type | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
DELETE method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.DeleteMapping("/{id}")
fun delete(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}): ${EntityFqn}? {
val ${entityVar} = ${repoFieldName}.findById(id).orElse(null)
if (${entityVar} != null) {
${repoFieldName}.delete(${entityVar})
}
return ${entityVar}
}with DTO
@org.springframework.web.bind.annotation.DeleteMapping("/{id}")
fun delete(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}): ${DtoFqn}? {
val ${entityVar} = ${repoFieldName}.findById(id).orElse(null)
if (${entityVar} != null) {
${repoFieldName}.delete(${entityVar})
}
return if (${entityVar} != null) ${mapperFieldName}.${toDtoMethodName}(${entityVar}) else null
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${IdType} | entity ID type | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
GET_LIST method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No filter, PAGE pagination, no DTO, springdoc available:
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.domain.Page<${EntityFqn}> getAll(@org.springdoc.core.annotations.ParameterObject org.springframework.data.domain.Pageable pageable) {
return ${repoFieldName}.findAll(pageable);
}no filter, PAGE pagination, with DTO
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.domain.Page<${DtoFqn}> getAll(@org.springdoc.core.annotations.ParameterObject org.springframework.data.domain.Pageable pageable) {
org.springframework.data.domain.Page<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll(pageable);
return ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName});
}with filter, PAGE pagination, no DTO
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.domain.Page<${EntityFqn}> getAll(@org.springframework.web.bind.annotation.ModelAttribute @org.springdoc.core.annotations.ParameterObject ${FilterFqn} filter, @org.springdoc.core.annotations.ParameterObject org.springframework.data.domain.Pageable pageable) {
org.springframework.data.jpa.domain.Specification<${EntityFqn}> spec = filter.${toSpecificationMethodName}();
return ${repoFieldName}.findAll(spec, pageable);
}no filter, no pagination, no DTO
@org.springframework.web.bind.annotation.GetMapping
public java.util.List<${EntityFqn}> getAll() {
return ${repoFieldName}.findAll();
}no filter, no pagination, with DTO
@org.springframework.web.bind.annotation.GetMapping
public java.util.List<${DtoFqn}> getAll() {
java.util.List<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll();
return ${entityVarPlural}.stream()
.map(${mapperFieldName}::${toDtoMethodName})
.toList();
}no filter, PAGE pagination, no DTO, no springdoc
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.domain.Page<${EntityFqn}> getAll(org.springframework.data.domain.Pageable pageable) {
return ${repoFieldName}.findAll(pageable);
}no filter, PAGE pagination (PagedModel), no DTO, springdoc available
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.web.PagedModel<${EntityFqn}> getAll(@org.springdoc.core.annotations.ParameterObject org.springframework.data.domain.Pageable pageable) {
org.springframework.data.domain.Page<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll(pageable);
return new org.springframework.data.web.PagedModel<>(${entityVarPlural});
}no filter, PAGE pagination (PagedModel), no DTO, no springdoc
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.web.PagedModel<${EntityFqn}> getAll(org.springframework.data.domain.Pageable pageable) {
org.springframework.data.domain.Page<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll(pageable);
return new org.springframework.data.web.PagedModel<>(${entityVarPlural});
}no filter, PAGE pagination (PagedModel), with DTO, no springdoc
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.web.PagedModel<${DtoFqn}> getAll(org.springframework.data.domain.Pageable pageable) {
org.springframework.data.domain.Page<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll(pageable);
org.springframework.data.domain.Page<${DtoFqn}> dtoPage = ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName});
return new org.springframework.data.web.PagedModel<>(dtoPage);
}with filter, PAGE pagination (PagedModel), no DTO
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.web.PagedModel<${EntityFqn}> getAll(@org.springframework.web.bind.annotation.ModelAttribute ${FilterFqn} filter, org.springframework.data.domain.Pageable pageable) {
org.springframework.data.jpa.domain.Specification<${EntityFqn}> spec = filter.${toSpecificationMethodName}();
org.springframework.data.domain.Page<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll(spec, pageable);
return new org.springframework.data.web.PagedModel<>(${entityVarPlural});
}with filter, PAGE pagination (PagedModel), with DTO
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.web.PagedModel<${DtoFqn}> getAll(@org.springframework.web.bind.annotation.ModelAttribute ${FilterFqn} filter, org.springframework.data.domain.Pageable pageable) {
org.springframework.data.jpa.domain.Specification<${EntityFqn}> spec = filter.${toSpecificationMethodName}();
org.springframework.data.domain.Page<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAll(spec, pageable);
org.springframework.data.domain.Page<${DtoFqn}> dtoPage = ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName});
return new org.springframework.data.web.PagedModel<>(dtoPage);
}with filter, WINDOW pagination, no DTO
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.domain.Window<${EntityFqn}> getAll(@org.springframework.web.bind.annotation.ModelAttribute ${FilterFqn} filter, org.springframework.data.domain.Pageable pageable) {
org.springframework.data.jpa.domain.Specification<${EntityFqn}> spec = filter.${toSpecificationMethodName}();
return ${repoFieldName}.findBy(spec, fluentQuery -> fluentQuery
.sortBy(pageable.getSort())
.limit(pageable.getPageSize())
.scroll(pageable.toScrollPosition()));
}with filter, WINDOW pagination, with DTO
@org.springframework.web.bind.annotation.GetMapping
public org.springframework.data.domain.Window<${DtoFqn}> getAll(@org.springframework.web.bind.annotation.ModelAttribute ${FilterFqn} filter, org.springframework.data.domain.Pageable pageable) {
org.springframework.data.jpa.domain.Specification<${EntityFqn}> spec = filter.${toSpecificationMethodName}();
org.springframework.data.domain.Window<${EntityFqn}> window = ${repoFieldName}.findBy(spec, fluentQuery -> fluentQuery
.sortBy(pageable.getSort())
.limit(pageable.getPageSize())
.scroll(pageable.toScrollPosition()));
return window.map(${mapperFieldName}::${toDtoMethodName});
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${repoFieldName} | repository field name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
${FilterFqn} | filter class FQN | -- |
${toSpecificationMethodName} | filter->Specification method | -- |
${entityVarPlural} | pluralized entity var | -- |
GET_LIST method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No filter, PAGE pagination, no DTO, springdoc available:
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springdoc.core.annotations.ParameterObject pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Page<${EntityFqn}> =
${repoFieldName}.findAll(pageable)no filter, PAGE pagination, with DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springdoc.core.annotations.ParameterObject pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Page<${DtoFqn}> {
val ${entityVarPlural}: org.springframework.data.domain.Page<${EntityFqn}> = ${repoFieldName}.findAll(pageable)
return ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName})
}no filter, no pagination, no DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(): List<${EntityFqn}> =
${repoFieldName}.findAll()no filter, PAGE pagination, no DTO, no springdoc
@org.springframework.web.bind.annotation.GetMapping
fun getAll(pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Page<${EntityFqn}> =
${repoFieldName}.findAll(pageable)no filter, no pagination, with DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(): List<${DtoFqn}> {
val ${entityVarPlural} = ${repoFieldName}.findAll()
return ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName})
}no filter, PAGE pagination (PagedModel), no DTO, no springdoc
@org.springframework.web.bind.annotation.GetMapping
fun getAll(pageable: org.springframework.data.domain.Pageable): org.springframework.data.web.PagedModel<${EntityFqn}> {
val ${entityVarPlural} = ${repoFieldName}.findAll(pageable)
return org.springframework.data.web.PagedModel(${entityVarPlural})
}no filter, PAGE pagination (PagedModel), with DTO, no springdoc
@org.springframework.web.bind.annotation.GetMapping
fun getAll(pageable: org.springframework.data.domain.Pageable): org.springframework.data.web.PagedModel<${DtoFqn}> {
val ${entityVarPlural} = ${repoFieldName}.findAll(pageable)
val dtoPage = ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName})
return org.springframework.data.web.PagedModel(dtoPage)
}with filter, PAGE pagination, no DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springframework.web.bind.annotation.ModelAttribute filter: ${FilterFqn}, pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Page<${EntityFqn}> {
val spec = filter.${toSpecificationMethodName}()
return ${repoFieldName}.findAll(spec, pageable)
}with filter, PAGE pagination (PagedModel), no DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springframework.web.bind.annotation.ModelAttribute filter: ${FilterFqn}, pageable: org.springframework.data.domain.Pageable): org.springframework.data.web.PagedModel<${EntityFqn}> {
val spec = filter.${toSpecificationMethodName}()
val ${entityVarPlural} = ${repoFieldName}.findAll(spec, pageable)
return org.springframework.data.web.PagedModel(${entityVarPlural})
}with filter, PAGE pagination (PagedModel), with DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springframework.web.bind.annotation.ModelAttribute filter: ${FilterFqn}, pageable: org.springframework.data.domain.Pageable): org.springframework.data.web.PagedModel<${DtoFqn}> {
val spec = filter.${toSpecificationMethodName}()
val ${entityVarPlural} = ${repoFieldName}.findAll(spec, pageable)
val dtoPage = ${entityVarPlural}.map(${mapperFieldName}::${toDtoMethodName})
return org.springframework.data.web.PagedModel(dtoPage)
}with filter, WINDOW pagination, no DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springframework.web.bind.annotation.ModelAttribute filter: ${FilterFqn}, pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Window<${EntityFqn}> {
val spec = filter.${toSpecificationMethodName}()
return ${repoFieldName}.findBy(spec) { fluentQuery ->
fluentQuery
.sortBy(pageable.sort)
.limit(pageable.pageSize)
.scroll(pageable.toScrollPosition())
}
}with filter, WINDOW pagination, with DTO
@org.springframework.web.bind.annotation.GetMapping
fun getAll(@org.springframework.web.bind.annotation.ModelAttribute filter: ${FilterFqn}, pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Window<${DtoFqn}> {
val spec = filter.${toSpecificationMethodName}()
val window = ${repoFieldName}.findBy(spec) { fluentQuery ->
fluentQuery
.sortBy(pageable.sort)
.limit(pageable.pageSize)
.scroll(pageable.toScrollPosition())
}
return window.map(${mapperFieldName}::${toDtoMethodName})
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${repoFieldName} | repository field name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
${FilterFqn} | filter class FQN | -- |
${toSpecificationMethodName} | filter->Specification method | -- |
${entityVarPlural} | pluralized entity var | -- |
GET_MANY method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.GetMapping("/by-ids")
public java.util.List<${EntityFqn}> getMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids) {
return ${repoFieldName}.findAllById(ids);
}with DTO
@org.springframework.web.bind.annotation.GetMapping("/by-ids")
public java.util.List<${DtoFqn}> getMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids) {
java.util.List<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAllById(ids);
return ${entityVarPlural}.stream()
.map(${mapperFieldName}::${toDtoMethodName})
.toList();
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${IdType} | entity ID type (boxed) | -- |
${repoFieldName} | repository field name | -- |
${entityVarPlural} | pluralized entity var | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
GET_MANY method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.GetMapping("/by-ids")
fun getMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>): List<${EntityFqn}> =
${repoFieldName}.findAllById(ids)with DTO
@org.springframework.web.bind.annotation.GetMapping("/by-ids")
fun getMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>): List<${DtoFqn}> {
val ${entityVarPlural} = ${repoFieldName}.findAllById(ids)
return ${entityVarPlural}.map { ${mapperFieldName}.${toDtoMethodName}(it) }
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${IdType} | entity ID type (boxed) | -- |
${repoFieldName} | repository field name | -- |
${entityVarPlural} | pluralized entity var | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
GET_ONE method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.GetMapping("/{id}")
public ${EntityFqn} getOne(@org.springframework.web.bind.annotation.PathVariable ${IdType} id) {
java.util.Optional<${EntityFqn}> ${entityVar}Optional = ${repoFieldName}.findById(id);
return ${entityVar}Optional.orElseThrow(() ->
new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id)));
}with DTO
@org.springframework.web.bind.annotation.GetMapping("/{id}")
public ${DtoFqn} getOne(@org.springframework.web.bind.annotation.PathVariable ${IdType} id) {
java.util.Optional<${EntityFqn}> ${entityVar}Optional = ${repoFieldName}.findById(id);
return ${mapperFieldName}.${toDtoMethodName}(${entityVar}Optional.orElseThrow(() ->
new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id))));
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${IdType} | entity ID type | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
GET_ONE method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO:
@org.springframework.web.bind.annotation.GetMapping("/{id}")
fun getOne(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}): ${EntityFqn} =
${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }with DTO
@org.springframework.web.bind.annotation.GetMapping("/{id}")
fun getOne(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}): ${DtoFqn} {
val ${entityVar} = ${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }
return ${mapperFieldName}.${toDtoMethodName}(${entityVar})
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${DtoFqn} | DTO FQN | -- |
${IdType} | entity ID type | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
PATCH_MANY method (Java)
Insert Point
As new method in controller class body, after last method.
Note on @Valid
When hasValidation = true, add @jakarta.validation.Valid before the ids parameter in all variants below. Example: @RequestParam @jakarta.validation.Valid java.util.List<${IdType}> ids
Code
defaults
No DTO, ObjectPatcher available:
@org.springframework.web.bind.annotation.PatchMapping
public java.util.List<${IdType}> patchMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) {
java.util.List<${EntityFqn}> ${entityVarPlural} = new java.util.ArrayList<>(${repoFieldName}.findAllById(ids));
${entityVarPlural}.replaceAll(${entityVar} -> objectPatcher.patchAndValidate(${entityVar}, patchNode));
java.util.List<${EntityFqn}> result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural});
return result${EntityNamePlural}.stream().map(${EntityFqn}::getId).toList();
}with DTO + mapper, ObjectPatcher available
@org.springframework.web.bind.annotation.PatchMapping
public java.util.List<${IdType}> patchMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) {
java.util.Collection<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAllById(ids);
for (${EntityFqn} ${entityVar} : ${entityVarPlural}) {
${DtoFqn} ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar});
${dtoVar} = objectPatcher.patchAndValidate(${dtoVar}, patchNode);
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar});
}
java.util.List<${EntityFqn}> result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural});
return result${EntityNamePlural}.stream().map(${EntityFqn}::getId).toList();
}with DTO + mapper, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping
public java.util.List<${IdType}> patchMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) throws java.io.IOException {
java.util.Collection<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAllById(ids);
for (${EntityFqn} ${entityVar} : ${entityVarPlural}) {
${DtoFqn} ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar});
objectMapper.readerForUpdating(${dtoVar}).readValue(patchNode);
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar});
}
java.util.List<${EntityFqn}> result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural});
return result${EntityNamePlural}.stream()
.map(${EntityFqn}::getId)
.toList();
}no DTO, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping
public java.util.List<${IdType}> patchMany(@org.springframework.web.bind.annotation.RequestParam java.util.List<${IdType}> ids, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) throws java.io.IOException {
java.util.Collection<${EntityFqn}> ${entityVarPlural} = ${repoFieldName}.findAllById(ids);
for (${EntityFqn} ${entityVar} : ${entityVarPlural}) {
objectMapper.readerForUpdating(${entityVar}).readValue(patchNode);
}
java.util.List<${EntityFqn}> result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural});
return result${EntityNamePlural}.stream()
.map(${EntityFqn}::getId)
.toList();
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${EntityNamePlural} | pluralized entity name | -- |
${DtoFqn} | DTO FQN | -- |
${dtoVar} | decapitalized DTO name | -- |
${IdType} | entity ID type (boxed) | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${entityVarPlural} | pluralized entity var | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
${JsonNodeFqn} | Jackson JsonNode FQN | com.fasterxml.jackson.databind.JsonNode (Jackson 2.x) or tools.jackson.databind.JsonNode (Jackson 3.x) |
PATCH_MANY method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Note on @Valid
When hasValidation = true, add @jakarta.validation.Valid before the ids parameter in all variants below. Example: @org.springframework.web.bind.annotation.RequestParam @jakarta.validation.Valid ids: List<${IdType}>
Code
defaults
No DTO, ObjectPatcher available:
@org.springframework.web.bind.annotation.PatchMapping
fun patchMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): List<${IdType}> {
val ${entityVarPlural} = kotlin.collections.ArrayList(${repoFieldName}.findAllById(ids))
${entityVarPlural}.replaceAll { ${entityVar} -> objectPatcher.patchAndValidate(${entityVar}, patchNode) }
val result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural})
return result${EntityNamePlural}.mapNotNull { it.id }
}with DTO + mapper, ObjectPatcher available
@org.springframework.web.bind.annotation.PatchMapping
fun patchMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): List<${IdType}> {
val ${entityVarPlural} = ${repoFieldName}.findAllById(ids)
for (${entityVar} in ${entityVarPlural}) {
var ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar})
${dtoVar} = objectPatcher.patchAndValidate(${dtoVar}, patchNode)
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar})
}
val result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural})
return result${EntityNamePlural}.mapNotNull { it.id }
}with DTO + mapper, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping
@Throws(java.io.IOException::class)
fun patchMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): List<${IdType}> {
val ${entityVarPlural} = ${repoFieldName}.findAllById(ids)
for (${entityVar} in ${entityVarPlural}) {
var ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar})
objectMapper.readerForUpdating(${dtoVar}).readValue<${DtoFqn}>(patchNode)
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar})
}
val result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural})
return result${EntityNamePlural}.mapNotNull { it.id }
}no DTO, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping
@Throws(java.io.IOException::class)
fun patchMany(@org.springframework.web.bind.annotation.RequestParam ids: List<${IdType}>, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): List<${IdType}> {
val ${entityVarPlural} = ${repoFieldName}.findAllById(ids)
for (${entityVar} in ${entityVarPlural}) {
objectMapper.readerForUpdating(${entityVar}).readValue<${EntityFqn}>(patchNode)
}
val result${EntityNamePlural} = ${repoFieldName}.saveAll(${entityVarPlural})
return result${EntityNamePlural}.mapNotNull { it.id }
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${EntityNamePlural} | pluralized entity name | -- |
${DtoFqn} | DTO FQN | -- |
${dtoVar} | decapitalized DTO name | -- |
${IdType} | entity ID type (boxed) | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${entityVarPlural} | pluralized entity var | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
${JsonNodeFqn} | Jackson JsonNode FQN | com.fasterxml.jackson.databind.JsonNode (Jackson 2.x) or tools.jackson.databind.JsonNode (Jackson 3.x) |
PATCH method (Java)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO, ObjectPatcher available:
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
public ${EntityFqn} patch(@org.springframework.web.bind.annotation.PathVariable ${IdType} id, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id)));
${entityVar} = objectPatcher.patchAndValidate(${entityVar}, patchNode);
return ${repoFieldName}.save(${entityVar});
}with DTO + mapper, ObjectPatcher available
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
public ${DtoFqn} patch(@org.springframework.web.bind.annotation.PathVariable ${IdType} id, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id)));
${DtoFqn} ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar});
${dtoVar} = objectPatcher.patchAndValidate(${dtoVar}, patchNode);
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar});
${EntityFqn} result${EntityName} = ${repoFieldName}.save(${entityVar});
return ${mapperFieldName}.${toDtoMethodName}(result${EntityName});
}no DTO, ObjectPatcher available, NO validation lib
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
public ${EntityFqn} patch(@org.springframework.web.bind.annotation.PathVariable ${IdType} id, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id)
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id)));
${entityVar} = objectPatcher.patch(${entityVar}, patchNode);
return ${repoFieldName}.save(${entityVar});
}no DTO, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
public ${EntityFqn} patch(@org.springframework.web.bind.annotation.PathVariable ${IdType} id, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) throws java.io.IOException {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id).orElseThrow(() ->
new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id)));
objectMapper.readerForUpdating(${entityVar}).readValue(patchNode);
return ${repoFieldName}.save(${entityVar});
}with DTO + mapper, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
public ${DtoFqn} patch(@org.springframework.web.bind.annotation.PathVariable ${IdType} id, @org.springframework.web.bind.annotation.RequestBody ${JsonNodeFqn} patchNode) throws java.io.IOException {
${EntityFqn} ${entityVar} = ${repoFieldName}.findById(id).orElseThrow(() ->
new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `%s` not found".formatted(id)));
${DtoFqn} ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar});
objectMapper.readerForUpdating(${dtoVar}).readValue(patchNode);
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar});
${EntityFqn} result${EntityName} = ${repoFieldName}.save(${entityVar});
return ${mapperFieldName}.${toDtoMethodName}(result${EntityName});
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${EntityName} | entity simple name | -- |
${DtoFqn} | DTO FQN | -- |
${dtoVar} | decapitalized DTO name | -- |
${IdType} | entity ID type | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
${JsonNodeFqn} | Jackson JsonNode FQN | com.fasterxml.jackson.databind.JsonNode (Jackson 2.x) or tools.jackson.databind.JsonNode (Jackson 3.x) |
PATCH method (Kotlin)
Insert Point
As new method in controller class body, after last method.
Code
defaults
No DTO, ObjectPatcher available:
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
fun patch(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): ${EntityFqn} {
var ${entityVar}: ${EntityFqn} = ${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }
${entityVar} = objectPatcher.patchAndValidate(${entityVar}, patchNode)
return ${repoFieldName}.save(${entityVar})
}with DTO + mapper, ObjectPatcher available
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
fun patch(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): ${DtoFqn} {
var ${entityVar}: ${EntityFqn} = ${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }
var ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar})
${dtoVar} = objectPatcher.patchAndValidate(${dtoVar}, patchNode)
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar})
val result${EntityName}: ${EntityFqn} = ${repoFieldName}.save(${entityVar})
return ${mapperFieldName}.${toDtoMethodName}(result${EntityName})
}no DTO, ObjectPatcher available, NO validation lib
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
fun patch(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): ${EntityFqn} {
var ${entityVar}: ${EntityFqn} = ${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }
${entityVar} = objectPatcher.patch(${entityVar}, patchNode)
return ${repoFieldName}.save(${entityVar})
}with DTO + mapper, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
@Throws(java.io.IOException::class)
fun patch(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): ${DtoFqn} {
val ${entityVar}: ${EntityFqn} = ${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }
var ${dtoVar} = ${mapperFieldName}.${toDtoMethodName}(${entityVar})
objectMapper.readerForUpdating(${dtoVar}).readValue<${DtoFqn}>(patchNode)
${mapperFieldName}.updateWithNull(${dtoVar}, ${entityVar})
val result${EntityName}: ${EntityFqn} = ${repoFieldName}.save(${entityVar})
return ${mapperFieldName}.${toDtoMethodName}(result${EntityName})
}no DTO, ObjectMapper fallback
@org.springframework.web.bind.annotation.PatchMapping("/{id}")
@Throws(java.io.IOException::class)
fun patch(@org.springframework.web.bind.annotation.PathVariable id: ${IdType}, @org.springframework.web.bind.annotation.RequestBody patchNode: ${JsonNodeFqn}): ${EntityFqn} {
val ${entityVar}: ${EntityFqn} = ${repoFieldName}.findById(id)
.orElseThrow { org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.NOT_FOUND, "Entity with id `$id` not found") }
objectMapper.readerForUpdating(${entityVar}).readValue<${EntityFqn}>(patchNode)
return ${repoFieldName}.save(${entityVar})
}Variables
| Variable | Source | Default |
|---|---|---|
${EntityFqn} | entity FQN | -- |
${EntityName} | entity simple name | -- |
${DtoFqn} | DTO FQN | -- |
${dtoVar} | decapitalized DTO name | -- |
${IdType} | entity ID type | -- |
${repoFieldName} | repository field name | -- |
${entityVar} | decapitalized entity name | -- |
${mapperFieldName} | mapper field name | -- |
${toDtoMethodName} | mapper entity->DTO method | toDto |
${JsonNodeFqn} | Jackson JsonNode FQN | com.fasterxml.jackson.databind.JsonNode (Jackson 2.x) or tools.jackson.databind.JsonNode (Jackson 3.x) |
Controller class skeleton (Java)
Code
package {packageName};
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
@RequestMapping("{requestPath}")
public class {className} {
}Variables
| Variable | Source | Default |
|---|---|---|
{packageName} | project context | mainPackage |
{className} | user choice | {EntityName}Resource |
{requestPath} | basePath + resourcePath | /rest/admin-ui/{entityVarPlural} |
Controller class skeleton (Kotlin)
Code
package {packageName}
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
@org.springframework.web.bind.annotation.RestController
@org.springframework.web.bind.annotation.RequestMapping("{requestPath}")
class {className}Variables
| Variable | Source | Default |
|---|---|---|
{packageName} | project context | mainPackage |
{className} | user choice | {EntityName}Resource |
{requestPath} | basePath + resourcePath | /rest/admin-ui/{entityVarPlural} |
Related skills
FAQ
Is Crud Rest Controller safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.