Reputation: 594
I have a Spring Boot 2 application which uses the ObjectMapper.convertValue
method to convert from/to Entities and DTOs.
I've been trying to understand why that method doesn't convert some of the fields, specifically, look at the following scenario:
Product entity:
@Entity
@Table(name = "product")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor
public class Product extends AbstractPersistable<Long> {
@Column
private String name;
@Column
private String description;
@Column
private BigDecimal price;
@Column
private int weight;
@Column
private int stock = 0;
@Column(name = "image_url", length = 254, unique = true)
private String imageUrl;
@NotEmpty
@Column(name = "banner_image_url", length = 254, unique = true)
private String bannerImageUrl;
@ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "product_category_id")
@JsonBackReference
private Category category;
@OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@OrderBy
@JsonManagedReference
private SortedSet<ProductThumbnail> thumbnails;
public Product(Long id) {
this.setId(id);
}
public Product(String name, String description, BigDecimal price, int weight) {
this.name = name;
this.description = description;
this.price = price;
this.weight = weight;
}
}
Product DTO:
@Getter
@Setter
@NoArgsConstructor
public class ProductDTO {
private Long id;
private String name;
private String description;
private BigDecimal price;
private int weight;
private String imageUrl;
private String bannerImageUrl;
private CategoryDTO category;
private SortedSet<ProductThumbnailDTO> thumbnails;
public ProductDTO(@JsonProperty("id") Long id) {
this.id = id;
}
@JsonCreator
public ProductDTO(@JsonProperty("id") Long id,
@JsonProperty("name") String name,
@JsonProperty("description") String description,
@JsonProperty("price") BigDecimal price,
@JsonProperty("weight") int weight,
@JsonProperty("imageUrl") String imageUrl,
@JsonProperty("category") CategoryDTO category,
@JsonProperty("variants") Set<ProductVariantDTO> variants) {
this.id = id;
this.name = name;
this.description = description;
this.price = price;
this.weight = weight;
this.imageUrl = imageUrl;
this.category = category;
this.variants = variants;
}
}
Each field gets converted automatically when executing the folliwng code:
ProductDTO productDTO = objectMapper.convertValue(product, ProductDTO.class);
but with the exception of category
. So category in the product
variable is set, while the resulting productDTO.category field is null after the conversion.
Category entity:
@Entity
@Table(name = "product_category")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor
@Immutable
public class Category extends AbstractPersistable<Long> {
@Column
private String name;
@Column
private String description;
@OneToMany(mappedBy = "category", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonManagedReference
private Set<Product> products = new HashSet<>();
}
Category DTO:
@Data
@NoArgsConstructor
public class CategoryDTO {
private String name;
private String description;
@JsonIgnore
private Set<Product> products = new HashSet<>();
@JsonCreator
public CategoryDTO(@JsonProperty("name") String name, @JsonProperty("description") String description) {
this.name = name;
this.description = description;
}
}
So the question is, why the ObjectMapper is not able to convert automatically the category field as well? Is there any condition that prevents it to happen? Because no error is thrown at all.
Following is the objectmapper bean:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.findAndRegisterModules();
objectMapper.registerModules(module(), new Jdk8Module());
return objectMapper;
}
jackson dep version 2.10.1
Thank you
Upvotes: 3
Views: 5123
Reputation: 4502
So the main culprit in your code is the @JsonBackReference
. As @JsonBackReference
annotation is assigned to
@JsonBackReference
private Category category;
This category is auto removed while serialization. From this reference:
@JsonManagedReference is the forward part of reference – the one that gets serialized normally. @JsonBackReference is the back part of reference – it will be omitted from serialization.
So instead of @JsonBackReference
try using: @JsonManagedReference
.
Hope this solve your problem.
Upvotes: 1