Hibernate many to one - delete parent when no more childs - java

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;

Related

Child elements cannot be cascaded when the parent element is deleted [duplicate]

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.

Mapping multiple tables to one List Hibernate

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!

JPA + Hibernate + Spring + OneToMany delete cascade

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.

Implementing hierarchical data structures with JPA (fixed depth)

I have a hierarchical data structure with a fixed depth of 4. For a better understanding, let's assume the following (just an example):
The "root" level is called countries
Each country contains an arbitrary amount of states
Each state countains an arbitrary amount of counties
Each county contains an arbitrary amount of cities
So there are always 1-N relationships between the levels.
A very important usecase (given the id of a country) is to load the whole "content" of a country at once with the smallest possible impact on the performance of the database.
In a first naive approach, I created 4 entitiy classes in Java where the entity "Country" contains a list of the type "State", the entity "State" contains a list of the type "County" and so on...
But what JPA creates afterwards are of course not 4 tables, but 7 (4 for the entities + 3 for the connection between the levels due to 1-N). I don't know if this is a good solution since there is a lot of joining going on under the hood.
I also tried to map the subtypes to their parent types (a city belongs to one county, a county belongs to one state, a state belongs to one country). This results in 4 tables, but makes it more difficult to retrieve all data at once from the application's point of view. If I'm not wrong, I would need 4 different requests instead of one.
How could I solve this problem? Is there a way to combine a simple table layout (with four tables, not seven) with easy to use entity classes (a parent type should know its children)?
If not, how would you realize this?
I'm using JPA with Hibernate and PostgreSQL.
You can avoid the 3 extra mapping tables by using the #JoinColumn annotation rather than the #JoinTable annotation that I suspect you are using.
So for example:
COUNTRY
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="country")
private List<State> stateList;
STATE
#ManyToOne
#JoinColumn(name="country_id")
private Country country
The database tables would be as follows:
Country
country_id => primary key
State
state_id => primary key
country_id => foreign key
This way the mapping tables between all the 4 entities can be avoided.
You can achieve this pretty easily using JPQL:
SELECT DISTINCT country
FROM Country country
JOIN FETCH country.states states
JOIN FETCH states.counties counties
JOIN FETCH counties.cities cities
WHERE country.id = :countryId
Using fetchType = FetchType.EAGER on #OneToMany/#ManyToOne(believe that one is already EAGER by default) will achieve similar results.
It's very simple use bidirectional mapping. Go through that link
How to delete Child or Parent objects from Relationship?
Make some changes like below
Country Entity:
------
#OneToMany(mappedBy="Country ",cascade = CascadeType.ALL)
private List<States > states;
#OneToMany(mappedBy="Country ",cascade = CascadeType.ALL)
private List<Counties> counties;
#OneToMany(mappedBy="Country ",cascade = CascadeType.ALL)
private List<Cities> cities;
-------
setters & getters
States Entity:
-----
#ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="countryId")
private Country country ;
-----
Counties Entity:
--------
#ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="countryId")
private Country country ;
-------
Cities Entity:
#ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="countryId")
private Country country ;
---------
After compilation of all entity's do your insertion . Only 4 will create and read your data by using Country object id.
You already have the solution: four table is the way to go, with bidirectional relationships (use the mappedBy property in the not-owning side of every relationship). If the relationships are EAGER-fetched, than all entities are automatically loaded. If you want to use LAZY fetching, you could try a named query in order to load the entity with all relationships loaded:
SELECT DISTINCT c FROM Country c LEFT JOIN FETCH c.states s LEFT JOIN FETCH s.counties co...
Did you try to declare the fetch type of the relations explicitely to eager with your second approach (default is lazy, that's why you have to do four queries).
E.g.
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn ...
private ...;
see here: http://www.concretepage.com/hibernate/fetch_hibernate_annotation
Here is how your entities will look like:(You can use EAGER Loading instead of LAZY as well if you want)
Entity: Country
#Id
private Integer id;
#OneToMany(orphanRemoval=true fetch=FetchType.LAZY)
#JoinColumn(name="COUNTRY_ID")
private List<State> stateList;
Entity: State
This table has COUNTRY_ID that is Foreign Key to Country
#Id
private Integer id;
#OneToMany(orphanRemoval=true fetch=FetchType.LAZY)
#JoinColumn(name="STATE_ID")
private List<County> countyList;
#Column(name="COUNTRY_ID")
private Integer countryId;
Entity: County
This table has STATE_ID that is Foreign Key to State
#Id
private Integer id;
#OneToMany(orphanRemoval=true fetch=FetchType.LAZY)
#JoinColumn(name="COUNTY_ID")
private List<City> cityList;
#Column(name="STATE_ID")
private Integer stateId;
Entity: City
This table has COUNTY_ID that is Foreign Key to County
#Id
private Integer id;
#Column(name="COUNTY_ID")
private Integer countyId;
Your JPQL will be:
Select o from Country o where o.id=10
This will pick The Country Entity along with all the mappings like below.
Country
Holding List of States
Each States Holding List of Counties
Each Counties Holding LIst of Cities
For a requirement like yours, I would suggest to have a tree-like structure to maintain the hierarchical location data. It is relatively easy to implement & maintain and is more scalable & extensible.
In order to implement tree you need to have 2 tables LOCATION_NODE (Location ID, Location Name, Location Type[country, state, county, city]) & LOCATION_REL (Relation ID, Parent ID, Child ID). Below is the basic implementation of the tree idea.
public class LocationRel<T> {
private LocationNode<T> root;
public LocationRel(T rootData) {
root = new LocationNode<T>();
root.data = rootData;
root.children = new ArrayList<LocationNode<T>>();
}
public static class LocationNode<T> {
private T data;
private LocationNode<T> parent;
private List<LocationNode<T>> children;
}
}
This is the basic building block for a tree. You may need to add methods for add to, removing from, traversing, and constructors. But, once implemented, you have the freedom to add any new location type, change the hierarchy, add node, delete node etc with your hierarchical data.
Think out of the box.
Shishir
If you need the performance, I would suggest to de-normalize your tables and create 4 entities with following attributes (columns):
Country: id, name
State: id, countryId, name
County: id, countryId, stateId, name
City: id, countryId, stateId, countyId, name
(mapping is obvious)
Then you will be able to build a simple SQL queries.
If you need performance, prefer named queries as they are compiled at initialization time.
E.g. select all cities by country: "SELECT id, name FROM city WHERE country_id=?"
You may even not declare a references between entities using #ManyToOne, but just declare a simple #Columns. API call will, most likely, accept IDs (countryId, stateId), so you'll be better to pass that IDs as parameters to DAO. Most likely, you have a locations tables filled in once by sql script and the data should not be modified. Create foreign keys to guarantee data integrity.
And do you really need a tree-like structure in memory? If so, create it by hand, it is not very complex.
Searching Online, I found a couple of Links on JPQL which I think might help.
Link 1
Link 2
Anyways,
JPQL is one of the best ways to achieve this, try out this Query
SELECT DISTINCT country FROM Country country JOIN FETCH country.states states JOIN FETCH states.counties counties JOIN FETCH counties.cities cities WHERE country.id = :countryId
A solution that is useful, if you have relations that point to their parent only is the following:
With records:
#Entity
public class Country
{
#Id
private Long id;
}
#Entity
public class State
{
#Id
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "country_id", referencedColumnName = "id", nullable = false)
Country country;
}
#Entity
public class County
{
#Id
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "state_id", referencedColumnName = "id", nullable = false)
State state;
}
#Entity
public class City
{
#Id
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "county_id", referencedColumnName = "id", nullable = false)
County county;
}
You can get all cities of a country with:
public interface CityRepository extends JpaRepository<City, Long>
{
List<City> findByCounty(County county); // county is a direct field of City
#Query("SELECT c FROM City c WHERE c.county.state.country = ?1")
List<City> findByCountry(Country country);
}

cannot insert null in one to many relationship hibernate annotation

I have a class A{Set b .....} which holds references of class B as Set. It is one to many relationship.
Both class have sequencer in oracle. I put cascade to all in hibernate annotations. When i save class A, it gives me error that cannot insert null B.a_id . A-id is not nullable in my database. How can i persist this relationship.
This is a unidirectional relationship from A->B. a_id column in table B is not nullable. When hibernate tries to save class B, it not able to find value for a_id.
Well, did you try to make the JoinColumn non nullable?
#OneToMany
#Cascade({CascadeType.ALL})
#JoinColumn(name="A_ID", nullable=false)
private Set<B> b;
See also
Hibernate Core Reference Guide
6.2.1. Collection foreign keys
I ran into the same problem and solved it by using the mappedBy attribute of the #OneToMany annotation in Class A:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "m_a")
private Set<B> b;
Here, m_a is the field in Class B that refers to Class A:
#JoinColumn(name = "aId", nullable = false)
#ManyToOne
private A m_a;
This way, the #JoinColumn is only specified in one place, no duplicated code.

Categories

Resources