Jackson with Spring Boot depth-based serialization - java

We have in our spring boot (1.5.6) app a classical bi-directional OneToMany hierarchy in our entities, e.g. an Order has many Item.
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "order", )
#JsonManagedReference
private Set<Item> items = new HashSet<>();
// getters and setters
}
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JsonBackReference
private Order order = new HashSet<>();
// getters and setters
}
In our JavaScript-view, which uses rest-controllers to fetch the data, sometimes the Order and sometimes the Items is the root object. Let's say we have somekind of "Show Order" and another "Show Item" view.
So, if Order is the root, we want to know also its children (items) and if an Item is the root, we also want to know its parent order.
The serialization obviously would generate an infinite recursion, so we would normally use #JsonManagedReference and #JsonBackReference here.
However, this removes the back-reference (so we have a one-directional serialization here...). We also tried to use JsonIdentityInfo - which does not work, because elasticsearch has some problems during the deserialization and we also have some cross-references which will be also replaced by the ids and messes everything up...
Long story short, our idea is now to have somekind of "depth-break":
When the first serialized object is an Order, then its items-property should be fully serialized. But for each Item the back-reference to order should not be serialized (one can say here "the depth of order is 2").
When the first serialized object is an Item then its back-reference order is serialized, but the second managed-refrence to items will be ignored.
We tried also JsonView for this, but we cannot dynamically set this value.
Anyone who solved that or has an approach what we can use for this?

Instead of #JsonManagedReference and #JsonBackReference you can use #JsonIgnoreProperties annotation to suppress serialization of nested properties:
public class Item {
//...
#JsonIgnoreProperties("items")
#ManyToOne
private Order order = new HashSet<>();
//...
}

Related

Crnk JsonApiRelation, OneToMany and filtering implementation

I use crnk (JSON-API) in java project and I have 3 questions regarding its usage with spring boot and jpa - haven't found exact implementation details in documentation.
For example, I have 2 entities and respective tables:
#Entity
#JsonApiResource(type = "employee")
public class Employee {
#Id
#JsonApiId
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "typeId")
private EmployeeType employeeType; //stored in table as typeId
}
#Entity
#JsonApiResource(type = "type")
public class EmployeeType {
#Id
#JsonApiId
private int id;
private String typeName;
private int salary;
}
How should JsonApiRelation be introduced in order to be able to call "/employee/1" and "/employee/1/type" urls?
For example there is one more entity.
#Entity
#JsonApiResource(type = "project")
public class Project {
#Id
#JsonApiId
private int id;
private String supervisorName;
private String projectName;
}
First, I'd like to have List of Projects for each Employee, where he is a supervisor, joint by name and have it listed as attribute in Json.
Tried implementing it with #OneToMany and #JoinColumn annotations but got StackOverflowException. How could this be implemented. And second, how could this be implemented with Relation? Like "/employee/1/projects" url.
How should I implement custom filtering of results for findAll method? For example, I have a List of all Employees, but I'd like to exclude some of them from the response. Which class/method should be introduced for this behaviour?
#JsonApiRelation annotation should not be necessary. Crnk will detect the #ManyToOne annotation and map it accordingly.
in case of crnk-jpa it is sufficient to specify all relationships in JPA. Matching JSON API relationships. So your approach seems good. What was the StackoverflowException stacktrace? (next to the examples, there are also many example entities in crnk-jpa)
I would make use of a decorator. See http://www.crnk.io/documentation/#_request_filtering. RepositoryDecoratorFactory allows to place a custom repository between the caller and crnk-jpa (or any other kind of repository). There you can do any kind of modification perform (maybe) calling the "real" repository. => Will add an example for this
feel free also make open up tickets in crnk for any documentation/example clarifications.

Why Nested Objects is retrieved from JPA? [duplicate]

I'm using SpringBoot and JPA to build a REST interface.
Now, I have a strange JSON returned for the list of products fetched from the database. Let's say that I have:
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "categoryId", nullable = false, updatable = false)
private Category category;
...
}
#Entity
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "category", cascade = CascadeType.DETACH)
#OrderBy("name ASC")
private List<Product> products = Collections.emptyList();
...
}
The JPA repository for the Product is defined as:
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findAll();
}
In my controller I have:
#Autowired
private ProductRepository productRepo;
#RequestMapping("/all-products", method = RequestMethod.GET)
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("products", productRepo.findAll());
return model;
}
What is driving my crazy, is that if I try to call this service as follows:
$ curl localhost:8080/all-products
I get a recursive output due to the relationship between tables product and category, e.g.:
{"products":[{"id":1,"name":"Product1","category":
{"id":1,"name":"Cat1","products":[{"id":6,"name":"Product6","category":
{"id":1,"name":"Cat1","products":[{"id":6,"name":"Product6","category":
{"id":1,...
What am I doing wrong?
You're not doing anything wrong (at least at the code level it's rather conceptual) - json serializer just goes like this:
Product - serialize it, but wait - there is a category field, so serializer must serialize the category field
Category - serialize it, but wait - there is a products field, so serializer must serialize each of the product in the list
Product - because your collection contains the product & product contains category it goes in a endless loop untill a timeout.
You must use a view or just skip it.
Use #JsonView
Use a view as a POJO
Return new ProductView that has all fields of product and a reference (category) to new CategoryView (you can end at this point) that has collection of (products) new ProductViewWithoutReferences, and so on
Use #JsonIgnore on a collection of products
And as a side note - if it's a #RestController and you're invoking "all-products" then it's a bit unusual to return something else than a list. Wrapping the response in a map is redundant. Many rest clients expect a list when they invoke list() method.
I know it's a bit late, but adding it here in case anybody faces the same problem.
Here is another relevant answer I could find which discuss about similar topic
https://stackoverflow.com/a/3359884/6785908
quoting it here
Jackson 1.6 has annotation-based support for handling such
parent/child linkage, see
http://wiki.fasterxml.com/JacksonFeatureBiDirReferences.
You can of course already exclude serialization of parent link already
using most JSON processing packages (jackson, gson and flex-json at
least support it), but the real trick is in how to deserialize it back
(re-create parent link), not just handle serialization side. Although
sounds like for now just exclusion might work for you.
EDIT (April 2012): Jackson 2.0 now supports true identity
references, so you can solve it this way also.
Adding #JsonIgnore worked for me
#OneToMany(mappedBy = "policy")
#JsonIgnore
private List<Payment> payments;
#JeanValjean your are the best

When not cascading a field are you required to use #Transient?

To illustrate my question consider the following example:
#Entity
public class Box implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToMany //No cascade
private List<Item> items;
.
.
.
}
Above we have a Box which has a one-to-many association with Item
Now say you grab a Box object and start filling it with Items and try to persist it. It will tell you that you cannot do this because you have some non-transient nested objects with no cascade (or something like that).
In this situation, where you only want an association (with no persistence), but you want to use those fields while the object is alive, what do you do when you want to persist it?
Do you null the list of Item? Do you annotate it with #Transient?
That is my question. Thanks!
If your Item object is not an database entity you should annotate the collection as #Transient. If the Item object is mapped into the database you should use the #OneToMany annotation so this collection will be read from the database. To prevent this collection from being stored when you save the Box object you can add the #JoinColumn annotation and set the insertable and updatabble attribute to false:
#OneToMany
#JoinColumn((name="BOX_ID", insertable=false, updatable=false)
private List<Item> items;

JPA/Hibernate + get specific item from onetomayrelation

I want to get a specific row in a OneToMany relation. E.g. getting the cheapest item of an order
Example:
public class Order {
#Id
#Column(name = "ORDER_ID")
private Long id;
???
private Item cheapestItem;
}
public class Item {
#Id
#Column(name = "ITEM_ID")
private Long id;
private Long price;
}
How can I do this?
Try specifying a where clause in the hibernate #Where annotation (Not sure if you can apply it to a non-collection, though)
I want to get (...) the cheapest item of an order
If you really want to get the cheapest Item (without actually persisting it), it should be is doable with a ManyToOne and a JoinColumnOrFormula. Requires Hibernate 3.5+, see issues like HHH-4382 and HHH-5041 for examples.
Retrieving only the price would be much easier and doable with previous versions of Hibernate. See Hibernate Derived Properties - Performance and Portability.

Hibernate: How to generate Id from entity constructor parameters?

How to generate id for entity using constructor parameters - is that possible ? I need that to make tree structure (category -> subcategories...) passing to constructor parent category id.
Category category = new Category(parentId);
CategoryDAO.add(category);
One idea would be to use "id" "generators" from Hibernate.
See docs here : http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-id
Looking at your use case, the "assigned" or "class" generators seem a fit
This does not directly answer your question but, unless I misunderstood your requirements, what you actually want is to model a self-referencing association, something like this:
#Entity
public class Category {
#Id #GeneratedValue
private Long id;
private String name;
#ManyToOne(optional=true)
private Category parent;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Categories> subCategories;
// ...
public void addToSubCategories(Category category) {
category.setParent(this);
this.subCategories.add(category);
}
}
If this is not what you want, and if you don't want to use a generated identifier, then simply don't use the #GeneratedValue annotation and assign the identifier manually (in the constructor, using the setter, anything). But I fail to see why you'd need this.

Categories