Lazy One-To-One Spring JPA and building “dynamic” JSON

By | December 24, 2017
Questions:

I’m developing a relatively large project using Spring Boot, and in a general way I’m pretty happy with it, but I’m having some problems that in my mind should’t be a problem.

  1. First of all, One-To-One Relationship. It’s frustrating that it doesn’t work as it should (at least in my mind).

    I have two entities, User and UserProfile, for example. They have One-To-One relationship, but most of the time I only need the User data, but it fetches (no matter what I try, and oh boy, I tried the world suggestions on every post for 5 pages of Google).

    So there is my first question, is there a way to be able to lazy fetch One-To-One relationship in JPA and Spring? (Because most of the posts are more than 2-3 years old).

  2. The other problem I have is about to build a JSON response in a “dynamic” way. I did some stuff using Rails and was very happy with JBuilder or even the to_json that gave me the ability to build the json response depending on the controller and my needs at the moment.

    In Spring I saw the following solutions:

    • Jackson @JsonView (which doesn’t solve entirely my problem because the responses are not that static and a attribute can’t be assigned to multiple views (as far as I understood the concept));
    • setting to null attributes that I don’t want on the response (using this, but I’s just too ugly and looks like a wrong walkthrough);
    • or building HashMap like I build .json.jbuilder on Rails (but this kills my performance as sometimes it has relationships so a lot of for to build the json, and also this looks like a ugly walkthrough).

I’m looking for some directions from someone that someday may have encountered one of this problems because it’s killing me not being able so solve problems that in my mind should not be this hard.

EDIT 1

Already tried to add optional = false on the @OneToOne annotation to solve the Eager load of OneToOne relationship as @snovelli suggested. Example:

@OneToOne(optional=false, fetch = FetchType.LAZY)
public UserProfile getUserProfile(){ ... }
Answers:

If the join column is not in the table to which a parent in a one-to-one association is mapped, then the association cannot be lazy. The reason is that JPA provider cannot determine whether to create the proxy, so that it can load the object when accessed later, or leave the null value.

Even if the association is not optional, JPA provider has to determine the id of the associated entity instance to store it in the proxy. Thus it has to go to the associated table anyway.

Solutions:

  1. Byte code instrumentation. Not widely adopted approach though.
  2. Use one-to-many and handle empty list as null, otherwise use list.get(0). You can of course encapsulate this in the entity class (getter returns the only element of the list or null). The drawback is that you will have to treat this as collection in JPQL queries.
  3. Use @PrimaryKeyJoinColumn instead of a foreign key. If the association is not optional (optional = false), then JPA provider knows that there is an associated child with the same PK, so it will just store the PK of the parent as the id of the child in the proxy. Obviously, you can’t use two independent id generators for both of the entities, otherwise PKs may be different. This is the best approach if it meets your requirements.
  4. Add the foreign key in the parent table also (making the relationship bidirectional in the database as well). The drawback is that you now basically have two independent associations which you have to maintain. Also, there is the performance cost of updating two tables instead of one (and the foreign keys have to be nullable).
  5. Map parent entity to a database view that joins parent table with the child table and contains all parent columns plus id of child table:

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "<child id in the view>", insertable = false, updatable = false)
    private Child child;
Questions:
Answers:

Regarding ‘dynamic’ JSON: Use DTOs.

This has the advantage of tailoring the objects to be serialized to the exact needs of the client consuming the resulting JSON. Also, the domain model (Hibernate entities) is decoupled from the JSON (de)serialization logic, allowing the two to evolve independently.

Questions:
Answers:

Regarding @OneToOne: are you worried more by amount of data or multiple queries against the DB?

In former case (if that’s possible when using Spring Roo) you could try a workaround with @ManyToOne relation modelling (one-to-one is a special case of many-to-one, isn’t it).

In latter case you could use @Embeddable to decompose entity like User into multiple classes while keeping the data together in the DB, so only one query would be used to fetch it.

Leave a Reply

Your email address will not be published. Required fields are marked *