Two way relationship with JsonBackReference & JsonManagedReference - java

ANSWER
I'll solve my problem regarding to this blog Jackson – Bidirectional Relationships
Thanks you.
UPDATE 2
The problem is about JsonBackReference and JsonManagedReference annotations.
With my two way relationship, I have to explicitly select one way for serialization with JsonBackReference and JsonManagedReference.
But here, I am in case to use the opposit way "Parent->Child" for a specific requierement (using the way "Child->Parent" by default)
When I inversed those two annotations, my JSON is what I'm looking for, for the special requierment.
Any idea on how to use JACKSON in a two way relationship ?
Thank you.
UPDATE 1
Here is a code simple using EntityGraph (thanks to #NeilStockton suggestion), but still don't serialize the lazy attribute in JSON :-(
Parent
#Entity
public class Parent {
#Id
#GeneratedValue
private Long id;
#column
private String parentAttribute;
#OneToOne(mappedBy = "parent", optional = false)
#JsonBackReference
private Child child;
Child
#Entity
public class Child {
#Id
#GeneratedValue
private Long id;
#column
private String childAttribute;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JsonManagedReference
private Parent parent;
Parent Repository
public interface ParentRepository extends CrudRepository<Parent> {
#EntityGraph(attributePaths = { "child" })
//a hack to use findAll with default lazy/eager mapping
Collection<Parent> findByIdNotNull();
}
Generated query :
Hibernate:
select
parent0_.id as id1_33_0_,
child1_.id as id1_32_1_,
parent0_.parent_attribute as parent_attribute2_33_0_,
child1_.child_attribute as child_attribute2_32_1_,
from
test.parent parent0_
left outer join
test.child child1_
on parent0_.id=child1_.parent_id
where
parent0_.id is not null
JSON (no child):
[ {
"id": 1
"parentAttribute": "I am the parent"
} ]
Any idea on how to force Jackson Hibernate4Module to serialize if present ?
Thank you.
I have a Spring Boot 1.3.1 back-office using JPA/hibernate for mapping entities. The front-end is an Angular2 application. The communication is a REST/JSON.
My question is about forcing EAGER loading in some queries when I have a Lazy relationship.
The solution using JOIN FETCH helped me in DAO layer (Repositories). The entity is now completely loaded in a single query as I want in controllers layer. But the serialized JSON still incomplete due to Hibernate4Module.
Bellow Hibernate4Module features can't help :-(
FORCE_LAZY_LOADING
USE_TRANSIENT_ANNOTATION
SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS
REQUIRE_EXPLICIT_LAZY_LOADING_MARKER
REPLACE_PERSISTENT_COLLECTIONS
Any idea is welcome. Thanks.

Finally, I solved my problem by using Custom Projection with a constructor in the select part of the query. In the new projection class, there is no "JsonIgnore" or any JPA annotation that make field not serialized by Jackson. I added more data in that projection for reach use.
Hope it'll help.

Related

Lazy loading with CrudRepository

I have a simple parent-child relation (one to many). And I am retrieving parents in two endpoints. One to get a list of parents and one to get a single parent. On the list, I don't want to serialize a list of children for each parent. I want to show a list of children only when I am getting a single parent.
I am working with spring boot and CrudRepository.
I tried
spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
also fetch = FetchType.LAZY and in the end, I was writing custom HQL with FETCH keyword.
Parent Model
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id")
private List<Child> children;
Child Model
#Column(name = "parent_id")
#JsonIgnore
private Long parentId;
Parent Repository
public interface ParentRepository extends CrudRepository<Parent, Long>
Parent Service
List<Parent> findAll() {
return StreamSupport
.stream(repository.findAll().spliterator(), false)
.collect(Collectors.toList());
}
As I said I want to sometimes serialize a list of children in this relation and in some cases not.
You may be able to do this using JsonView or JsonFilter with Jackson. Another option would be to separate the concerns, i.e. use a DTO to control the serialized view of the entity.
Here is a link to a tutorial on some of these Jackson features: https://www.baeldung.com/jackson-serialize-field-custom-criteria
Most likely it’s due to the default Open Session in View. Starting with version 2.0
If you added below line in your application.properties configuration file, then it should work
spring.jpa.open-in-view=false
Ok, I finally have done it. I wrote a dedicated HQL for Parent without children.
#Query("SELECT new Parent(a.parentId, a.name, a.address, a.city) FROM Parent a WHERE a.otherId= :id")
List<Parent> findAllBySomeOtherIdWithoutChildren(#Param("id") Long id);
It looks like this is the fastest way for it. Thanks for all suggestions.

spring boot jpa manyToOne(column="fatherId") join the same entity works on a recursive way until the fatherId is null

Entity{
String code;
String parentCode;
...
#ManyToOne
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
My entity class is like this. what i want to do is using findAll() to get an entity list with each entity get its own direct parent. But spring jpa will get parent's parent until the root , i need to avoid this.
Thank you!
Since a default fetch type for a #ManyToOne relation is an FetchType.EAGER I think you have just add a fetch type as LAZY explicitly:
Entity{
String code;
String parentCode;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
It's not about Spring JPA but JPA itself. When you add a relationship, following defaults apply unless specified otherwise.
#xxToOne - FetchType.EAGER
#xxToMany- FetchType.LAZY
So, in your example you have a #ManyToOne which has a default EAGER fetch and one join query is appended. If your parent has another #xxToOne it adds one more join and so on. It's good to know the boundaries of your entities and decide which type of FetchType is required.
Even if you add like this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
.. if you parent has more relationships you might be getting everything loaded while fetching parent. Thus, all relationships need to be Lazy. It's a design choice based on entities.
But be aware about the ORM's N+1 problem : JPA Hibernate n+1 issue (Lazy & Eager Diff)

JPA OneToMany eager fetch does not work

I have a weird problem with two entities with one-to-many relation in JPA. I am using Glassfish 3.1.2.2 with EclipseLink 2.3.2. This is the first entity:
#NamedQueries({
#NamedQuery(name="SampleQueryGroup.findAll", query="SELECT g FROM SampleQueryGroup g")
})
#Entity
public class SampleQueryGroup implements Serializable {
// Simple properties, including id (primary key)
#OneToMany(
mappedBy = "group",
fetch = FetchType.EAGER,
cascade = {CascadeType.REMOVE, CascadeType.MERGE}
)
private List<SampleQuery> sampleQueries;
// Gettes/setters, hashcode/equals
}
And this is the second one:
#Entity
public class SampleQuery implements Serializable {
// Simple properties, including id (primary key)
#ManyToOne(cascade = {CascadeType.PERSIST})
private SampleQueryGroup group;
// Gettes/setters, hashcode/equals
}
I have a stateless session bean which uses an injected EntityManager to run SampleQueryGroup.findAll named query. I also have a CDI managed bean which calls the SSB method and iterates through SampleQueryGroup.getSampleQueries() for each SampleQueryGroup returned by the method. I didn't paste the code as it is pretty straightforward and somehow standard for any Java EE application.
The problem is the eager fetch does not work and getSampleQueries() returns an empty list. However, when I change the fetch type back to FetchType.LAZY, everything works and I get the list correctly populated. I don't understand why this happens. Does it have anything to do with internal caching mechanisms?
My guess is that when you add a new SampleQuery you are not adding it to the SampleQueryGroup sampleQueries, so when you access it, it is not their. When it is LAZY you do not trigger it until you have inserted the SampleQuery, so then it is there.
You need to maintain both sides of your relationships. (you could also disable caching, or refesh the object, but your code would still be broken).
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side

How can I access the underlying column after defining a #ManyToOne relationship on it in Spring?

I'm using Spring 3.2 with Roo 1.2.3 to build a database-backed Java application via Hibernate. I have several bidirectional OneToMany/ManyToOne relationships among the tables in my database. When I set up the ManyToOne side of the relationship using #JoinColumn (via "field reference" in Roo), a new field whose type is the related entity (the "one" in ManyToOne) is created. However, once this is done, there seems to be no way to access the underlying column value on which the ManyToOne relationship is based. This is a problem when the underlying join column contains data needed by the application (i.e. when the join column contains product stock numbers).
Is there any way to set up my entity class so that the column on which its ManyToOne relationship is based remains accessible without traversing the new join property? How can I define an accessor method for the value of this column?
I've been looking online for an answer to this question for several days, but to no avail. Thanks in advance for your help.
just map the column a second time with insertable=false and updateable=false
To make it more concrete. It's possible to do a HQL-SELCT and restrict a ManyToOne relationship, without any join in the resulting SQL:
Instead of using a join in
session.createQuery("FROM Person person WHERE person.adress.id = 42")
we use can use the adress_idcolumn
session.createQuery("FROM Person person WHERE person.adressId = 42")
This works, if you specify an additional adressId field, which is only used as mapping info for Hibernate:
#Entity
#Access(AccessType.FIELD)
public class Person{
#Id
String id;
#JoinColumn(name = "adress_id")
#ManyToOne(fetch = FetchType.LAZY)
#Nullable
public Adress adress;
#Column(name = "adress_id", insertable = false, updatable = false)
private String adressId;
}
#Entity
#Access(FIELD)
public class Adress{
#Id
String id;
}
The AccessType.FIELD is not needed (But we can leave getters/setters in example). The FetchType.LAZY and #Nullable are also optional, but make it clear when it makes sense to use it. We are able to load Person entities which have a specific Address (we know the address id). But we don't need a join because it's not needed for the WHERE-clause and not for the initial fetch (the address can be fetched lazy).

EJB3/JPA entity with an aggregated attribute

I wanted to know if there is a way to get in a One2Many relationship a field of the One side that is an aggregate of the Many side.
Let's take the following example:
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
// Here I don't know how to Map the latest B by date
private B latestB;
// Acceptable would be to have : private Date latestBDate;
}
#Entity
public class B {
#Id
private Long id;
private Date date;
#ManyToOne (targetEntity=A.class)
private A parentA;
}
My question is how can I make the mapping of the field latestB in the A entity object without doing any de-normalization (not keeping in sync the field with triggers/listeners)?
Perhaps this question gives some answers, but really I don't understand how it can work since I still want to be able to fetch all childs objects.
Thanks for reading/helping.
PS: I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
PS2: Or just tell me that I should not do this (with arguments of course) ;-)
I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
Implementing the acceptable solution (i.e. fetching a Date for the latest B) would be possible using a #Formula.
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
#Formula("(select max(b.some_date) from B b where b.a_id = id)")
private Date latestBDate;
}
References
Hibernate Annotations Reference Guide
2.4.3.1. Formula
Resources
Hibernate Derived Properties - Performance and Portability
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Filtering.2C_Complex_Joins
Basically JPA does not support this, but some JPA providers do.
You could also,
- Make the variable transient and lazy initialize it from the OneToMany, or just provide a get method that searches the OneToMany.
- Define another foreign key to the latest.
- Remove the relationship and just query for the latest.

Categories