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;
Related
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
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?
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");
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
I know this makes none sense as many tutorials state that you can use SecondaryTable annotation, however it doesn't work in hibernate. I have schema like this:
#Entity
#Table(name="server")
#SecondaryTable(name="cluster", pkJoinColumns = { #PrimaryKeyJoinColumn(name = "uuid", referencedColumnName = "cluster_uuid") })
public class Server {
#Id
#Column(name = "uuid")
private String uuid;
#Column(name = "cluster_uuid")
private String clusterUuid;
#Column(name = "ip", table="cluster")
private String ip;
..... }
#Entity
#Table(name = "cluster")
public class Cluster {
#Id
#Column(name = "uuid")
private String uuid;
#Column(name = "ip")
private String ip;
.....
}
Server.clusterUuid is a foreign key to Cluster.uuid. I am hoping to get Server entity that fetches ip column from Cluster by joining Server.clusterUuid to Cluster.uuid.
Then I was greeted by a hibernate exception:
Caused by: org.hibernate.AnnotationException: SecondaryTable
JoinColumn cannot reference a non primary key
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:402)
at org.hibernate.cfg.annotations.EntityBinder.bindJoinToPersistentClass(EntityBinder.java:620)
at org.hibernate.cfg.annotations.EntityBinder.createPrimaryColumnsToSecondaryTable(EntityBinder.java:612)
I see lots of people encountered this problem. But the first bug for this in Hibernate's bugzilla was 2010, I am surprised it's been there for over two years as this is supposed to be a basic feature. There is some post saying JPA spec only allows primary key to do the mapping, however, I get below from JPA wikibook
JPA allows multiple tables to be assigned to a single class. The
#SecondaryTable and SecondaryTables annotations or
elements can be used. By default the #Id column(s) are assumed to be
in both tables, such that the secondary table's #Id column(s) are the
primary key of the table and a foreign key to the first table. If
the first table's #Id column(s) are not named the same the
#PrimaryKeyJoinColumn or can be used to
define the foreign key join condition.
it's obviously OK for non-primary key. Then I am confused why Hibernate didn't fix this problem as it seems to be easy to implement by a join clause.
anybody knows how to overcome this problem? thank you.
I don't quite understand your setup.
#SecondaryTable is for storing a single entity in multiple tables, but in your case you have a many-to-one relationship between different entities (each one stored in its own table), and it should be mapped as such:
#Entity
#Table(name="server")
public class Server {
#ManyToOne
#JoinColumn(name = "cluster_uuid")
private Cluster cluster;
...
}