Implement 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 say the following (just an example):

  • The root level is called countries.
  • Each country contains an arbitrary number of states
  • Each state gains an arbitrary number of counties
  • Each environment contains an arbitrary number of cities

Thus, there is always a 1-N relationship between levels.

A very important usecase (taking into account the identifier of the country) is to download all the "content" of the country at once with the least possible impact on database performance.

In the first naive approach, I created 4 entitiy classes in Java, where the "Country" entity contains a list of the "State" type, the "State" entity contains a list of the "County" type, etc. ... But what JPA creates subsequently, of course , not 4 tables, but 7 (4 for entities + 3 for joining between levels due to 1-N). I do not know if this is a good solution, because there are many ways to join the hood.

I also tried to compare the subtypes with their parent types (the city belongs to one county, the county belongs to one state, the state belongs to one country). This leads to 4 tables, but makes it difficult to get all the data at once from the point of view of the application. If I'm not mistaken, I will need 4 different queries instead of one.

? ( , ) ( )? , ?

JPA Hibernate PostgreSQL.

+4
9

3 , @JoinColumn, @JoinTable, , .

, ,

COUNTRY

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="country")
private List<State> stateList;

@ManyToOne
@JoinColumn(name="country_id")
private Country country

:

country_id => primary key

state_id => primary key
country_id => foriegn key

.

+3

: - , ( mappedBy , ). EAGER -fetched, . LAZY, , :

SELECT DISTINCT c FROM Country c LEFT JOIN FETCH c.states s LEFT JOIN FETCH s.counties co...
+2

.

?

,

:

------

@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

:

-----
@ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="countryId")      
private Country country ;
-----

:

--------
 @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
 @JoinColumn(name="countryId")      
 private Country country ;
 -------

:


@ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="countryId")      
private Country country ;
---------

. 4 , Country.

+2

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

fetchType = FetchType.EAGER @OneToMany/@ManyToOne (, EAGER), .

+2

( , ).

.

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn ...
private ...;

. : http://www.concretepage.com/hibernate/fetch_hibernate_annotation

+1

: ( EAGER Loading LAZY, )

:

@Id
private Integer id;

@OneToMany(orphanRemoval=true fetch=FetchType.LAZY)
@JoinColumn(name="COUNTRY_ID") 
private List<State> stateList;

:

COUNTRY_ID,

@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;

:

STATE_ID,

@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;

:

COUNTY_ID,

@Id
private Integer id;

@Column(name="COUNTY_ID")
private Integer countyId;

JPQL :

Select o from Country o where o.id=10

"-" , .

Country
  Holding List of States
    Each States Holding List of Counties
      Each Counties Holding LIst of Cities 
+1

, , . .

2 LOCATION_NODE ( , , [, , , ]) LOCATION_REL ( , , ID), ​​ .

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;
    }
}

. , , , . , , node, node .. .

" ".

+1

, - 4 ():

  • : id,
  • : id, countryId, name
  • : id, countryId, stateId, name
  • : id, countryId, stateId, countyId, name

( )

SQL-. , , . . : "SELECT id, name FROM city WHERE country_id =?"

, @ManyToOne, @Columns. API, , (countryId, stateId), DAO. , , sql script, . , .

? , , .

+1

, JPQL, , , .

1

2

,

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
0

Source: https://habr.com/ru/post/1526148/


All Articles