Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson: referencing an object as a property

In my java spring application, I am working with hibernate and jpa, and i use jackson to populate data in DB.

Here is the User class:

@Data
@Entity
public class User{

    @Id
    @GeneratedValue
    Long id;

    String username;
    String password;
    boolean activated;

    public User(){}
}

and the second class is:

@Entity
@Data
public class Roles {

    @Id
    @GeneratedValue
    Long id;

    @OneToOne
    User user;

    String role;

    public Roles(){}


}

In the class Roles i have a property of User and then i made a json file to store the data:

[ {"_class" : "com.example.domains.User", "id": 1, "username": "Admin", "password": "123Admin123","activated":true}
,
  {"_class" : "com.example.domains.Roles", "id": 1,"user":1, "role": "Admin"}]

Unfortunately, when i run the app it complains with:

.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.example.domains.User: no int/Int-argument constructor/factory method to deserialize from Number value (1)
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.example.domains.Roles["user"])

The problem comes from

{"_class" : "com.example.domains.Roles", "id": 1,"user":1, "role": "Admin"}

and when i remove the above line the app works well.

I think, it complains because it cannot make an instance of user. So, how can i fix it?

like image 551
Jeff Avatar asked Feb 01 '17 21:02

Jeff


1 Answers

Do yourself a favor and stop using your Entities as DTOs!

JPA entities have bidirectional relations, JSON objects don't, I also believe that the responsibilities of an Entity is very different from a DTO, and although joining these responsibilities into a single Java class is possible, in my experience it is a very bad idea.

Here are a couple of reasons

  • You almost always need more flexibility in the DTO layer, because it is often related to a UI.
  • You should avoid exposing primary keys from your database to the outside, including your own UI. We always generate an additional uniqueId (UUID) for every publicly exposed Entity, the primary key stays in the DB and is only used for joins.
  • You often need multiple views of the same Entity. Or a single view of multiple entities.
  • If you need to add a new entity to a relation with an existing, you will need find the existing one in the database, so posting the new and old object as a single JSON structure has no advantage. You just need the uniqueId of the existing, and then new.

A lot of the problems developers have with JPA, specifically with regards to merging comes from the fact that they receive a detached entity after their json has been deserialized. But this entity typically doesn't have the OneToMany relations (and if it does, it's the parent which has a relation to the child in JSON, but in JPA it is the child's reference to the parent which constitutes the relationship). In most cases you will always need to load the existing version of the entity from the database, and then copy the changes from your DTO into the entity.

I have worked extensively with JPA since 2009, and I know most corner cases of detachment and merging, and have no problem using an Entity as a DTO, but I have seen the confusion and types of errors that occur when you hand such code over to some one who is not intimately familiar with JPA. The few lines you need for a DTO (especially since you already use Lombok), are so simple and allows you much more flexibility, than trying to save a few files and breaking the separation of concerns.

like image 193
Klaus Groenbaek Avatar answered Oct 20 '22 10:10

Klaus Groenbaek