public interface JpaHibernateRelations
@ManyToOne @JoinColumn(name="countryId", referencedColumnName="countryId") private Country country;JPADataSource and HibernateDataSource support this style of declaration and will automatically handle mapping between IDs and entities.
The example above and the following examples assume a DataSource "country" for a JPA/Hibernate entity "Country" with an Id field of "countryId", and a DataSource "city" for a JPA/Hibernate entity "City" with an Id field of "cityId".
An example of Many-To-One is that Many "City"s belong to One "Country". In Java, each City bean has a field of type Country. In the database, rows for cities and countries are linked by ID.
To specify a many-to-one relation, declare a DataSourceField named after the Java field that
declares the relation ("country" above) with the property
foreignKey
pointing to related
DataSource's primary key:
<field name="country" foreignKey="country.countryId"/>When delivered to the browser, the value of the
country
field will be the ID of
the related Country entity. The country
field can be treated as a normal text
or integer field value, for example, you can use a SelectItem
that uses
optionDataSource
to allow selecting the ID of a different related
entity. Then, when the new ID is saved to the server, JPADataSource automatically looks up
the related object and persists the new relation to JPA.
NOTE:: do not declare a "type" attribute for such a field - these fields provide specialized mapping between IDs and JPA/Hibernate entities, so don't really have a single concrete type.
If you want fields from a related entity to be included whenever your entity is fetched, for
example, whenever a city is fetched you want the countryName
fetched from the
related country
entity, use includeFrom
.
Automatic Criteria translation
If criteria are submitted for a ManyToOne relation field containing an ID value, this will correctly return Records that are associated with the related object that has that ID.
For example, given a countryId of "1", you can fetch all city Records that are related to that countryId as follows:
DataSource.get("city").fetchData(new Criteria("countryId", "1"),);
An example of One-To-Many relation is that One "Country" has Many "City"'s. Each "Country" has a list of cities within it, which may be declared as a Java bean property of Collection type (or List, Set, etc).
To specify a one-to-many relation, declare a DataSourceField that:
foreignKey
pointing to the related
DataSource's primaryKey field
<field name="cities" type="city" multiple="true" foreignKey="city.cityId"/>With this declaration, whenever Records are loaded from the Country DataSource, they will contain a list of City Records as subobjects, accessible via
countryRecord.getAttributeAsRecordList("cities")
countryRecord.cities
If loading all related "city" records is desirable in some circumstances and not others, you
can use outputs
to avoid
loading "city" records for certain
operations.
For a Many-To-One relation, instead of loading just the ID of the related object, you can load the entire related object as a nested Record. To do so, just declare the type of the field to be the ID of the related DataSource, rather than leaving type unset:
<field name="country" type="country" foreignKey="country.countryId"/>The nested "country" Record will be available on a "city" record via
cityRecord.getAttributeAsRecord("country")
cityRecord.country.
Saving a City record that contains a nested Country record in the "country"
attribute will result in the Country being updated in JPA/Hibernate.
This mode is not typically used, since loading just the ID of the related Country object is
more efficient if many cities are being loaded, and the related Country object can always be
loaded with a second fetchData() data, which can still be done in a single HTTP request via
queuing
.
For a One-To-Many relation, instead of loading the complete list of related objects, you can load just a list of their IDs. To do so, just omit the type declaration when declaring the relation:
<field name="cities" multiple="true" foreignKey="city.cityId"/>When saving, if a replacement list of IDs is included in the Record, the appropriate JPA/Hibernate relationships will be updated.
This is very rarely used, and would typically only be used by client-side code that plans to programmatically work with the list of related IDs rather than display them in a UI component.
When relations are declared, JPA and Hibernate consider only one of the two entities to be the "owner" of the relation, meaning essentially that the references that make up the relationship are stored with that entity. When performing updates, make sure you update the entity that "owns" the relation. All changes to relations on "non-owning" entities are silently discarded.
For example, Country bean that has a Collection of City beans:
<field name="cities" type="city" multiple="true" foreignKey="city.cityId"/>To filter it by city.cityId List of IDs must be provided in criteria:
criteria:{criteria:[{fieldName:"cities",operator:"inSet",value:[1,2,3]}],operator:"and",_constructor:"AdvancedCriteria"},Alternative syntax is using List of Maps where each Map holds idField=value pair:
criteria:{criteria:[{fieldName:"cities",operator:"inSet",value:[{cityId:1},{cityId:2},cityId:3]}],operator:"and",_constructor:"AdvancedCriteria"},Same approach can be used with Many-to-Many relations, in that case omit foreignKey attribute.