Jackson #JsonBackreference for embedded or nested classes - java

I have parent child relationship build this way (came from legacy system)
#Entity
#Table
class A implements Serializable {
........
#jsonmanagedreference("test")
#OneToMany(mappedBy="PK.id", fetch = FetchType.LAZY,)
Set<B> b = new HashSet<B>()
......
setters / getters
}
#Embeddable
class PK {
#jsonbackreference("tets") // can't find it
#ManyToOne
#JoinColumns
A a
}
#Entity
#Table
class B implements Serializable {
#EmbeddedId
private PK pk;
setters / getters
}
It works fine with regular operations, the problem is I can not serialize A object because circular reference.
After I tried to add #jsonmanagedreference to A.b and #jsonbackreference to PK.a it did not work because it could not find JsonBackReference, is there a way to manage parent child relationship during serialization if child in Embedded class or nested?

I used this solution:
Read embedded object in Jackson
It worked for me, just using #JsonUnwrapped on embedded class reference and mapping the embedded id with: #JsonIdentityInfo
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "yourEmbeddedIdAtribute")

Related

Using javax #Transient annotation with a field of MappedSuperclass

Javax's #Transient annotation when applied over an #Entity class' field will prevent the annotated field from being persisted into the database as a column. Is there a method to selectively achieve this behavior (of excluding the persistence of a certain column) for a field in a MappedSuperclass?
To clarify, I want some field x present in a MappedSuperclass to be persisted for some entity classes that extend the MappedSuperclass and excluded from persistence in some other extending entity classes.
I have tried shadowing the field x in the extending class and annotating it with #Transient, however, this doesn't seem to work. Is there any alternative approach that would enable this behavior?
Yes. In the children entity class that extends #MappedSuperclass , you can configure it to use the property access for this transient field .
So assuming a given #MappedSuperclass has a #Transient field :
#MappedSuperclass
public abstract class Parent {
#Transient
protected LocalDateTime createdTs;
}
For the children entity that want to include this transient field , you could do :
#Entity
#Table(name = "foo")
public class Foo extends Parent {
#Id
private Long id;
#Access(AccessType.PROPERTY)
#Column(name= "createTs")
public LocalDateTime getCreatedTs() {
return this.createdTs;
}
}
And for the children entity that want to exclude this transient field , you could do :
#Entity
#Table(name = "foo")
public class Foo extends Parent {
#Id
private Long id;
}

Failed to lazily initialize a collection of role. Simple JPA findById

I'm using Spring and JPA (Hibernate with MySQL) and Lombok also.
Hi have this part of my entities:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "entitya")
public class EntityA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ea_id")
Long id;
....
#ManyToOne
#JoinColumn(name="g_id", nullable=false)
private Group group;
....
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "group")
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="g_id")
private Long id;
#OneToMany(mappedBy="group")
private List<EntityA> enitiesA = new ArrayList<>();
...
}
I implemented also the repository extends JPARepository.
Into my controllers, if I try to retrieve an EntityA by Id I obtain this exception:
failed to lazily initialize a collection of role: com.mytest.entity.Group.enitiesA, could not initialize proxy - no Session
For me it's strange because I need to retrieve only the object. I not use some getter methods on this. So, in theory, using the default fetch types, I don't need to have also the group list.
What's wrong?
Are you debugging your object with toString()?
In case it could be an error caused by the #Data annotation.
The generated toString() method contains all fields, so it might call the enitiesA variable, producing the lazy initialization error.
https://mdeinum.github.io/2019-02-13-Lombok-Data-Ojects-Arent-Entities/
Likely it's because you're accessing group.enitiesA outside of the transactional boundaries. If you want to do this, you can eager fetch them by adding eager fetch type to your OneToMany mapping such as
#OneToMany(mappedBy="group", fetch = FetchType.EAGER)
This will load the entire object graph when the parent is loaded.
If you still want to do lazy loading, look to encapsulate all of the calls into the children under the session that loaded the parent.

Spring-Data Jpa Inheritance: Keeping Entity Id's in Children Entity

I'm dealing with a couple of Entities with Tree like structures that were getting more complicated so I decided to create an abstract class for it so code was a bit more mainainable:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class TreeStructure<T extends TreeStructure>
{
#ManyToOne
protected T parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
protected Set<T> children = new HashSet<>();
//...
Then I have two Entities which extend it:
#Entity(name = "TreeStructureOne")
public class TreeStructureOne extends TreeStructure<TreeStructureOne>
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty("TreeStructureOne_id")
private long id;
And I basically want the database to be completely unaware of this TreeStructure abstraction and save all of the fields in each Entities tableand expected InheritanceType.TABLE_PER_CLASS to deal with that. But it seems I need to define the Id in the TreeStructure Entity at least or I get:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: TreeStructure
And I don't want to add an ID into the abstract class since this makes three tables in the database called: HT_TREE_STRUCTURE, HT_TREE_STRUCTURE_ONE and HT_TREE_STRUCTURE_TWO with one field ID each one.
Is there any solution to that?
Since TreeStructure is not an #Entity use only #MappedSuperclass
#MappedSuperclass
public abstract class TreeStructure<T extends TreeStructure> {
instead of #Entity and #Inheritance for the parent class.
You can find #MappedSuperclass in the Oracle JEE API documentation.

#AssociationOverride does not create #JoinColumn from scratch

I have a group of tables, that are all identical apart from their owner table, and the corresponding foreign keys to that table. I made it all generic thanks to Hibernate/JPA, but cannot pass the #JoinColumn information via #AssociationOverride since the name value for it is ignored, or not overridden at all.
For example;
#Data
#Entity
#Table(name = "etc")
#AssociationOverride(name = "parent", joinColumns = #JoinColumn(name = "id"))
public class RealEntity extends BaseEntity<ParentEntity, String> {
}
with;
#Data
#MappedSuperClass
public class BaseEntity<K, P> implements Serializable {
#EmbeddedId
protected Key<K> key = new Key<>();
#MapsId("fk")
#ManyToOne
#JsonBackReference
protected P parent;
#Data
#Embeddable
public static class Key<K> implements Serializable {
protected K fk;
#Column(name = "sub_id")
protected String subId;
}
}
parent:
#Data
#Entity
#Table(name = "parentc")
public class ParentEntity implements Serializable {
#Column(name = "id")
protected String parentId;
}
So as you see, it works well except for the parent's fk reference definition, I get mapping error since Hibernate tries to find a parent_id, rather than just id for the foreignKey, since #JoinColumn override is ignored. It works if I put the parent information in the RealEntity directly (obviously), or if I put #JoinColumn(name = "id") on parent in BaseEntity but I want to keep it as generic as possible. Is there any solution to this issue? Or should I just give up?
edit: it seems when I put a proper #JoinColumn with acceptable mapping for joining on parent in BaseEntity, that does get overridden, so it needs something valid to override. I cannot just add an association from nothingness is that the case? I've seen many examples on the web where they were putting associations from scratch, my usage of #MapsId, might be breaking the usage I guess. But I cannot change my current structure, since it is necessary to be able to represent composite foreign key definition for dependent child tables... I feel like there is a very simple solution, or some hacky way to achieve what I want, and I cannot seem to find it!

Spring MVC + Hibernate & Jackson = Could not write JSON: Infinite recursion

I know this has been asked a lot of times, and the solution is pretty obvious, but in my case it doesn't really work, and I can't figure out how to solve it.
problem: Could not write JSON: Infinite recursion (StackOverflowError)
The setup is like this:
Employee belongs to a Department. (ManyToOne)
Department has a Employee as Manager. (OneToOne)
I didn't want to have a #OneToMany List in Department so the owning side is missing.
Employee has a Department object, but this is the Department it belongs to, not the Department he manages.
Employee.java
#Entity
#Table(name = "ts_employee")
//#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="#emp_id")
public class Employee extends AbstractEntity {
#ManyToOne
#JoinColumn(name = "dept_id")
#JsonManagedReference
private Department department;
... getters and setters
}
Department.java
#Entity
#Table(name = "ts_department")
//#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#dept_id")
public class Department extends AbstractEntity {
#OneToOne
#JoinColumn (name = "manager_id")
#JsonBackReference
private Employee manager;
.. other fields.. getters and setters
}
AbstractEntity.java
#MappedSuperclass
public class AbstractEntity implements Serializable {
#Id
#GeneratedValue()
private Long id;
... getters and setters
}
I've tried both solutions:
#JsonBackReference + #JsonManagedReference
I get rid of the StackOverflow, but the Department.manager is not serialized (#JsonBackReference), and not sent in the response, which in bad.
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#emp_id"), which doesn't seem to do anything, and StackOverflow is thrown in my face :(
How can I solve this, hopefully without modifying the model classes?
Thanks a lot :)
The #JsonBackReference wont be serialized.
If possible try to use #JsonIdentityInfo over #JsonManagedReference and #JsonBackReference. Follow this link for documentation.
You can also try using #JsonIgnore if you don't need to maintain the relationship further in the process.

Categories