#java #json #spring #hibernate #jpa
#java #json #весна #спящий режим #jpa
Вопрос:
У меня есть Order
сущность и OrderProduct
. Я хочу показать детали заказа в интерфейсе и, конечно, заказать в нем продукты. Итак, как получить объект продукта в OrderProduct
JSON. Мне не хватает объекта product в массиве products. Мне больше не нужен объект order, и я думаю, что это будет бесконечная рекурсия. 🙂
Моя Order
сущность:
@Entity
@Getter
@Setter
@Table(name ="orders")
public class Order{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
private BigDecimal totalPrice;
@OneToMany(mappedBy = "order", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JsonManagedReference(value="orders")
private List<OrderProduct> products = new ArrayList<>();
private int userId;
@DateTimeFormat(pattern="dd/MM/yyyy")
private Date date = new Date();
@DateTimeFormat(pattern="dd/MM/yyyy")
private Date deliveryDate;
@Enumerated(EnumType.STRING)
private OrderType orderType;
}
Моя OrderProduct
сущность:
@Entity
@Setter
@Getter
public class OrderProduct {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JsonBackReference(value="product")
@JoinColumn(name = "product_id")
private Product product;
@ManyToOne
@JsonBackReference(value="orders")
@JoinColumn(name = "order_id")
private Order order;
private Integer quantity;
}
Сущность продукта:
@Entity
@Getter
@Setter
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
private double price;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
@JsonManagedReference(value="ingredients")
private List<Ingredient> ingredients = new ArrayList<>();
@OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
@JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
private String fileName;
}
Ответ №1:
Это может помочь аннотировать один из ваших классов объектов с помощью
@JsonIdentityInfo(
property = "id",
generator = ObjectIdGenerators.PropertyGenerator.class
)
Каждый раз, когда сериализация JSON идет по кругу, данные объекта будут заменены идентификатором объекта или другим полем объекта по вашему выбору.
Комментарии:
1. К сожалению, это не работает для меня. Я пробовал это. Я добавил этот код в свою сущность отдельно, и он все тот же. Мне все еще не хватает объекта product в products[] .
Ответ №2:
Вы можете использовать @JsonView
аннотации для определения полей, которые вам нужно сериализовать в JSON
Как это работает:
- Вам нужно определить класс с интерфейсами. Например:
public class SomeView { public interface id {} public interface CoreData extends id {} public interface FullData extends CoreData {} }
- Отметьте поля сущностей с
@JsonView(<some interface.class>)
public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @JsonView(SomeView.id.class) private Long id; @Column(nullable = false) @JsonView(SomeView.CoreData.class) private String username; @Column(nullable = false) @JsonView(SomeView.FullData.class) private String email; }
- Аннотировать конечную точку с помощью
@JsonView(<some interface.class>)
@GetMapping() @JsonView(<some interface.class>) public User getUser() { return <get user entity somwhere> }
В случае @JsonView(SomeView.id.class)
, если вы получите этот JSON:
{
id: <some id>
}
В случае @JsonView(SomeView.CoreData.class)
:
{
id: <some id>,
username: <some username>
}
В случае @JsonView(SomeView.FullData.class)
:
{
id: <some id>,
username: <some username>,
email: <some email>
}
@JsonView
также работает со встроенными объектами, и вы можете аннотировать одно поле с помощью классов multiple views — @JsonView({SomeView.FullData.class, SomeOtherView.OtherData.class})
В вашем случае, я думаю, вы должны аннотировать все поля, которые вам нужны, кроме:
@OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
@JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
в Product
чтобы избежать циклической сериализации
Или в качестве альтернативы вы можете просто использовать классы DTO или сериализовать извлечение в JSON вручную (https://thepracticaldeveloper.com/java-and-json-jackson-serialization-with-objectmapper /)
Ответ №3:
Это можно сделать с помощью моей библиотеки beanknife
// This configure generate a class named ProductInfo which has the same shape with Product without property "products"
@ViewOf(value = Product.class, genName="ProductInfo", includePattern = ".*", excludes = {"products"})
class ProductInfoConfigure {}
// This configure generate a class named OrderProductRelation with the same shape of OrderProduct.
// But it has not order property and the type of its product property is change to ProductInfo generated above.
@ViewOf(value = OrderProduct.class, genName="OrderProductRelation", includePattern = ".*", excludes = {"order"})
class OrderProductRelationConfigure {
@OverrideViewProperty("product")
private ProductInfo product;
}
// This configure generate a class named OrderDetail with the same shape of Order.
// But the type of its products property is change to List<OrderProductRelation>
@ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
class OrderDetailConfigure {
@OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
будет генерировать эти классы:
class ProductInfo {
private Long id;
private String name;
private double price;
private List<Ingredient> ingredients; // it is not processed because you have not provide the class Ingredient
private String fileName;
}
public class OrderProductRelation {
private Long id;
private ProductInfo product;
private Integer quantity;
}
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
private Date date = new Date();
private Date deliveryDate;
private OrderType orderType;
}
Затем
Order order = ...
OrderDetail orderDetail = OrderDetail.read(order);
// serialize the otherDetail instead of order.
List<Order> orders = ...
List<OrderDetail> orderDetails = OrderDetail.read(orders);
// serialize the orderDetails instead of orders.
Возможные проблемы:
- Я не использую Lombok, поэтому Lombok, возможно, потребуется адаптировать, потому что он изменяет байтовый код на лету. Но это не большая проблема, я постараюсь ее адаптировать, если кто-то зафиксирует проблему и предоставит достаточно вариантов использования.
- Сгенерированный класс не наследует аннотацию исходного класса. В следующем выпуске я предоставлю sulotion. На данный момент, в качестве обходного пути, мы можем использовать пользовательский метод для преобразования свойства вручную. например
@ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
class OrderDetailConfigure {
@OverrideViewProperty("products")
private List<OrderProductRelation> products;
@OverrideViewProperty("orderType")
public static String orderType(Order source) {
return source.getOrder().name();
}
}
Сгенерированный класс будет изменен на
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
private Date date = new Date();
private Date deliveryDate;
private String orderType;
}
Обновить
Выпущена версия 1.2.0. Добавьте поддержку наследования аннотаций.
@ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
@UseAnnotation({DateTimeFormat.class, Enumerated.class, JsonProperty.class})
class OrderDetailConfigure {
@OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
генерировать
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
@DateTimeFormat(pattern="dd/MM/yyyy")
private Date date;
@DateTimeFormat(pattern="dd/MM/yyyy")
private Date deliveryDate;
@Enumerated(EnumType.STRING)
private OrderType orderType;
}