I have an Order entity and OrderProduct. I want to show order details on frontend and of course order products in it. So how to fetch product object in OrderProduct JSON. I'm missing product object in products array. I don't need order object one more time and i think it going to be a infinite recursion stuff with it. :)
My Order entity:
#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;
}
My OrderProduct entity:
#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;
}
Product entity:
#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;
}
This can help annotate one of your entity clases with
#JsonIdentityInfo(
property = "id",
generator = ObjectIdGenerators.PropertyGenerator.class
)
Every time when JSON serialization go in circles object data will be replaced with object id or orher field of entity for your choose.
You can use #JsonViewannotation to define the fields that you need to serialize to JSON
How it works:
You need define class with interfaces. For example:
public class SomeView {
public interface id {}
public interface CoreData extends id {}
public interface FullData extends CoreData {}
}
Mark entity fields with #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;
}
Annotate endpoint with #JsonView(<some interface.class>)
#GetMapping()
#JsonView(<some interface.class>)
public User getUser() {
return <get user entity somwhere>
}
In case #JsonView(SomeView.id.class) you will get this JSON:
{
id: <some id>
}
In case #JsonView(SomeView.CoreData.class):
{
id: <some id>,
username: <some username>
}
In case #JsonView(SomeView.FullData.class):
{
id: <some id>,
username: <some username>,
email: <some email>
}
#JsonView also works with embeded objects and you can annotate one field with multiply views classes - #JsonView({SomeView.FullData.class, SomeOtherView.OtherData.class})
In your case i think you should annotate all the fields you need except:
#OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
#JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
in Product
to avoid circular serialization
Or as alternative you can just use DTO classes or seralize oject to JSON manualy (https://thepracticaldeveloper.com/java-and-json-jackson-serialization-with-objectmapper/)
This can be done by my library 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;
}
will generate these classes:
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;
}
Then
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.
Possible problems:
I doesn't use Lombok, so Lombok may need to be adapted because it change the byte code on the fly. But it is not a big problem, I will try to adapt it if someone commit the issue and provide enough use cases.
The generated class does not inherit the annotation on the original class. In next release I will provide a sulotion. At this moment, as a workaround, we can use custom method to convert the property manually. such as
#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();
}
}
The generated class will be changed to
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;
}
Update
Version 1.2.0 released. Add support of annotation inheritance.
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
#UseAnnotation({DateTimeFormat.class, Enumerated.class, JsonProperty.class})
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
generate
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;
}
I am absolute beginner in Spring and need some help. Code below is service layer and is intended to update organisation entity in CrudeRepository, but it only saves new organisation. Just can't imaginate what is wrong.
if (repository.exists(organisation.getId())) {
repository.save(organisation);
}
Organisation.java
#Entity
#Table(name = "organisations")
public class Organisation implements Serializable {
#Id
#GeneratedValue
private long id;
#Version
private Integer version;
#NotNull
#Size(min=3, max=100)
#Basic(optional = false)
#Column(length = 100)
private String name;
#Column(name = "full_name")
private String fullName;
private long inn;
private long kpp;
private String address;
private long phone;
#AssertTrue
#Column(name = "is_active")
private boolean isActive;
Probably your organization object is not managed by spring (it's a raw object, not a managed entity), try finding the object first and then updating the java object and finally save.
Long id = ...
if (repository.exists(id)) {
Organization organization = repository.getOne(id);
// ...
// Update objects field e.g. organization.setName("New Name");
// ...
repository.save(organisation);
}
Note you're using the #Version annotation, when the current version is null spring considers the entity as new. So this field shouldn't be null to update instead insert.
i'm trying to create tabel entity with composite primary key.
so i tried to create #Embeddable class with all the primary keys and use #EmbeddedId on the field that represents the primary keys. but i get this error:
Caused by: org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
i wos tried to follow this link bat noting work
what i'm doing wrong?
here is my code
DocEntity
#Entity(name = "doc")
#Table(name = "doc")
public class DocEntity extends baseDataBase implements Serializable
{
private static final long serialVersionUID = 1L;
#EmbeddedId
private DocID docID;
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "DocID")
#JsonProperty
#NotEmpty
private short id;
#Column(name = "DocDescription")
#JsonProperty
#NotEmpty
private String docDescription;
#Column(name = "DocPath")
#JsonProperty
#NotEmpty
private String docPath;
// getters, setters ,equals, hashCode
}
DocID
#Embeddable
public class DocID implements Serializable
{
private static final long serialVersionUID = 1L;
#Column(name = "DocName")
#JsonProperty
#NotEmpty
protected String docName;
#Column(name = "DocVersion")
#JsonProperty
#NotEmpty
protected String docVersion;
// getters, setters ,equals, hashCode
}
what i'm doing wrong?
I have the following class:
#Entity
public class User {
#Embeddable
public static class Key implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
#Temporal(TemporalType.TIMESTAMP)
private Date effectiveDate;
#Column(unique = true)
private String name;
// Getters, Setters
}
#EmbeddedId
private Key key;
#Column(nullable = false)
private String password;
// Getters, Setters ...
}
I want the id field within key to be generated, unfortunately I can't find a way to do so. I've looked at similar questions here but I can't find the answer (all the solution proposed did not work in this case).
I've got two entities
ServiceDownload.java
#Entity
public class ServiceDownload implements Serializable {
private static final long serialVersionUID = -5336424090042137820L;
#Id
#GeneratedValue
private Long id;
#Length(max = 255)
private String description;
private byte[] download;
private String fileName;
#ManyToOne
private Service service;
Service.java
#Entity
public class Service implements Serializable {
private static final long serialVersionUID = 4520872456865907866L;
#EmbeddedId
private ServiceId id;
#Length(max = 255)
private String servicename;
#Column(columnDefinition = "text")
private String highlightsText;
#Column(columnDefinition = "text")
private String detailsText;
#Column(columnDefinition = "text")
private String productText;
#Column(columnDefinition = "text")
private String dataText;
#ManyToMany(mappedBy = "services")
private Set<Machine> machines;
#OneToMany(targetEntity = ServiceDownload.class)
private List<ServiceDownload> serviceDownloads;
#OneToOne
private ServicePicture servicePicture;
When I create a new ServiceDownload Object and try to persists this I recieve a duplicate key exception. It seems that jpa tries to insert a new service object into the service table. How can I disable this behaviour?
You are using #GeneratedValue annotation for your #Id. According to JPA documentation, you should supply unique identifiers
By default, the application is responsible for supplying and setting entity identifiers (see #Id)
Try using a #SequenceGenerator and a sequence in your database to generate unique identifiers