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.
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 a many to one relationship between two of my entities. now i want the parent to get removed when there are no more children.
my case:
class contactperson
class organisation
one organisation has a few contactpersons and every contactperson has one organisation. when all contactpersons of a organisation are removed, i want the organisation to get removed, too. is this possible by using cascade or something like that?
thanks, basti
you can do it like this :
1) in Organization.java
#OneToMany(cascade = CascadeType.ALL, mappedBy = "organization", orphanRemoval = true)
private List<ContactPerson> contactPerson= new ArrayList<ContactPerson>();
and
2) in ContactPerson.java
#ManyToOne
private Organization organization;
I've been searching over the web to find out a solution for this. It seems nobody has the answer... I start thinking i'm in wrong way adressing the problem.
Let's see if i can explain easy.
Im developing a contract maintenance. (table: contrat_mercan). For the contract, we will select a category (table: categoria), each category has qualities (table: calidad) in relation 1 - N (relationship table categoria_calidad).
This qualities must have a value for each contract where the category is selected, so I created a table to cover this relationship: contrato_categoria_calidad.
#Entity
#Table(name = "contrato_categoria_calidad")
public class ContratoCategoriaCalidad implements Serializable{
// Constants --------------------------------------------------------
private static final long serialVersionUID = -1821053251702048097L;
// Fields -----------------------------------------------------------
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "CCC_ID")
private int id;
#Column(name = "CONTRAT_MERCAN_ID")
private int contratoId;
#Column(name = "CATEGORIA_ID")
private int categoriaId;
#Column(name = "CALIDAD_ID")
private int calidadId;
#Column(name = "VALOR")
private double valor;
.... getters/ setters
In this table I wanted to avoid having an Id, three fields are marked as FK in database and first attempts where with #JoinColumn in the three fields. But it does not worked for hibernate.
Anyway, now ContratoCategoriaCalidad is behaving okay as independent entity. But I will need to implement all maintenance, updates, deletes for each case manually... :(
What I really want, (and I think is a better practice) is a cascade when I saveOrUpdate the contract as the other entities do, but I don't find the way to make a List in contrat_mercan table.
This is working perfect for other relationships in same table:
#OneToOne
#JoinColumn(name="CONDICION")
private Condicion condicion;
#OneToMany (cascade = {CascadeType.ALL})
#JoinTable(
name="contrato_mercan_condicion",
joinColumns = #JoinColumn( name="CONTRATO_MERCAN_ID")
,inverseJoinColumns = #JoinColumn( name="CONDICION_ID")
)
private List<Condicion> condiciones;
But all my attempts to map this failed, what i want, is to have in my Java entity contrat_mercan a field like this:
private List<ContratoCategoriaCalidad> relacionContratoCategoriaCalidad;
not a real column in database, just representation of the relationship.
I found solutions to join multiple fields of the same table, here, and here, but not to make a relationship with 3 tables...
Any idea? Im doing something wrong? Maybe i must use intermediate table categoria_calidad to perform this?
Thanks!!
If you want to access a list of related ContratoCategoriaCalidad objects from Contrato entity you need to declare a relationship between those two entities using proper annotations.
In ContratoCategoriaCalidad class change field to:
#ManyToOne
#JoinColumn(name = "CONTRATO_ID")
private Contrato contrato;
In Contrato class add field:
#OneToMany(mappedBy = "contrato")
private List<ContratoCategoriaCalidad> relacionContratoCategoriaCalidad;
If you want to enable cascade updates and removals consider adding cascade = CascadeType.ALL and orphanRemoval = true attributes to #OneToMany annotation.
Hope this helps!
I've got two entities with unidirectional #OneToMany mapping:
#Entity
#Table(name = "table1")
public class A {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
Integer pk;
String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "a_pk", nullable = false)
#Where(clause = "is_deleted = 0")
List<B> children;
}
#Entity
#Table(name = "table2")
public class B {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
Integer pk;
String content;
#Column(name = "is_deleted",nullable=false)
#NotNull
boolean deleted = false;
}
I want to obtain a list of all B entities, which are children of A entities matching a restriction.
Criteria c = session.createCriteria(A.class)
.add(Restriction.like("name", "Test%"))
.createAlias("children","b");
???
And this is where I'm stuck: c.list() will return me a list of A objects. I don't care about A, I want B. How can I do it with Hibernate criteria/projections? If it matters, I use Hibernate 4.2.12
In this simplified case it would make sense to just fetch eagerly; in the real case there's a chain of four OneToMany unidirectional associations, and I want to get all (or better yet, SOME) children all the way down the tree knowing the root id; but I don't want to load all the intermediate stuff (it's not needed, and even if join-fetching four tables would work it's plain gross). Also, simply getting the root and following down the lazy associations is a clear example of N+1 problem escalated.
As a side question, does Hibernate criteria satisfy entity/property #Where restrictions?
You can use projection to get list of "b". Like this :
Criteria c = session.createCriteria(A.class)
.add(Restriction.like("name", "Test%"))
.createAlias("children","b").setProjection(Projections.property("b"));
after this when you try to get results using c.list() it will return a list of B.
I hope this helps!!
So, yeah, I went with using HQL in the end. Nothing special.
List<B> bList = session.createQuery(
"select b from A as a join a.children as b where a.name like 'Test%'"
).list();
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