Я использую ModelMapper в своем приложении Spring Boot (Java 12) для сопоставления DatabaseEntities с DTO, которые будут использоваться для конечных точек REST.
Класс Recipe (Database Entity) содержит несколько переменных. Некоторые из них имеют собственный тип, так что они могут содержать несколько языков. Однако для конечной точки REST должен быть возвращен простой объект, который содержит только один язык. Поэтому у меня есть два подкласса: RecipeENG_DTO
и RecipeGER_DTO
, которые оба наследуют от своего суперкласса Recipe_DTO
.
Моя цель - сопоставить объект Recipe
с RecipeGER_DTO
объектом
Java Code
Конечная точка REST
@Autowired
private ModelMapper modelMapper;
@GetMapping(path = "/all")
public @ResponseBody
List<? extends RecipeName_DTO> getAllRecipes(HttpServletRequest request) {
Iterable<Recipe> recipes = recipeRepository.findAll();
if (request.getHeader("Accept-Language").equals("de")) {
return StreamSupport.stream(recipes.spliterator(), false).map(recipe -> modelMapper.map(recipe, RecipeNameGER_DTO.class)).collect(Collectors.toList());
} else if (request.getHeader("Accept-Language").equals("en")) {
return StreamSupport.stream(recipes.spliterator(), false).map(recipe -> modelMapper.map(recipe, RecipeNameENG_DTO.class)).collect(Collectors.toList());
} else {
return null;
}
}
}
Класс рецепта
@Entity
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"},
allowGetters = true)
@Getter
@Setter
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "recipe_name_id")
@JsonManagedReference
private RecipeName recipe_name;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "method_id")
@JsonManagedReference
private Method method;
private Time prep_time;
private Time cook_time;
private int serves;
private String image;
private String source;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
@JsonManagedReference
private List<RecipeIngredient> recipeIngredients;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "recipe_categories",
joinColumns = @JoinColumn(name = "recipe_id"),
inverseJoinColumns = @JoinColumn(name = "category_id"))
private List<Category> categories;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id")
@JsonManagedReference
private User user_id;
private short access;
@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdAt;
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private Date updatedAt;
public Recipe() {
recipeIngredients = new ArrayList<>();
categories = new ArrayList<>();
}
}
Класс RecipeDTO
@Getter
@Setter
public class Recipe_DTO {
private Long id;
private String recipe_name;
private String method;
private Time prep_time;
private Time cook_time;
private int serves;
private String image;
private String source;
private List<String> categories;
private String user;
private short access;
private Date createdAt;
private Date updatedAt;
}
Класс RecipeENG_DTO
@Getter
@Setter
public class RecipeENG_DTO extends Recipe_DTO{
private List<RecipeIngredientENG_DTO> recipeIngredients;
public RecipeENG_DTO() {
super();
}
}
Класс RecipeGER_DTO
@Getter
@Setter
public class RecipeGER_DTO extends Recipe_DTO {
private List<RecipeIngredientGER_DTO> recipeIngredients;
public RecipeGER_DTO() {
super();
}
}
Класс AppConfig (Конфигурация ModelMapper)
@Configuration
public class AppConfig {
@Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
//modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.addMappings(new PropertyMap<RecipeIngredient, RecipeIngredientGER_DTO>() {
@Override
protected void configure() {
map().setIngredient(source.getIngredient().getGer());
map().setAmount(source.getAmount());
map().setUnit(source.getUnit().getGer());
map().setGroup(source.getGroup().getGer());
}
});
modelMapper.addMappings(new PropertyMap<Recipe, RecipeGER_DTO>() {
@Override
protected void configure() {
map().setId(source.getId());
map().setRecipe_name(source.getRecipe_name().getGer());
map().setMethod(source.getMethod().getGer());
map().setPrep_time(source.getPrep_time());
map().setCook_time(source.getCook_time());
map().setServes(source.getServes());
map().setImage(source.getImage());
map().setSource(source.getSource());
map().setCategories(source.getCategories().stream().map(Category::getGer).collect(Collectors.toList()));
map().setUser(source.getUser_id().getName());
map().setAccess(source.getAccess());
map().setCreatedAt(source.getCreatedAt());
map().setUpdatedAt(source.getUpdatedAt());
map().setRecipeIngredients(mapAll(source.getRecipeIngredients(), RecipeIngredientGER_DTO.class));
}
});
return modelMapper;
}
}
Следующие сопоставления вызывают исключение NullPointerException
map (). SetCategories (source.getCategories (). Stream (). Map (Category :: getGer).collect (Collectors.toList ()));
map (). setUser (source.getUser_id (). getName ());
map (). setUser (source.getUser_id () .getName ());
map (). setRecipeIngredients (mapAll (source.getRecipeIngredients (), RecipeIngredientGER_DTO.class));
журнал ошибок
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'recipeEndpoints': Unsatisfied dependency expressed through field 'modelMapper'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'modelMapper' defined in class path resource [xyz/gigler/cook/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.modelmapper.ModelMapper]: Factory method 'modelMapper' threw exception; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) Failed to configure mappings
1 error
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
at xyz.gigler.cook.CookApplication.main(CookApplication.java:12)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'modelMapper' defined in class path resource [xyz/gigler/cook/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.modelmapper.ModelMapper]: Factory method 'modelMapper' threw exception; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) Failed to configure mappings
1 error
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 24 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.modelmapper.ModelMapper]: Factory method 'modelMapper' threw exception; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) Failed to configure mappings
1 error
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
... 37 common frames omitted
Caused by: org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) Failed to configure mappings
1 error
at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:244)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:96)
at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:92)
at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:124)
at org.modelmapper.ModelMapper.addMappings(ModelMapper.java:113)
at xyz.gigler.cook.AppConfig.modelMapper(AppConfig.java:51)
at xyz.gigler.cook.AppConfig$$EnhancerBySpringCGLIB$$93f5c8e.CGLIB$modelMapper$0(<generated>)
at xyz.gigler.cook.AppConfig$$EnhancerBySpringCGLIB$$93f5c8e$$FastClassBySpringCGLIB$$c2501590.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at xyz.gigler.cook.AppConfig$$EnhancerBySpringCGLIB$$93f5c8e.modelMapper(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 38 common frames omitted
Caused by: java.lang.NullPointerException: null
at xyz.gigler.cook.AppConfig$4.configure(AppConfig.java:63)
at org.modelmapper.PropertyMap.configure(PropertyMap.java:389)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:227)
... 53 common frames omitted
Process finished with exit code 0