I need some help with a JPA self referencing relationship. I think there is something that I have not defined correctly.
I have a JPA entity bean named ItemEntity. There are two types of items. Parent items and child items. The parent item can have many child items and the child item only has one parent item. So really this is a ManyToOne / OneToMany JPA self referencing relationship.
In my database the item table looks like this...
item_no,parent_item_no,item_description
111,null,This is my parent item
222,111,This is my child item
So in my java program when I call itemEntity.getChildren on item 111, I would expect to see 222 however I am getting null.
Here is how I have defined my JPA relationship...
#Entity(name = "stg_item")
public class ItemEntity implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "itemid")
#TableGenerator(name = "itemid", table = "stg_items_sequence", allocationSize = 1)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "item_no", referencedColumnName = "parent_item_no")
private ItemEntity parent;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "parent")
private Collection<ItemEntity> children;
Can anyone tell me what I am doing wrong here?
The item entity has a #ManyToOne relationship with another one of my entities named stg_import_payload. Here is the named query I am using. Maybe I have to do something special with the named query?
"SELECT x "
+ " FROM stg_import_payload x "
+ " WHERE x.processedInd='N' "
+ " AND EXISTS (SELECT stg_item FROM stg_item stg_item WHERE stg_item.importPayload = x AND stg_item.processedInd='N') ";
Thanks.
Docs:
String javax.persistence.JoinColumn.referencedColumnName()
(Optional) The name of the column referenced by this foreign key column.
When used with entity relationship mappings other than the cases described here, the referenced column is in the table of the target entity.
When used with a unidirectional OneToMany foreign key mapping, the referenced column is in the table of the source entity.When used inside a JoinTable annotation, the referenced key column is in the entity table of the owning entity, or inverse entity if the join is part of the inverse join definition.
When used in a CollectionTable mapping, the referenced column is in the table of the entity containing the collection.
Default (only applies if single join column is being used): The same name as the primary key column of the referenced table.
You should change this:
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "item_no", referencedColumnName = "parent_item_no")
private ItemEntity parent;
to
#ManyToOne(fetch = FetchType.LAZY, optional = true)
private ItemEntity parent;
It works for me
Related
I've read some related questions but they are not exactly the same problem as mine.
I'm using JPA + Hibernate + Spring and I want to do something that I'm not sure if it is possible just with config.
I have my domain classes with a more or less complicated relation. There are many elements that are related with one element (like if it was a tree many elements are sons of one element).
Something like:
#Entity
class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "PARENT_ID")
private Foo parentNode;
...
}
Which will get a table like:
Foo id parent_id
1
2 1
3 1
When I delete row with id = 1 I want to delete rows with id = 2 and id = 3 (it may be recursive, elements with parent_id = 2 and parent_id = 3 would be deleted as well).
For some restrictions I only can have the relation in son's side with the parent_id reference.
My question is: is it possible to do this with JPA or Hibernate configuration or do I need to do some recursive function to delete all children and all parents?
I've tried with:
#OneToMany(name = "PARENT_ID", cascade = CascadeType.REMOVE)
And I've read that maybe using Hibernate annotations.
If anyone can give me some clue I'm lost at this point.
Edit 1
Would it be possible to do like:
#Entity
class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name="PARENT_ID")
private Foo parentNode;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parentNode", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<Foo> childs = new LinkedHashSet<Foo>();
...
}
Keeping the table as is, with the fk to the parent?
I've tried this but I keep getting the same error, fk restriction violated.
Edit 2
Finally solved with:
#Entity
class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "PARENT_ID")
private Foo parentNode;
#OneToMany(mappedBy = "parentNode", cascade = CascadeType.REMOVE)
private Set<Foo> childs = new LinkedHashSet<Foo>();
...
}
This #OneToMany is needed even if we do the mapping in our BBDD by refering just the parent id.
Now when we delete a Foo with childs, it's childs will be deleted as well.
Thanks for your time and good advices!
Look at orphanRemoval option:
#OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
Here is complete explication about CascadeType.REMOVE and orphanRemoval.
Good luck!
Relationships in JPA are always unidirectional, unless you associate the parent with the child in both directions. Cascading REMOVE operations from the parent to the child will require a relation from the parent to the child (not just the opposite).
So here you need to change unidirectional relationship to bi-directional.
for more details refer this link.
I have two entities: WorkoutTemplate and ConcreteExercise.
In WorkoutTemplate I have this relationship with ConcreteExercises
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
And in ConcreteExercise I have this relationship with WorkoutTemplate
#ManyToOne
private WorkoutTemplate belongingWorkout;
I would like to insert a WorkoutTemplate into my database...
I make the request to the controller sending a Json like this:
{ "workoutName" : "My Workout",
concreteExercises: [
{
"name" : "Squat"
}
]
}
The DAO insert into my DB the WorkoutTemplate
And insert also in the table of the ConcreteExercise the name.
But not the reference to the WorkoutTemplate...
Practically, the table ConcreteExercise is made of:
id, name, belongin_workout_id
With the request above, we populate the id (auto-increment) and the name, but not the foreign key to the WorkoutTemplate.
How can I solve this ?
I would like to automatically insert the foreign key without sending it in the request or doing it manually into the service
Hi there it's because you are not using #JoinColumn which marks a column for as a join column for an entity association or an element collection.
On your WorkoutTemplate entity - you can retain this:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
But on your ConcreteExercise entity - you need to have this change:
#ManyToOne
#JoinColumn(name = "workout_template_id", nullable = false)
private WorkoutTemplate belongingWorkout;
The above code will create a foreign key linking the ConcreteExercise entity with the primary key from the WorkoutTemplate entity. The name of the foreign key column in the ConcreteExercise entity is specified by name property which for this case is workout_template_id - feel free to change this.
If you have set this up but still not working, it will also be helpful to share a code snippet on how are you saving these.
Currently, my database is organized in a way that I have the following relationships(in a simplified manner):
#Entity
class A {
/*... class A columns */
#Id #NotNull
private Long id;
}
#Entity
#Immutable
#Table(name = "b_view")
class B {
/* ... same columns as class A, but no setters */
#Id #NotNull
private Long id;
}
The B entity is actually defined by a VIEW, which is written in this manner(assuming Postgres):
CREATE VIEW b_view AS
SELECT a.* FROM a WHERE EXISTS
(SELECT 1 FROM filter_table ft WHERE a.id = ft.b_id);
The idea here is that B references all elements of A that are present on filter_table. filter_table is another view that isn't really important, but it's the result of joining the A table with another, unrelated table, through a non-trivial comparison of substrings. These views are done so that I don't need to duplicate and control which elements of A also show up in B.
All of these are completely fine. JpaRepository is working great for B(obviously without saving the data, as B is Immutable) and it's all good.
However, at one point we have an entity that has a relationship with B objects:
#Entity
class SortOfRelatedEntity {
/** ... other columns of SortOfRelatedEntity */
#ManyToOne(fetch = FetchType.EAGER, targetEntity = Fornecedor.class)
#JoinColumn(name = "b_id", foreignKey = #ForeignKey(foreignKeyDefinition = "references a(id)"))
private B b;
}
For obvious reasons, I can't make this foreign key reference "b", since B is a view. However, I do want the query for searching this attribute to be defined by the b_view table, and having the foreign key defined by the underlying table(as written above) would be also nice in order to guarantee DB integrity.
However, when applying the above snippet, my sort-of-related-entity table doesn't create a foreign key as I would have expected. For the record, I'm using Hibernate 5.2.16 atm.
What am I doing wrong? Is this even possible? Is there something else I should do that I'm not aware of?
Oh FFS
I realized my mistake now. This:
#JoinColumn(name = "b_id", foreignKey = #ForeignKey(foreignKeyDefinition = "references a(id)"))
Should have been this:
#JoinColumn(name = "b_id", foreignKey = #ForeignKey(foreignKeyDefinition = "foreign key(b_id) references a(id)"))
Notice that the foreignKeyDefinition must include foreign key(), not just the references part.
Hopefully this helps someone in the future.
I've read some related questions but they are not exactly the same problem as mine.
I'm using JPA + Hibernate + Spring and I want to do something that I'm not sure if it is possible just with config.
I have my domain classes with a more or less complicated relation. There are many elements that are related with one element (like if it was a tree many elements are sons of one element).
Something like:
#Entity
class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "PARENT_ID")
private Foo parentNode;
...
}
Which will get a table like:
Foo id parent_id
1
2 1
3 1
When I delete row with id = 1 I want to delete rows with id = 2 and id = 3 (it may be recursive, elements with parent_id = 2 and parent_id = 3 would be deleted as well).
For some restrictions I only can have the relation in son's side with the parent_id reference.
My question is: is it possible to do this with JPA or Hibernate configuration or do I need to do some recursive function to delete all children and all parents?
I've tried with:
#OneToMany(name = "PARENT_ID", cascade = CascadeType.REMOVE)
And I've read that maybe using Hibernate annotations.
If anyone can give me some clue I'm lost at this point.
Edit 1
Would it be possible to do like:
#Entity
class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name="PARENT_ID")
private Foo parentNode;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parentNode", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<Foo> childs = new LinkedHashSet<Foo>();
...
}
Keeping the table as is, with the fk to the parent?
I've tried this but I keep getting the same error, fk restriction violated.
Edit 2
Finally solved with:
#Entity
class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "PARENT_ID")
private Foo parentNode;
#OneToMany(mappedBy = "parentNode", cascade = CascadeType.REMOVE)
private Set<Foo> childs = new LinkedHashSet<Foo>();
...
}
This #OneToMany is needed even if we do the mapping in our BBDD by refering just the parent id.
Now when we delete a Foo with childs, it's childs will be deleted as well.
Thanks for your time and good advices!
Look at orphanRemoval option:
#OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
Here is complete explication about CascadeType.REMOVE and orphanRemoval.
Good luck!
Relationships in JPA are always unidirectional, unless you associate the parent with the child in both directions. Cascading REMOVE operations from the parent to the child will require a relation from the parent to the child (not just the opposite).
So here you need to change unidirectional relationship to bi-directional.
for more details refer this link.
There are two main #Entity classes reflecting these tables:
TableA {id,name}
TableB {id,name}
And one reference table
TableC {tableA.id,tableB.id}
Question is: how to map a TableA's entity's field with #OneToMany realation to TableB objects list:
#OneToMany
??????????
private List<TableBEntity> tableBItems;
If what you really have is a OneToMany (which means that a give tableB.id appears at most once in TableC), then the mapping is the following:
#OneToMany
#JoinTable(name = "TableC",
joinColumns = #JoinColumn(name = "TABLE_A_ID"),
inverseJoinColumns = #JoinColumn(name = "TABLE_B_ID"))
private List<TableBEntity> tableBItems;
Else, what you have is in fact a ManyToMany, and the mapping is the same, except that #OneToMany must be replaced by #ManyToMany.