ResponseEntity incorrectly maps values

I have two entities: Product and Variant. Each product has a set of Variant. To get a product, I have a controller endpoint: http://localhost:8081/api/products/{id} Hence to call a product, I'll have to call this endpoint:
@GetMapping("{productId}")
public ResponseEntity<Object> getProduct(
@PathVariable Long productId
) {
ProductDto productDto = productService.getProduct(productId);
return ResponseEntity.status(HttpStatus.OK).body(productDto);
}
@GetMapping("{productId}")
public ResponseEntity<Object> getProduct(
@PathVariable Long productId
) {
ProductDto productDto = productService.getProduct(productId);
return ResponseEntity.status(HttpStatus.OK).body(productDto);
}
Well, I have tried debugging the problem and even explicitly serializing the objects before I pass it to the ResponseEntity's body, none works. Images attached described my attempt on verifying whether the object passed to the .body was changed:
Left picture: I explicitly serialize it and debugging the output to the console. You can see that the serialization process works correctly. Right picture: I only printed the first and last variant id of the product's variants. It resulted in the correct return of variant ids.
No description
No description
27 Replies
JavaBot
JavaBot7d ago
This post has been reserved for your question.
Hey @circle! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
circle
circleOP7d ago
I could not share the codes here, but I'll give the repository where the problem can be reproduced: https://github.com/vianneynara/kuenyawz-api/tree/develop/src/main/java/dev/realtards/kuenyawz Interesting candidates (link to the classes): - entities: Product, Variant - mapper: Product - DTOS that are sent back: Product, Variant - Product Controller, Product Service Severity: Receiving end received duplicate variant ids even though the rest of the fields are not duplicated
circle
circleOP7d ago
This is the generated mapper by Mapstruct. But as far as I understand my own code, the problem should not be during the logical process I wrote, it's not on the response pre-flight, but is on the flight/post-flight
circle
circleOP7d ago
The database for the product I showed: Where on the case, I'm expecting: - product id: 9636560366469120 - variant ids: {9636560370663424, 9636560370663425} Got variants: {9636560370663424, 9636560370663424} instead
No description
circle
circleOP7d ago
Correct output example: (variantId can have the same id as productId, id generations and identification is defnitely unrelated). I see that most outputs have the wrong variantId
No description
circle
circleOP7d ago
Any help is much appreciated ^-^
armad
armad7d ago
No help available for command: is
circle
circleOP7d ago
Help is
armad
armad7d ago
No help available for command: is
Tomasm21
Tomasm217d ago
Hi The problem is in Swagger itself open Developer tools network tab and compare response body:
Tomasm21
Tomasm217d ago
No description
Tomasm21
Tomasm217d ago
Postman:
No description
Tomasm21
Tomasm217d ago
And I removed @JsonBackReference from Variant.product and @JsonManagedReference from Product.variants since you use dtos when get that data:
@Entity
@SQLRestriction("deleted = false")
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Product extends Auditables {

//...

@OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
//@JsonManagedReference
private Set<Variant> variants = new HashSet<>();

@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private Set<ProductImage> images = new HashSet<>();
//...
}
@Entity
@SQLRestriction("deleted = false")
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Product extends Auditables {

//...

@OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
//@JsonManagedReference
private Set<Variant> variants = new HashSet<>();

@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private Set<ProductImage> images = new HashSet<>();
//...
}
and:
@Entity
@Getter
@Setter
@EqualsAndHashCode(callSuper = true, exclude = "product")
//@ToString(callSuper = true, exclude = "product")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Variant extends Auditables {

//...

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "product_id", nullable = false)
//@JsonBackReference
private Product product;

//...
}
@Entity
@Getter
@Setter
@EqualsAndHashCode(callSuper = true, exclude = "product")
//@ToString(callSuper = true, exclude = "product")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Variant extends Auditables {

//...

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "product_id", nullable = false)
//@JsonBackReference
private Product product;

//...
}
And also fetch it eagerly. I don't know what magic happens at http://localhost:8081/swagger-ui/index.html in their Response body output. But that's strange. Maybe bug. So, ResponseEntity maps values correctly. Just Swagger from Springdoc shows it incorrectly in UI even though response body in developer tools network tab is correct.
circle
circleOP7d ago
It's really weird, I wouldn't just say it only happens in swagger, but the report came from the front end divison that uses my api 😂 let me check in postman oh i never knew you can see the stuff there
Tomasm21
Tomasm217d ago
Now you will
circle
circleOP7d ago
@JsonBackReference and @JsonManagedReference helped me at the ealy stage of development, for some reason I had circular backreference in the JSON and it causes stack overflow I'm not sure what people usually do when declaring those relationships Jumped between course episodes 😂 but I will review it again Though I will admit, it didn't work that well. The problem disappears when I do:
@Override
public String toString() {
return "Variant{" +
"variantId=" + variantId +
", price=" + price +
", type='" + type + "'" +
", minQuantity=" + minQuantity +
", maxQuantity=" + maxQuantity +
", version=" + version +
", productId=" + (product != null ? product.getProductId() : null) +
'}';
}
@Override
public String toString() {
return "Variant{" +
"variantId=" + variantId +
", price=" + price +
", type='" + type + "'" +
", minQuantity=" + minQuantity +
", maxQuantity=" + maxQuantity +
", version=" + version +
", productId=" + (product != null ? product.getProductId() : null) +
'}';
}
Basically overriding the representation
Tomasm21
Tomasm217d ago
Without those annotations if you would return entities instead of Dto represtations of it then you would get infinite references in entities serialisation to json. You would get infinite loop that would lead to stack overflow abd exception. So don't serialise sheer entities, but dtos. Yea you can escape the problem with to string and choose what to show. But better to map to dtos.
circle
circleOP7d ago
You are right, I even removed the overridden toString and it still works perfectly fine. Problem has probably resolved without my knowledge after switching to DTOs
JavaBot
JavaBot7d ago
If you are finished with your post, please close it. If you are not, please ignore this message. Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
circle
circleOP7d ago
Not sure when to use LAZY and EAGER yet, but as far as I know EAGER mean the beans will be loaded together (product -- variant loaded together)? And then what would be the implication of using LAZY on both entities?
Tomasm21
Tomasm217d ago
I don't really know. Just also heard that beans with LAZY would be loaded when specifically requesting it. But I used it because chatgpt said that Hibernare might serialise Variants in products badly and might copy its previous object with older id when transaction is loaded lazily. So I switched to eager. But I don't think this is the solution.Just more strict rule for Hibernate to load all dependant entities at the same transaction. When I debugged like you then in service method ProductDto is with correct variant ids for both loading Variants eagerly or lazily. The problem is in front-end. For unknown reason Swagger shows variant ids wrong. But in response body numbers are correct. I reported it to Springdoc issues on github.
circle
circleOP7d ago
Hah okay Yeah and his front end, I also saw the network response. It's right, I'lll have to check with them Thanks, I was planning to do so
JavaBot
JavaBot7d ago
If you are finished with your post, please close it. If you are not, please ignore this message. Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
circle
circleOP7d ago
Thank you so much!
circle
circleOP7d ago
Problem has been resolved, but is crazy.
No description
JavaBot
JavaBot7d ago
If you are finished with your post, please close it. If you are not, please ignore this message. Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
JavaBot
JavaBot7d ago
Post Closed
This post has been closed by <@389034898666553344>.
Want results from more Discord servers?
Add your server