I use Micronaut Data with JPA and have two entities. The first one is Recipe
:
@Entity
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@ManyToOne
private Category category;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Step> steps;
// + other fields, getters and setters
}
The second one is ParseError
which refers to Recipe
:
@Entity
@Table(name = "parse_error")
public class ParseError implements Serializable {
@Id
@ManyToOne(fetch = FetchType.LAZY)
private Recipe recipe;
@Id
@Enumerated(EnumType.ORDINAL)
@Column(name = "problem_area")
private ProblemArea problemArea;
private String message;
// + other fields, getters and setters
}
Now I would like to provide DTO in API with ParseError
properties but not with whole Recipe
entity because it contains ManyToOne and OneToMany relations which are not needed in this case. So I created projection DTO for that:
@Introspected
public class ParseErrorDto {
private Integer recipeId;
private String recipeName;
private ParseError.ProblemArea problemArea;
private String message;
// + getters and setters
}
And added listAll()
method into ParseErrorRepository
:
@Repository
public interface ParseErrorRepository extends CrudRepository<ParseError, Integer> {
List<ParseErrorDto> listAll();
}
But it seems that Micronaut Data is not able to project properties from nested entities or I missed something in the DTO or the repository method:
ParseErrorRepository.java:22: error: Unable to implement Repository method: ParseErrorRepository.listAll(). Property recipeId is not present in entity: ParseError
I also tried to create RecipeDto
:
@Introspected
public class RecipeDto {
private Integer id;
private String name;
// + getters and setters
}
And updated ParseErrorDto
accordingly:
@Introspected
public class ParseErrorDto {
private RecipeDto recipe;
private ParseError.ProblemArea problemArea;
private String message;
// + getters and setters
}
Again no success:
ParseErrorRepository.java:22: error: Unable to implement Repository method: ParseErrorRepository.listAll(). Property [recipe] of type [RecipeDto] is not compatible with equivalent property declared in entity: ParseError
Is Micronaut Data able to handle this use case by DTO projection? If not then is there another way how can I solve it in Micronaut Data?
Now (in latest version 1.0.0.M1) it is not possible. So I created feature request issue for that: https://github.com/micronaut-projects/micronaut-data/issues/184
Current workaround is to map entity bean into DTO bean in Java stream or reactive stream for example and do the properties mapping manually or by Mapstruct.
Update: Here is an answer to question from comments with an example how to do the workaround using Mapstruct:
Add Mapstruct dependency into build.gradle:
implementation "org.mapstruct:mapstruct:$mapstructVersion"
annotationProcessor "org.mapstruct:mapstruct-processor:$mapstructVersion"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:$mapstructVersion"
Define mapper:
import org.mapstruct.Mapper;
@Mapper(
componentModel = "jsr330"
)
public interface ParseErrorMapper {
ParseErrorDto entityToDto(@NotNull ParseError parseError);
EntityReference recipeToDto(@NotNull Recipe recipe);
}
And here is a usage of that mapper in the controller:
@Controller("/parse-error")
public class ParseErrorController {
private final ParseErrorRepository repository;
private final ParseErrorMapper mapper;
public ParseErrorController(ParseErrorRepository repository, ParseErrorMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}
@Get("all")
@Transactional
public Page<ParseErrorDto> getAll(final Pageable pageable) {
return repository.findAll(pageable).map(mapper::entityToDto);
}
}
Update (September 2023): feature implemented by https://github.com/micronaut-projects/micronaut-data/pull/2495