
Mapper Creator
Generate MapStruct @AfterMapping default methods that wire bidirectional OneToOne and OneToMany associations after DTO-to-entity mapping.
Overview
mapper-creator is an agent skill for the Build phase that generates MapStruct @AfterMapping methods to link inverse sides of OneToOne and OneToMany entity associations.
Install
npx skills add https://github.com/amplicode/spring-skills --skill mapper-creatorWhat is this skill?
- Inserts @AfterMapping default methods in mapper interface bodies at the documented insert point
- OneToMany template: forEach child with setInverse on mappedBy owner
- OneToOne template: null-safe link of inverse mappedBy attribute
- Variable table maps entityAttrName, FQN, inverseAttributeNameCapitalized from entity metadata
- Skips fragment when entity lacks non-owner OneToOne/OneToMany with mappedBy plus sub-DTO
- Two association templates: OneToMany and OneToOne
- Seven documented template variables in the variable table
Adoption & trust: 1 installs on skills.sh; 54 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
What problem does it solve?
Your MapStruct mapper copies fields but leaves bidirectional JPA associations unset, so persisted graphs violate ownership invariants.
Who is it for?
Solo JVM builders using MapStruct with Spring Data entities that use mappedBy on the non-owning association side.
Skip if: Greenfield projects on non-JVM stacks, mappers without entity associations, or cases where associations are read-only and never written from DTOs.
When should I use this skill?
Entity has non-owner OneToOne or OneToMany with mappedBy and you are editing or generating the MapStruct mapper interface.
What do I get? / Deliverables
Mapper interfaces gain default link methods that set inverse mappedBy references on children or optional OneToOne peers after each mapping target update.
- @AfterMapping default link methods inside the mapper interface
- Correct forEach or null-checked setInverse calls per association type
Recommended Skills
Journey fit
Entity–DTO mapping with correct inverse sides belongs in backend implementation during feature build, not launch or ops. MapStruct mappers are server-side Java persistence glue, squarely in backend API and domain modeling work.
How it compares
Targeted MapStruct fragment generator—not a full OpenAPI client skill or database migration tool.
Common Questions / FAQ
Who is mapper-creator for?
Independent developers and tiny teams on Spring plus MapStruct who want consistent association wiring in generated mapper interfaces.
When should I use mapper-creator?
During Build/backend work when adding or updating entity-to-DTO mappers that include OneToMany or owning-side OneToOne with mappedBy.
Is mapper-creator safe to install?
It outputs standard MapStruct Java; confirm trust via the Security Audits panel on this Prism catalog page.
SKILL.md
READMESKILL.md - Mapper Creator
# @AfterMapping method (Java) ## Insert Point Default method in mapper interface body. ## Code ```defaults skip this fragment (only added when entity has non-owner OneToOne/OneToMany with mappedBy + sub-DTO) ``` ### OneToMany association ```java @org.mapstruct.AfterMapping default void link{entityAttrNameCapitalized}(@org.mapstruct.MappingTarget {entityClassFqn} {entityParamName}) { {entityParamName}.get{entityAttrNameCapitalized}().forEach({unpluralizedAttrName} -> {unpluralizedAttrName}.set{inverseAttributeNameCapitalized}({entityParamName})); } ``` ### OneToOne association ```java @org.mapstruct.AfterMapping default void link{entityAttrNameCapitalized}(@org.mapstruct.MappingTarget {entityClassFqn} {entityParamName}) { {attrTypeEntityFqn} {entityAttrName} = {entityParamName}.get{entityAttrNameCapitalized}(); if ({entityAttrName} != null) { {entityAttrName}.set{inverseAttributeNameCapitalized}({entityParamName}); } } ``` ## Variables | Variable | Source | Default | |----------|--------|---------| | `{entityAttrName}` | entity association attribute name | e.g. `children`, `address` | | `{entityAttrNameCapitalized}` | capitalized | e.g. `Children`, `Address` | | `{entityParamName}` | decapitalized entity short name | e.g. `order` | | `{entityClassFqn}` | entity class FQN | — | | `{inverseAttributeNameCapitalized}` | capitalized mappedBy attribute | e.g. `Parent`, `Order` | | `{unpluralizedAttrName}` | unpluralized attr name (OneToMany only) | e.g. `child` | | `{attrTypeEntityFqn}` | association target entity FQN (OneToOne only) | — | # @AfterMapping method (Kotlin) ## Insert Point Function in mapper abstract class body. ## Code ```defaults skip this fragment (only added when entity has non-owner OneToOne/OneToMany with mappedBy + sub-DTO) ``` ### OneToMany association ```kotlin @org.mapstruct.AfterMapping fun link{entityAttrNameCapitalized}(@org.mapstruct.MappingTarget {entityParamName}: {entityClassFqn}) { {entityParamName}.{entityAttrName}.forEach { it.{inverseAttributeName} = {entityParamName} } } ``` ### OneToOne association ```kotlin @org.mapstruct.AfterMapping fun link{entityAttrNameCapitalized}(@org.mapstruct.MappingTarget {entityParamName}: {entityClassFqn}) { {entityParamName}.{entityAttrName}?.{inverseAttributeName} = {entityParamName} } ``` ## Variables | Variable | Source | Default | |----------|--------|---------| | `{entityAttrName}` | entity association attribute name | e.g. `children`, `address` | | `{entityAttrNameCapitalized}` | capitalized | e.g. `Children`, `Address` | | `{entityParamName}` | decapitalized entity short name | e.g. `order` | | `{entityClassFqn}` | entity class FQN | — | | `{inverseAttributeName}` | mappedBy attribute name | e.g. `parent`, `order` | # mapFromAggregateReference helper (Java) ## Insert Point Default method in mapper interface body. ## Code ```defaults skip this fragment (only for Spring Data JDBC entities with AggregateReference attributes) ``` ### When entity has AggregateReference attributes with ID sub-DTO type ```java default <T, R> R mapFromAggregateReference(org.springframework.data.jdbc.core.mapping.AggregateReference<T, R> aggregateReference) { if (aggregateReference == null) { return null; } return aggregateReference.getId(); } ``` ## Variables None (generic helper method). # mapFromAggregateReference helper (Kotlin) ## Insert Point Function in mapper abstract class body. ## Code ```defaults skip this fragment (only for Spring Data JDBC entities with AggregateReference attributes) ``` ### When entity has AggregateReference attributes with ID sub-DTO type ```kotlin fun <T, R> mapFromAggregateReference(aggregateReference: org.springframework.data.jdbc.core.mapping.AggregateReference<T, R>): R { return aggregateReference?.id } ``` ## Variables None (generic helper method). # mapToAggregateReference helper (Java) ## Insert Point Default method in mapper interface body. ## Code ```