JsonIgnoreProperties not working , Infinite recursion - Kotlin/Spring/ - json

Hello i have 2 tables with relation ManyToMany but i am getting this error after if fetch from the api:
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException:
My Classes are ManyToMany as example:
Class A:
#ManyToMany( fetch = FetchType.LAZY , cascade = [
CascadeType.PERSIST,
CascadeType.MERGE
])
#JoinTable(name = "material_formula_material_set",
joinColumns = [JoinColumn(name = "material_formula_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "material_set_id", referencedColumnName = "id")]
)
val materialSet: Set<MaterialSet>?,
And Class B:
#ManyToMany(mappedBy = "materialSet")
val materialFormulas: Set<MaterialFormula>?,
I also tried to use #JsonManagedReference and #JsonBackReference and JsonIgnore to one of them but still not working :\

You need to annotate a reference to your parent class in your child class with #JsonIgnore annotation. Since it is many to many relationship it might be not that obvious to see who is parent and who is the child. But in any case the infinite recursion occurs because class A references B and B in turn references A. So, lets say in B you will need to annotate reference to A with #JsonIgnore and it will solve the problem

Related

Hibernate create relation without relational table

Is it possible to map hibernate entities without using another table that is mapping them ?
When I create #OneToMany and #ManyToOne relation between 2 entities, hibernate always creates another table in the database to map the relation, I would like to map 2 entities directly using column in the mapped entity like this:
"InstalledApp" entity:
#OneToMany (fetch = FetchType.EAGER, targetEntity=InstalledAppPort.class, mappedBy = "id")
private List<InstalledAppPort> ports;
"InstalledAppPort" entity:
#ManyToOne (targetEntity=InstalledApp.class, fetch=FetchType.LAZY)
#JoinColumn(name = "iappId")
private InstalledApp iapp;
When using the code above, the list is always empty and I do not know why. Technically this mapping should work but it is not. No exception thrown.
Solved by using:
#OneToMany (fetch = FetchType.EAGER, targetEntity=InstalledAppPort.class)
#JoinColumn(name = "iappId", referencedColumnName = "id")
private List<InstalledAppPort> ports;

Hibernate Doesn't support Bidirectional #ManytoMany mapping for JSON?

If I have Three Tables, 1 user can work into Multiple projects and the same is true that one project can have multiple users. To Achive this I have three tables
1) User ---> UserEntity
2) Project ---> ProjectEntity
3) UserProjectMapping --> Join Table
Now If I try to create a bidirectional mapping and use Maven to get the JSON response using Hibernate Since I will be defining a Join in UserEntity using Join Table. On one of the entity, I will have to define #JsonbTransient, else it will go into Circular Loops and I will get Stackover flow exception.
org.eclipse.yasson.internal.serializer.AbstractDateTimeSerializer.serialize(AbstractDateTimeSerializer.java:52)
org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:95)
org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:92)
org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:61)
#ManyToMany(fetch = FetchType.EAGER,cascade= CascadeType.ALL)
#JoinTable(name= "UserProjectMapping ", joinColumns = #JoinColumn(name = "User_id"),
inverseJoinColumns = #JoinColumn(name = "Project_ID") )
List<ProjectEntity> ProjectList = new ArrayList<ProjectEntity>();
On Project
#ManyToMany( cascade= CascadeType.ALL, mappedBy= "ProjectList")
List<UserEntity> UserEntityList = new ArrayList<UserEntity>();
But after defining #JsonbTransient on getters I will not get the corresponding response. So let's say I define #JsonbTransient on getter of UserEntityList in ProjectEntity, I will not be getting the response of the User list when I am calling the ProjectEntity?
SO is there any way I cam make bidirectional mapping work on JSON, or is it safe to conclude JSON HIbernate don't support complete bidirectional mapping?

Hibernate: do not delete child collection on parent removal

I have one of the weird issues that make you growl. I'm having (also weird) Spring/Hibernate application, that is intended to manage database in following way (i've simplified some things, so don't be confused that source code mentions slightly different tables/columns):
active_proxy_view table:
id | entity
<uuid> | <string containing json>
archive_proxy_view table:
id | entity
<uuid> | <string containing json>
track_reference table:
ref_type | ref_id | track_domain | track_type | track_id |
'proxy' | <uuid> | 'example.com' | 'customer' | '123' |
Keeping two tables is mandatory - i need to have both all-time-history/statistical queries and business-value queries only for things that being active right now, so i need to keep set for active proxies tight. track_reference table is used for searches so i could do queries like that:
SELECT p.id, p.entity FROM archive_proxy_view AS p
INNER JOIN track_reference AS t1 ON
t1.ref_id = p.id AND
t1.ref_type = 'proxy' AND
t1.track_domain = 'example.com' AND
t1.track_type = 'customer' AND
t1.track_id = '123'
INNER JOIN track_reference AS t2 ON
t2.ref_id = p.id AND
t2.ref_type = 'proxy' AND
t2.track_domain = 'example.com' AND
t2.track_type = 'campaign' AND
t2.track_id = 'halloween-2017'
(it may be not 100% correct, i haven't raw sql experience in a while)
And here's the problem:
Both active_proxy_view and archive_proxy_view entities are inherited from one class that specifies #OneToMany relationship on track_reference entity; #ManyToOne usage is not really possible, because there are many entities tied to tracking reference
track_reference is managed separately (and this is mandatory too)
I need to manage views separately from track_reference table, but whenever i tell Hibernate to remove entity from active_proxy_view table, it takes away track_reference entities as well. Even if i play with cascade annotation value, which is blank by default (and as i understand, it means that child records should not be deleted with parent). There is possibility that i've missed something, though.
I also failed to hack the whole thing using custom #SQLDeleteAll, i still can see regular deletes in general log:
55 Query delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='agent'
55 Query delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='lead'
55 Query delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='source'
53 Query DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = '13c6b55c-f9b7-4de7-8bd4-958d487e461c' AND 1 = 0
I'm using Hibernate 5.2.3.Final through Spring 4.3.2.RELEASE / Spring Data JPA 1.10.2.RELEASE
TL; DR
So, the question is: how do i prevent Hibernate from deleting associated entities when parent is deleted?
The source code for entities looks like this:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class ProxyViewEntryTemplate {
#Id
#NotNull
#Column(nullable = false)
private String id;
#NotNull
#Column
private String entity;
// some other columns
#OneToMany
#JoinColumn(name = TrackRef.REFERENCE_ID_COLUMN_NAME) // 'reference_entity_id`
#Where(clause = ProxyView.TRACK_WHERE_JOIN_CLAUSE) // `referenced_entity_type` = 'proxy'
#SQLDeleteAll(sql = ProxyView.TRACK_DELETE_ALL_QUERY) // DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = ? AND 1 = 0
private Collection<TrackingReference> track = new ArrayList<>();
// setters, getters, hashCode, equals
}
#Entity
#Table(name = "active_proxy")
public class ActiveProxyViewEntry extends ProxyViewEntryTemplate {}
#Entity
#Table(name = "tracking_reference")
#IdClass(TrackingReferenceId.class)
public class TrackingReference {
#Id
#Column(name = "tracked_entity_type", nullable = false)
#NotNull
private String trackedType;
#Id
#Column(name = "tracked_entity_domain", nullable = false)
private String trackedDomain;
#Id
#Column(name = "tracked_entity_id", nullable = false)
private String trackedId;
#Id
#Column(name = "referenced_entity_type", nullable = false)
#NotNull
private String referencedType;
#Id
#Column(name = "referenced_entity_id", nullable = false)
#NotNull
private String referencedId;
// setters, getters, hashCode, equals
}
The whole thing is managed through Spring JPA Repositories:
#NoRepositoryBean
public interface SuperRepository<E, ID extends Serializable> extends PagingAndSortingRepository<E, ID>,
JpaSpecificationExecutor<E> {
}
public interface ActiveProxyViewRepository extends SuperRepository<ActiveProxyViewEntry, String> {}
// the call for deletion
public CompletableFuture<Void> delete(ID id) {
...
descriptor.getRepository().delete(descriptor.getIdentifierConverter().convert(id));
...
}
// which is equal to
...
ActiveProxyViewRepository repository = descriptor.getRepository();
String uuidAsString = descriptor.getIdentifierConverter().convert(id);
repository.delete(uuidAsString);
...
If you remove the #JoinColumn, you shouldn't have this problem.
If you need to keep the #JoinColumn, you need to remove the foreign-key constraint requirement that gets automatically applied by the persistence provider by changing the annotation to:
#JoinColumn(
name = "yourName"
foreignKey = #Foreignkey(value = ConstraintMode.NO_CONSTRAINT)
)
You should then be able to delete the view entity without forcing the tracking references to be removed.
It turned out to be a typical shoot yourself in the foot scenario.
Tracking references were updated in a rather sophisticated way:
Build collection of references to be stored in database (C1)
Load all present references (C2)
Store C1
Delete all references that are present in C2 but not referenced in C1 (using collection.removeAll)
And it turned out that my .equals method has been written terribly wrong, returning false in nearly in each case. Because of that, usually every reference was deleted from database (the queries you can see in the log in question), so it was my fault.
After i've fixed that, only #SQLDeleteAll query was run - for reasons not known to me, it still acted like if cascade option was set. I managed to get rid of it using #OneToMany(updatable = false, insertable = false); it seems like a dirty hack, but i don't have enough time to dig it through.
I haven't tested it thoroughly yet, but, i hope, that solves the problem.

#OnDelete Hibernate annotation does not generate ON DELETE CASCADE for MySql

I have two entities parent and child with a unidirectional relationship
class Parent {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
}
and
class Child {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "parent_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private Parent parent;
}
In the application.properties file I have configured
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
But when I run the application the following statement is created
...
alter table child add constraint FKi62eg01ijyk2kya7eil2gafmx foreign key (parent_id) references parent (id)
...
So there is no ON CASCADE DELETE as there should be. The tables are created each time I run the application and I checked if the method
org.hibernate.dialect.MySQL5InnoDBDialect#supportsCascadeDelete()
is really called (it is). I am using spring-boot-parent version 1.4.3, which uses Hibernate 5.11. Any ideas? I do not want to use a bi-directional relationship by the way.
Edit
Thanks to #AlanHay I discovered that I omitted an important part. There actually is a third class involved
class Kindergarten {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "kindergarten", fetch = FetchType.EAGER)
#MapKeyJoinColumn(name = "parent_id") // parent_id causes the problem!
private Map<Parent, Child> children;
}
and Child with Kindergarten looks actually like this
class Child {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "parent_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private Parent parent;
#ManyToOne
#JoinColumn(name = "kindergarten_id")
private Kindergarten kindergarten;
}
and that is why the problem occurs. If you change "parent_id" in the MapKeyJoinColumn annotation to something not existing in Child as a column e.g. "map_id" the ON DELETE CASCADE is added to the foreign key parent_id in Child. If the parameter is the Child's column "parent_id" the ON DELETE CASCADE part will not be appended. Unfortunately the reason for this is not yet clear to me. Changing the parameter is no option because I want to use the existing link to parent of the child object.
Maybe a little late, but since it is one of the top posts when searching for 'hibernate ondelete generate cascade':
For some reason putting #OnDelete on the ManyToOne side in Mysql did not work for me, but it worked on the OneToMany side. So if you are unlucky, try it on the other side.
In my case I also had to place #OnDelete on the OneToMany side, next to that I also had to change the JpaVendorAdapter bean method. The adapter it returns must be set to org.hibernate.dialect.MySQL5InnoDBDialect like so:
adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");

Bidirectional mapping of entities in Hibernate and spring mvc

I am trying to map two entities User(userid,name,password,address) and Role(roleId,roleName).
I am trying to do bidirectional OneToMany mapping between User and Role.
My User entity:
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="USER_ID"),
inverseJoinColumns = #JoinColumn(name="ROLE_ID")
)
public Set<Role> role;
My Role entity:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
private User user;
Now the corresponding intermediate table created in the database has following attributes.
userrole-> attributes( user_userId,ROLE_ID,USER_ID )
Now when i add set of items to a user. ROLE_ID and USER_ID of userrole table gets populated but user_userId remain null. What is the purpose of user_useerId.Should i manully make it primary key in the table user_userId?
UPDATE:
i did the following editing in Role entity
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name = "ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
And now when i checked the table in the database, the "userrole" table is perfectly ok and contains only (USER_ID,ROLE_ID)
I want to know why shouldn't i map two entities though this way?
In User entity you have declared #OneToMany with Role and also given details about the Join table using #JoinTable.
So if you need bidirectional between User and Role entities, then adding user property in Role entity with #ManyToOne declaration is sufficient, so no need of having #JoinTable once again.
Now coming to user_userId - in your Role entity you have declared the #JoinTable annotation and this annotation needs two column names for Join Table.
#JoinTable(
name="JoinTableName",
joinColumns = #JoinColumn(name="ID_NAME_FOR_THIS_ENTITY"),
inverseJoinColumns = #JoinColumn(name="ID_NAME_FOR_THE_MAPPING_ENTITY")
)
but you have provided one with name ROLE_ID and ignore the second name which is inverseJoinColumns that points to User entity.
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
so hibernate should make a guess about the column name for inverseJoinColumns, so it creates a column with name as combination of the entity name in lower case (which is user in your case) then the identifier in that entity separated by underscore (which is userId I guess based on column name). So finally the column name becomes user_userId.
Update:
If you need bidirectional relationship then you need to declare your entities like this:
#OneToMany(mappedBy="user", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
public Set<Role> role;
Here you are telling to hibernate that User entity has one-to-many relationship with Role entity and the relationship is bi-directional and the Role entity has a property called user.
Now in your Role entity you will give details about the JoinTable like this:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
The property name user in Role entity should match with the mappedBy attribute that you have declared in your User entity for the #OneToMany annotation. Adding the user field to Role entity makes the relationship bi-directional.
A (typical) bidirectional mapping has one side that is maintaining the relationship and the other side follows this relation ship.
This mean when you modifiy the maintaining side of the relation ship and save this change, then it gets stored in the database. While when you modifiy the following side, then nothing gets changed in the database. (it gets only updated when you load the entity).
A mapping would looks like:
#Entity
public class User {
...
#OneToMany(mappedBy="user") //mappedBy makes this side to the following side
public Set<Role> role;
...
}
#Entity
public class Role {
...
#ManyToOne
//#JoinTable... when you need it
private User user;
...
}
But this mapping is strange: because it mean that every User can have serveral roles, but each role belongs to exactly one user. -- If this is not intended, then switch ManyToOne and OneToMany or move over to ManyToMany