JPA + JSON deserialization - json

I'm quite new to JPA and using Java as backend for REST services, and I'd like to store a JSON into the database, and wanted to check what is the best way to do so. Please let me know in case I'm taking the "long path".
(I'm using Spring)
My data:
{
frequency: "Week"
isGoalOrMore: "true"
name: "Develop"
targetValue: "5"
type: "Average"
}
Habit.java
#Entity
#Table(name = "habits")
public class Habit {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_habit_type")
private HabitType type;
private boolean isGoalOrMore; //basically means, achieve goal or do more
private double targetValue;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_frequency")
private Frequency frequency;
//getters and setters
}
HabitType.java
#Entity
#Table(name = "habits_type")
public class HabitType {
#Id
private Long id;
private String description;
}
DB Model (mysql)
habits
--id
--name
--id_frequency
--id_habit_type
habits_type
--id
--description
Problem
When I try to save this data I receive an error as below:
com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class com.tiperapp.model.HabitType] from String value ('Average'); no single-String constructor/factory method
How can I solve it?
By reading some topics, one option would be to write a JSON deserializer. Other cases they solved it by fixing their JPA.
I'd like to know which would be recommend.. can you guys please help me how to deserialize this, the best way ?

The best solution, IMHO, is to think of the data that you send and receive to/from the browser (which I'll call the DTOs), and the data that you store in the database, as two distinct things.
Sure, they often look the same. But they're different. You can't have exactly the same model for both. For example:
the entities constitute a huge graph, often containing bidirectional associations, and which has no limit, since everything can be loaded lazily. For example, an order line has an order, which has a buyer, which has an address, which has a city, which has a country, which has... You can't reasonably serialize all this graph when the browser asks for an order line, otherwise you'll serialize half of the database to JSON.
the JSON sometimes has a different representation of the same thing than the database. For example, you store all the habit types in the database as a row with an ID and a description, but it seems the JSON only really cares about the description. The ID seems to be a way to avoid duplicating the same description everywhere.
many attributes of the entities can not be seen (for security reasons) by the end user, or are only relevant for some use cases, etc.
So I would thus use different classes for both. When you receive a HabitDTO as JSON, you deserialize it with Jackson to a HabitDTO instance, and then find the HabitType entity correspondint to the description in the DTO, and create/update the Habit entity instance based on the corresponding information in the HabitDTO.
To recap: the entities contain the complete business model of your application, used to implement all the functional use cases. The DTOs contain serialized information and are used to transfer a small part of the information to/from the client, often for a specific use case. Having a clear distinction between the two allows much more flexibility: you can change the underlying persistence model without changing the interface of your services, or vice versa.

Your json is wrong.
type is not mapped to a string but to an object. You can do that by using this :
{
frequency: "Week"
isGoalOrMore: "true"
name: "Develop"
targetValue: "5"
type: {
description: "A DESCRIPTION"
id: "Average"
}
}

Related

Is there a way to get "#JsonBackReference" only working when the element is in a collection, but not when standalone?

I am working with fasterxml, and I have two objects that have a relationship "one to many" or "many to one". For example the class Author and the class Book, one author has many books, and one book has one author. Can I tell fasterxml not to serialize the author of the book, only when the book is in it's author's books collection, but when the book is on itself, to serialize it.
Author class:
public class Author{
public int id;
public string name;
#JsonManagedReference
public Set<Book> books;
}
Book class:
public class Book{
public int id;
public string name;
#JsonBackReference
public Author author;
}
That setup works just fine if I want to get only the author, because the books are in place and theirs's author property isn't being serialized, but if I want to serialize only the book, it's author again isn't being serialized, because of the "#JsonBackReference" annotation. Is there any workaround in the said situation? Here are some more examples if you are not getting what I mean...
When I serialize an Autor:
{
id:3,
name: "Ivan Vazov"
books:[
{
id:5,
name: "Under the Yoke"
}
]
}
And that is what I want here.
When I serialize a Book:
{
id:5,
name: "Under the Yoke"
}
But i don't want this, I want this:
{
id:5,
name: "Under the Yoke",
author: {
id:3,
name: "Ivan Vazov"
}
}
Any thoughts on the matter would be great! Thanks.
If you want the references to be serialized from both sides(Book, Author) then jackson faces the issue with circular reference where it get's stuck in an endless loop of references when serializing one of those 2 objects.
The workaround was with #JsonManagedReference and #JsonBackReference where jackson ignored 1 side and serialized only the other side in order to avoid circular reference.
The solution to your problem (when you want to serialize both sides) is to create seperate DTO objects(AuthorDto, BookDto) and instead of returning from your controller a Author to be serialized you return an AuthorDto. Same with Book. Then circlular reference problem does not exist any more and both sides serialize the problematic references.
DTOs are the way to go in more complex scenarios, especially on the inbound side. For dynamic filtering of simpler use cases i wrote an addon for jackson to use antpath style filtering. Probably it helps you:
https://github.com/Antibrumm/jackson-antpathfilter
You have add fetch type in ManyToOne relationship side as follow.
To fetch all book entity then add fetch type Eager.
#ManyToOne(
fetch = FetchType.EAGER,
optional = false
)
To fetch only book entity then add fetch lazy.
#ManyToOne(
fetch = FetchType.LAZY,
optional = false
)

Is there a way in QueryDSL to use JsonProperty Names in the query string instead of field names

When the entity classes were first created they were modeled in JPA to match database field names. For our rest endpoints we used JsonProperty to let Jackson auto map names the client would expect they are kabob case. However we are now implementing search functionality and are building it using QueryDSL. It works and works well as long as we use the internal field names.
What we are looking to do though is accept querystrings with the JsonProperty Name.
Like: campaign-grouping-code=123 vs groupingCode=123
I am assuming it would probably have to be done in the Q Classes that are generated but can't find much documentation on them.
#Column(name = "GROUPING_CODE")
private String groupingCode;
#JsonProperty("campaign-grouping-code")
#NotBlank(message = "1", groups = CampaignFieldCheck.class)
#Size(max = 40, message = "100", groups = CampaignFieldCheck.class)
public String getGroupingCode() {
return groupingCode;
}
public void setGroupingCode(String groupingCode) {
this.groupingCode = groupingCode;
}
Quite simply: no that is not possible because the Spring Data endpoints use the values from the attributes in the metamodel, which in the case for querydsl-jpa are always the JPA attribute names, which are in turn derived from the field names, and - is not a valid character in a Java field name.
So there isnt a way to get the jsonproperty names to work. There is a workaround aliasing.. In this link you can find the black list and white list settings. Aliases are automatically white listed. but the aliases can be mapped via whatever text string makes sense to the field names.
https://gt-tech.bitbucket.io/spring-data-querydsl-value-operators/README.html

Object modelling - REST vs Hibernate

I am facing this in a project of my own, but i am sure this should be a generic issue.
I am trying to build rest services using java, spring and hibernate.
My typical entity object is modeled like below
#Entity
public class Company implements Serializable{
private Long companyId;
private String name;
private String shortDescription;
private Long logoId;
private Set<ActivityType> activityTypeList;
private String address;
private RegistrationInfo regInfo;
}
Where one object has associations with other objects, so my model object contains references to the associated objects.
I use jackson configured with spring for json-object and object-json conversion, it works perfectly, so I am good till this point.
Now I want my POST calls(which I am using for object creation) to create Company objects, only catch here is that I do not want to pass complete json for contained objects(like ActivityType and RegistrationInfo in above example), rather an id for each where the object with provided id already exists in the database. For example see this JSON snippet:
{
"companyId": 5,
"name": "test company created by rest call",
"shortDescription": "dummy description",
"logoId": 0,
"activityTypeList": [
{"activityTypeId": 1}
],
"address": "202, kardoga lane, lonavala, Maharashtra",
"regInfo": {
"registrationInfoId": 1
}
}
My rest service just needs to associate the newly created company object with the existing contained object with the provided id(inside POST call's body).
Jackson serialization creates unpopulated objects in this case, which hibernate fails to persist (for obvious reasons, there is no data just an id)
Sure i can use a DTO over here, i can get REST -> DTO and then DTO -> entity, but that seems an overhead and moreover in order to populate the Company object defined above, I will need to fetch the associated objects first from DB, attach them to my company object and then save it back. That will be an unnecessary DB trip.
Anyone has better idea on this one? Any design pattern, framework or simple tips/tricks in which i dont require a dto as well as save DB calls.

Pattern to map JPA Objects to DTO for Conversion to JSON

I have a somewhat philosophical question relating to mapping JPA Objects to JSON Strings. Of course there is no necessity for the source object to be a persistent object - it is just that that is my situation.
I have a collection of objects that are managed by Eclipse Link. I need to turn some of these objects into JSON Strings, however the mapping is not one-to-one. I am convinced that the conversion should be loosely coupled so as to isolate the JSON objects from changes in the underlying entities.
I am planning to have the JPA entity as such:
#Entity
#Table(name = "AbnormalFlags")
public class AbnormalFlag implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "Code", unique = false, nullable = false)
private String code;
#Column(name = "Description", unique = false, nullable = false)
private String description;
// Getters and setters
}
and the equivalent object to be converted to JSON
public class AbnormalFlagDTO implements java.io.Serializable {
private String code;
private String description;
private Boolean disabled;
// Getters and setters
}
Is there an elegant pattern or methodology I can use to facilitate this process for several types of objects.
Thanks in anticipation
My answer: no, and also you should generally extend DTOs with care (when reusing existing DTOs). But you could use a Map<String, Object> as a DTO (if you do not use the same DTO to read the data back). Besides you could create an APT (annotation processor tool) that generates the code for DTOs from your entities and then you simply modify them.
This is a perfect use case for Blaze-Persistence Entity Views as you will most probably also want to keep an eye on the performance of the query used for fetching the data.
I created the library to allow easy mapping between JPA models and custom interface defined models. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A mapping for your model could look as simple as the following
#EntityView(AbnormalFlag.class)
interface AbnormalFlagDTO extends Serializable {
String getCode();
String getDescription();
Boolean getDisabled();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
AbnormalFlagDTO dto = entityViewManager.find(entityManager, AbnormalFlagDTO.class, id);
The serialization of the entity view to JSON will work as expected. If you also want to deserialize objects, you will have to construct the object first and also add setters to the interface.

Serializing JPA entities to JSON using Jackson

Question regarding combination of Jackson/JPA
If there are about 20 entities in current application and I have add Jackson dependency in POM, does it mean all entities are by default ready to convert to JSON object? I saw a sample project seems only class annotated as #JsonIgnored is skipped by JSON. If so, then how can this happen, what is behind such mechanism? how JACKSON handle those entities which don't have any Jackson annotation, by default ignored or not? I've been looking for resources online but not much luck.
If only one of the 20 entities need to be mapped to JSON object, does it mean I have to add #JsonIgnore to all other 19 entities? If not, how Jackson differentiate with entity to work on?
Thanks.
Jackson and JPA don't have anything to do with each other. Jackson is a JSON parsing library and JPA is a persistence framework. Jackson can serialize almost any object - the only requirement being that the object have some kind of recognizable properties (Javabean type properties, or bare fields annotated with #JsonProperty. There is an additional requirement for deserialization, that the target type have a default (no-arg) constructor. So, for example, this is an object that Jackson can serialize:
// Class with a single Javabean property, "name"
class Person {
private String name;
public String getName() { return name ; }
public String setName(String name) { this.name = name ; }
}
And here is another:
// Class with a single field annotated with #JsonProperty
class Account {
#JsonProperty("accountNumber")
private String accountNumber;
}
And here is yet another:
#Entity
public class User {
#Id
private Long id;
#Basic
private String userName;
#Basic
#JsonIgnore
private String password;
#Basic
#JsonIgnore
private Address address;
// Constructors, getters, setters
}
The last example shows a JPA entity class - as far as Jackson is concerned it can be serialized just like any other type. But, take note of its fields: when this object is serialized into JSON two of the fields will not be included - 'password' and 'address'. This is because they have been annotated with #JsonIgnore. The #JsonIgnore annotation allows a developer to say 'Hey, its ok to serialize this object, but when you do so don't include these fields in the output'. This exclusion only occurs for the fields of this object, so for example, if you included an Address field in another class, but did not mark the field as ignorable, it would be serialized.
To prevent serialization of a type in all cases, regardless of context, use the #JsonIgnoreType annotation. When used on a type it basically means 'I dont care where this type is used, never serialize it'.
No, you don't need to add #JsonIgnore on every class and if you had tried you would have gotten a compile error, since you can't put it there. Jackson will only work on objects you give to it, it's no magic.
The Jackson documentation is easily found online, such at its project page on github or on the codehaus website.