Mapping JSON object to Hibernate entity - java

I'm going to start a project of a REST application managed with Spring and with Hibernate for my model.
I know that Spring allows you to get Java object from the HTTP Request (with #Consumes(JSON) annotation). Is there any conflict if this Java object is also a Hibernate entities? And is nested object working (like #ManyToOne relation)?

Maven dependency
The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Domain model
Now, if you are using PostgreSQL, you need to use the JsonType from Hibernate Types.
In order to use it in your entities, you will have to declare it on either class level or in a package-info.java package-level descriptor, like this:
#TypeDef(name = "json", typeClass = JsonType.class)
And, the entity mapping will look like this:
#Type(type = "json")
#Column(columnDefinition = "json")
private Location location;
If you're using Hibernate 5 or later, then the JSON type is registered automatically by the Postgre92Dialect.
Otherwise, you need to register it yourself:
public class PostgreSQLDialect extends PostgreSQL91Dialect {
public PostgreSQL92Dialect() {
super();
this.registerColumnType( Types.JAVA_OBJECT, "json" );
}
}
The JsonType works with Oracle, SQL Server, PostgreSQL, MySQL, and H2 as well. Check out the project page for more details about how you can map JSON column types on various relational database systems.

Yes, this wouldn't be a problem and is actually a fairly common practice.
In the recent years I have come to realize that sometimes, however, it is not a good idea to always build your views based on your domain directly. You can take a look at this post:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
It is also known as "Presentation Model":
http://martinfowler.com/eaaDev/PresentationModel.html
The idea behind that is basically the following:
Imagine you have the domain entry User, who looks like that :
#Entity
#Data
public class User {
#Id private UUID userId;
private String username;
#OneToMany private List<Permission> permissions;
}
Let's now imagine you have a view where you wanna display that user's name, and you totally don't care about the permissions. If you use your approach of immediately returning the User to the view, Hibernate will make an additional join from the Permissions table because event though the permissions are lazily loaded by default, there is no easy way to signal to the jackson serializer or whatever you are using, that you don't care about them in this particular occasion, so jackson will try to unproxy them (if your transaction is still alive by the time your object is put for json serialization, otherwise you get a nasty exception). Yes, you can add a #JsonIgnore annotation on the permissions field, but then if you need it in some other view, you are screwed.
That a very basic example, but you should get the idea that sometimes your domain model can't be immediately used to be returned to the presentation layer, due to both code maintainability and performance issues.

We were using such approach to simplify design and get rid of many dtos (we were abusing them too much). Basically, it worked for us.
However, in our REST model we were trying to do not expose other relations for an object as you can always create another REST resources to access them.
So we just put #JsonIgnore annotations to relations mappings like #OneToMany or #ManyToOnemaking them transient.
Another problem I see that if you still like to return these relations you would have to use Join.FETCH strategy for them or move transaction management higher so that transaction still exists when a response is serialized to JSON (Open Session In View Pattern).
On my opinion these two solutions are not so good.

You can map the json request without using any library at REST web-services (Jersy)
this sample of code:
This hibernate entity called book:
#Entity
#Table(name = "book", schema = "cashcall")
public class Book implements java.io.Serializable {
private int id;
private Author author; // another hibernate entity
private String bookName;
//setters and getters
}
This web-services function
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String addBook(Book book) {
String bookName=book.getName();
return bookName;
}
This is sample json request:
{
"bookName" : "Head First Java",
"author" : {
"id" : 1
}
}

Since you are just starting, perhaps you could use Spring Data REST?
This is the project: http://projects.spring.io/spring-data-rest/
And here are some simple examples:
https://github.com/spring-projects/spring-data-book/tree/master/rest
https://github.com/olivergierke/spring-restbucks
As you can see in the examples, there are no extra DTOs beyond the #Entity annotated POJOs.

Related

What is the proper way to create and validate an Entity Model and its DTO in a RESTful API?

I am developing my first RESTful API from scratch and with Spring Boot.
I have already created the endpoints, models and JPA repositories for "standalone" entities. But now that I started linking them together and after doing some research I got to the conclusion that I may have to create DTOs. I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know). I am thinking about creating a DTO by just replacing the class relations with just IDs.
This is how my entity is currently defined:
#Data
#Entity
#Table(name = "Orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#NotBlank
#NotNull
private String description;
#NotBlank
#NotNull
private Status status;
#NotNull
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "employee_id_fk"))
private Employee employee;
#NotNull
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "customer_id_fk"))
private Customer customer;
protected Order() {}
public Order(String description) {
this.description = description;
this.status = Status.IN_PROGRESS;
}
}
And my endpoint (this is what I must change):
#PostMapping("/orders")
ResponseEntity<EntityModel<Order>> createOrder(#Valid #RequestBody Order order) {
order.setStatus(Status.IN_PROGRESS);
Order newOrder = repository.save(order);
return ResponseEntity
.created(linkTo(methodOn(OrderController.class).getOrder(newOrder.getId())).toUri())
.body(assembler.toModel(newOrder));
}
Now, how should I validate the requests with this format?
Previously, as you can see, I would simply use #Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (#NotNull, #NotBlank, etc.). Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests. I also can't remove the validations from the entity model because I'm using Hibernate to map them to tables.
Great questions!
I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know).
You're right. It's not because we can save bits and bytes (as it may look like), but because the lesser information you can ask from the client, the better the experience he/she would get (whether it's an external integrator or front-end/back-end application within the same company). Fewer amounts of data to encompass = easier to comprehend and less room for an error. It also makes your API cleaner from the design perspective. Is it possible to process your request without the field? Then it shouldn't be in your API.
Now, how should I validate the requests with this format? Previously, as you can see, I would simply use #Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (#NotNull, #NotBlank, etc.).
You can also use #Valid to kick in validation for DTO inside the controller within the method mapped to endpoint. But as you mentioned correctly, all validated fields within DTO should be annotated with #NotNull, #NotBlank, etc. As a solution, to the "duplication" problem, you can create a base class and define all validations in there and inherit DTO and Entity from it. But please, don't do that!
Having the same fields and validation rules within DTO and Enity isn't considered duplication since they are separate concepts and each one of serves its specific purpose within its layer (DTO - top tier, Entity - most often lowest, Data tier). There are a lot of examples demonstrating it (e.g. here and here)
Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests.
It's a best practice to validate the request and a lot of projects are following it. In your example, it's very straightforward (direct mapping from DTO to Entity), but very often you would have a service layer that does some business logic before handing it off to a data layer (even in your example I recommend moving out your code from controller to a service layer). You don't want malformed request pass beyond the controller to handle it later with excessive if statements, null checks (that leads to a defensive code that's hard to follow and it's also error-prone).
Another note: you shouldn't sacrifice client experience and tell them or force yourself to add two more fields because it allows having one Object serving as DTO and Entity and simplifies development.
The last note: To map fields from DTO to Entity you can use one of the object mapper libraries.

Do you directly pass hibernate / jpa entity-objects via http or is there an better alternative?

first of all I want to state that I'm fairly new to this JPA / Hibernate thing and also micronaut is new for me (I have much experience in for example PHP, Python, "Desktop Java", C++ and so on).
I cannot get rid of the feel that I'm misunderstanding something in the concept when I'm passing entities via the micronaut #Body annotation as they come in detached and I have to merge them into the persistence context manually using em.merge(entity).
Please consider the following example (shortened to just show the important portions):
MyEntity.java:
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Entity
#Table(name = "entities")
#Getter
#Setter
#ToString
public class Entity{
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#Column
protected String name;
}
MyController.java:
import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.*;
#Controller("/entities")
public class EntitiyCrudController {
#Inject
private final EntityManager entityManager;
#Post("/create")
public HttpResponse create(#Body Entity entity) {
try {
entity = entityManager.merge(entity);
return HttpResponse.ok(entity);
} catch (ConstraintViolationException exception) {
return HttpResponse.badRequest();
}
}
}
Please note that the samples are just copied together, so there might be some errors, but I think this is more a question that is about the right approach to choose.
During my research all examples I've seen so far are about persisting entities that are not detached. Sometimes I've seen people using DTOs (exact copies of the entity classes on the property level but without the jpa / hibernate annotations). I think this approach also have some smell as I have to build a whole new class which has the exact same properties.
I would have no big problem using DTOs if there weren't the transfering of the properties of the dto to the entity (calling dto getter, calling entity setter). Is there a "clean" way to do this without transfering every property manually (maybe some reflection based library or an annotation processor). Then I could load an entity from the persistence context via the dto id and then update my entity fields and trigger an update.
In summary my problems are:
- Passing Entities directly is leading to the entities being detached (So for example the micronaut- data package cannot do updates this way)
- Using DTOs one ends up copying property by property which is very verbose
So which is the right way posting an object via json to a webserver and saving or updating it?
If something isn't understandable I'm happy to clarify it!
IMO DTO approach is the way to go ahead when passing data to UI layer from persistence layer.
Try using Dozer entity copier (https://www.baeldung.com/dozer). This will help you reduce lot of boiler-plate code of copying from one entity to another.
The approach you describe has nothing wrong by itself. But you have to be aware what you are doing, you are coupling your rest interface with your DB implementation. Deppending on your requirements this will probably be a problem. For instance, if your application need Optimistic Concurrency, you will need to add a #Version field in your entity, and you will automatically expose this information through the web service.
Generally you want to have control over the data that is exposed and generally you want to decouple the DB implementation of the rest API. And by the way, generally you also want to have a domain object with business logic for validation, business rules...

Spring Data Rest - PUT on repository silently fails on child references

I use Spring Data Rest with Spring Boot 2.1.1.RELEASE.
I have a class User with a #ManyToMany relationship to a class Skill.
When I make a POST to create a user with his skills, everything works finely.
When I make a PUT to update a user, the skills are not updated, no error is produced.
But when I make a PATCH instead of a PUT, the skills are correctly updated.
Have anyone met a similar issue?
I have found another (old) question about it, but there isn't solution (Spring Data Rest - PUT is not working for associated reference types?)
I probably have missed something, somewhere...
(Code using Lombok)
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class User {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
#ManyToMany
#JoinTable(name="user_skills")
private List<Skill> skills = new ArrayList<>();
}
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString
public class Skill {
#Id
#GeneratedValue
private Long id;
private String name;
}
I make a PUT with the following JSON content:
{
"id": 7,
"firstName": "John",
"lastName": "Doe",
"skills": ["http://localhost:9001/skills/1", "http://localhost:9001/skills/2", "http://localhost:9001/skills/3"]
}
The firstName or lastName can be modified, but the skills remain unmodified.
If I do a PATCH with the same payload, the skills are correctly modified.
It should work with a PUT, doesn't it?
After more investigations, it seems that this behaviour is by purpose: PUT does not update the resource links, only the main attributes.
The answer from Oliver Gierke is here: https://jira.spring.io/browse/DATAREST-1001?focusedCommentId=135791&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-135791:
I looked into this and I'd argue you're expecting things to work in a way they don't work. PUT requests don't consider associations to linkable resources, i.e. related resources that are pointed to by links. The reason for that is two-fold:
If we consider URIs for association fields in the payload to update those associations, the question comes up about what's supposed to happen if no URI is specified. With the current behavior, linked associations are simply not a part of the payload as they only reside in the _links block. We have two options in this scenario: wiping the associations that are not handed, which breaks the "PUT what you GET" approach. Only wiping the ones that are supplied using null would sort of blur the "you PUT the entire state of the resource".
For all the reasons mentioned in 1. there are dedicated assoctiation resources exposed that can be manipulated directly.
So it looks like that if you want to change both state of the resource plus associations at the same time, I guess exposing a dedicated resource to do that is the way to go.
Other posts and links:
"Unable to update associated resource using PUT request on the item resource": https://jira.spring.io/browse/DATAREST-1001
"Spring Data Rest PUT v.s PATCH LinkableResources" : Spring Data Rest PUT v.s PATCH LinkableResources
"PUT behaving like PATCH for nested collections": https://jira.spring.io/browse/DATAREST-1012

How to maintain bi-directional relationships with Spring Data REST and JPA?

Working with Spring Data REST, if you have a OneToMany or ManyToOne relationship, the PUT operation returns 200 on the "non-owning" entity but does not actually persist the joined resource.
Example Entities:
#Entity(name = 'author')
#ToString
class AuthorEntity implements Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id
String fullName
#ManyToMany(mappedBy = 'authors')
Set<BookEntity> books
}
#Entity(name = 'book')
#EqualsAndHashCode
class BookEntity implements Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id
#Column(nullable = false)
String title
#Column(nullable = false)
String isbn
#Column(nullable = false)
String publisher
#ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
Set<AuthorEntity> authors
}
If you back them with a PagingAndSortingRepository, you can GET a Book, follow the authors link on the book and do a PUT with the URI of a author to associate with. You cannot go the other way.
If you do a GET on an Author and do a PUT on its books link, the response returns 200, but the relationship is never persisted.
Is this the expected behavior?
tl;dr
The key to that is not so much anything in Spring Data REST - as you can easily get it to work in your scenario - but making sure that your model keeps both ends of the association in sync.
The problem
The problem you see here arises from the fact that Spring Data REST basically modifies the books property of your AuthorEntity. That itself doesn't reflect this update in the authors property of the BookEntity. This has to be worked around manually, which is not a constraint that Spring Data REST makes up but the way that JPA works in general. You will be able to reproduce the erroneous behavior by simply invoking setters manually and trying to persist the result.
How to solve this?
If removing the bi-directional association is not an option (see below on why I'd recommend this) the only way to make this work is to make sure changes to the association are reflected on both sides. Usually people take care of this by manually adding the author to the BookEntity when a book is added:
class AuthorEntity {
void add(BookEntity book) {
this.books.add(book);
if (!book.getAuthors().contains(this)) {
book.add(this);
}
}
}
The additional if clause would've to be added on the BookEntity side as well if you want to make sure that changes from the other side are propagated, too. The if is basically required as otherwise the two methods would constantly call themselves.
Spring Data REST, by default uses field access so that theres actually no method that you can put this logic into. One option would be to switch to property access and put the logic into the setters. Another option is to use a method annotated with #PreUpdate/#PrePersist that iterates over the entities and makes sure the modifications are reflected on both sides.
Removing the root cause of the issue
As you can see, this adds quite a lot of complexity to the domain model. As I joked on Twitter yesterday:
#1 rule of bi-directional associations: don't use them… :)
It usually simplifies the matter if you try not to use bi-directional relationship whenever possible and rather fall back to a repository to obtain all the entities that make up the backside of the association.
A good heuristics to determine which side to cut is to think about which side of the association is really core and crucial to the domain you're modeling. In your case I'd argue that it's perfectly fine for an author to exist with no books written by her. On the flip side, a book without an author doesn't make too much sense at all. So I'd keep the authors property in BookEntity but introduce the following method on the BookRepository:
interface BookRepository extends Repository<Book, Long> {
List<Book> findByAuthor(Author author);
}
Yes, that requires all clients that previously could just have invoked author.getBooks() to now work with a repository. But on the positive side you've removed all the cruft from your domain objects and created a clear dependency direction from book to author along the way. Books depend on authors but not the other way round.
I faced a similar problem, while sending my POJO(containing bi-directional mapping #OneToMany and #ManyToOne) as JSON via REST api, the data was persisted in both the parent and child entities but the foreign key relation was not established. This happens because bidirectional associations need to be manually maintained.
JPA provides an annotation #PrePersist which can be used to make sure that the method annotated with it is executed before the entity is persisted. Since, JPA first inserts the parent entity to the database followed by the child entity, I included a method annotated with #PrePersist which would iterate through the list of child entities and manually set the parent entity to it.
In your case it would be something like this:
class AuthorEntitiy {
#PrePersist
public void populateBooks {
for(BookEntity book : books)
book.addToAuthorList(this);
}
}
class BookEntity {
#PrePersist
public void populateAuthors {
for(AuthorEntity author : authors)
author.addToBookList(this);
}
}
After this you might get an infinite recursion error, to avoid that annotate your parent class with #JsonManagedReference and your child class with #JsonBackReference. This solution worked for me, hopefully it will work for you too.
This link has a very good tutorial on how you can navigate the recursion problem:Bidirectional Relationships
I was able to use #JsonManagedReference and #JsonBackReference and it worked like a charm
I believe one can also utilize #RepositoryEventHandler by adding a #BeforeLinkSave handler to cross link the bidirectional relation between entities. This seems to be working for me.
#Component
#RepositoryEventHandler
public class BiDirectionalLinkHandler {
#HandleBeforeLinkSave
public void crossLink(Author author, Collection<Books> books) {
for (Book b : books) {
b.setAuthor(author);
}
}
}
Note: #HandlBeforeLinkSave is called based on the first parameter, if you have multiple relations in your equivalent of an Author class, the second param should be Object and you will need to test within the method for the different relation types.

Can Hibernate be used to store HashMaps of data without classes to represent their structure?

I have pretty much zero experience with Hibernate, though I've used similar persistence libraries in other languages before. I'm working on a Java project that will require a way to define "models" (in the MVC sense) in text configuration files, generate the database tables automatically, and (ideally) be database-backend-agnostic. As far as I can tell from some quick Googling, Hibernate is the only widely-used backend-agnostic Java database library; while I could write my own compatibility layer between my model system and multiple DB backends, that's a debugging endeavor that I'd like to avoid if possible.
My question is: Can Hibernate be used to store data whose structure is represented in some other way than an annotated Java class file, such as a HashMap with some configuration object that describes its structure? And, if not, are there any other relatively-stable Java database libraries that could provide this functionality?
EDIT: Here's a clearer description of what I'm trying to accomplish:
I am writing a data-modeling library. When a certain method of the library is called, and passed a configuration object (loaded from a text file), it should create a new "model," and create a database table for that model if necessary. The library can be queried for items of this model, which should return HashMaps containing the models' fields. It should also allow HashMaps to be saved to the database, provided their structure matches the configuration files. None of these models should be represented by actual compiled Java classes.
I think you could try use #MapKey annotation provided by JPA (not the Hibernate #MapKey annotation, it's pretty different!).
#javax.persistence.OneToMany(cascade = CascadeType.ALL)
#javax.persistence.MapKey(name = "name")
private Map<String, Configuration> configurationMap = new HashMap<String, Configuration>();
I don't believe Hibernate will let you have a Map as an #Entity but it will let you have a custom class that contains a map field:
#Entity
public class Customer {
#Id #GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
#OneToMany #JoinTable(name="Cust_Order")
#MapKeyColumn(name"orders_number")
public Map<String,Order> getOrders() { return orders; }
public void setOrders(Map<String,Order> orders) { this.orders = orders; }
private Map<String,Order> orders;
}
(example from Hibernate docs)
Additionally, you don't have to use annotations (if that is what you're trying to avoid): Hibernate relationships can be described via xml files and there are utilities (maven plugins for example) which can automatically generate the necessary java pojo's from the xml.
Does your model require a relational database? You might consider a database like Mongo that stores any object you can represent with JSON.
you can configure hibernate to work without any entity classes (beans linked to tables),
1. you need to use xml configuration for this. in place of class use entity-name and in place of <property name="something" use <property node="something".
create a hibernate session with entiy-mode as map.
you can use a map to store and retuive information from db. Remember, since you are using map there will be difficulties in 2-way mapping (this was as of 3.3, not sure if its been fixed in later releses)

Categories

Resources