|
|
AssociationsDefining associationsAssociations are a more complex form of attributes. Simple attributes, are attributes of the types (integer, float, boolean, date, ...), associations are relations with other complex objects. It must be said that this type of relation is only supported through the Hibernate Spatial LayerModel! This page will provide 2 example configurations for two different types of relations (many-to-one, and one-to-many), using an imaginary PostGIS database. For both example, we will provide database table definitions, Java class definitions for the features, and geomajas XML cofiguration. The notions of "many-to-one" and "one-to-many" are taken from the Hibernate framework on which Hibernate Spatial is a GIS extension. If you have a problem understanding these terms, look up the Hibernate website. In these examples, we will use the OpenStreetMaps vector data model (or part of it). The main feature will be an object called "way", which has a user_id, pointing to a user. Then to make things interesting there a other objects called "way_tags", which point back to the way. A quick overview is shown in the picture below:
The database
Talking from the viewpoint of a "way", it has a user_id that points to one user's id (many-to-one). But more then that, it may also have many way_tags pointing back to the way (one-to-many). Let us now create these tables in PostGIS: CREATE TABLE ways ( id serial NOT NULL, osm_id int4 NOT NULL, creation_time timestamp NOT NULL, user_id int4 NOT NULL, geom geometry ) CREATE TABLE users ( id serial NOT NULL, name varchar(255) NOT NULL ) CREATE TABLE way_tags ( id serial NOT NULL, way_id int4 NOT NULL, tag_key varchar(255) NOT NULL, tag_value varchar(255) NOT NULL ) The Java POJO classesThe next step is to create the Java classes we intent to use as features in the Geomajas layers. Out intention here is to create a layer called "ways" (it is after all the only table with geometries), and have to other tables attached to it as complex attributes (or associations). First we need to create the 3 POJO (plain old Java objects) classes that map onto the 3 database tables. To do this, we use Hibernate annotations. Let's begin with the users: @Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
...So this Java class represents a database table called "users", and has 2 fields representing the database column "id" and "name" respectively. The goal here is not to explain how Hibernate OR-mapping works, but to show you the necessary steps in using Hibernate and it's OR model, to create complex attribute relations in Geomajas. Next up is the WayTag class: @Entity
@Table(name = "way_tags")
public class WayTag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@ManyToOne
@JoinColumn(name = "way_id")
private Way way;
@Column(name = "tag_key")
private String key;
@Column(name = "tag_value")
private String value;
...
@JSON(serialize=false)
public Way getWay() {
return way;
}
...For the WayTag class there is one field that stands out: the "way". Remember that the database table "way_tags" had a foreign key to the "ways"-table. In the Java class, this has been mapped by a Way object through a many-to-one relationship. This is seen from the perspective of a WayTag of course. From the perspective of the Way this relationship is a one-to-many. Also note that the getter for the way field has an extra annotation, saying it should not be serialized by the JSON serializer, when it is passed to the client. This is necessary because otherwise the serialization would result in circular reference errors. Now last but not least is the Way class: @Entity
@Table(name = "ways")
public class Way {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Type(type = "org.hibernatespatial.GeometryUserType")
@Column(name = "geom")
private Geometry geom;
@OneToMany(mappedBy = "way", fetch = FetchType.EAGER)
@org.hibernate.annotations.Cascade(value = {
org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private List tags = new ArrayList();
@Column(name = "creation_time")
private Date timestamp;
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "user_id")
private User user
...A Way is the type of object that we want our Geomajas layer to consist out of. So as expected it contains a geometry. Furthermore, it also contains mappings according to the relations in the database: the foreign key to the "users"-table, is now replaced by a User object, and the one-to-many relationship with the "way_tags"-table, has been replaced by a List of WayTag objects. The idea now is to use Hibernate Spatial as a source of data for creating VectorLayers in Geomajas. So first things first, we need to configure Hibernate. This happens in the hibernate.cfg.xml file in the Java resources. To make sure Hibernate is aware of our mapping classes, we add them here: <mapping class="be.dfc.majas.foss4g.Way"/> <mapping class="be.dfc.majas.foss4g.WayTag" /> <mapping class="be.dfc.majas.foss4g.User" /> Then we have to configure a LayerModelFactory in the application.xml file of the application we will use (in this section we use the "osm" application): <layerModelFactory id="hibernate">
<factoryClass>be.dfc.majas.layermodels.hibernate.HibernateLayerModelFactory</factoryClass>
<parameterMap />
</layerModelFactory>XML ConfigurationNext is to create a VectorLayer configuration of the Way class. We will show the part of the layerconfiguration in which the attributes are configured: <featureType> <layerModelFactoryRef>hibernate</layerModelFactoryRef> <name>be.dfc.majas.foss4g.Way</name> <identifier> <label>Id</label> <name>id</name> <type>long</type> </identifier> <geometryType> <name>geom</name> <crs>EPSG:900913</crs> <editable>true</editable> </geometryType> <attributes> <!-- ATTRIBUTE 1 --> <attribute> <label>Creation Date</label> <name>timestamp</name> <editable>false</editable> <identifying>true</identifying> <type>date</type> </attribute> <!-- ATTRIBUTE 2 --> <association> <label>Author</label> <name>user</name> <editable>true</editable> <identifying>true</identifying> <type>many-to-one</type> <object> <name>be.dfc.majas.foss4g.User</name> <identifier> <label>Id</label> <name>id</name> <type>long</type> </identifier> <attributes> <attribute> <label>Name</label> <name>name</name> <editable>false</editable> <identifying>true</identifying> <type>string</type> </attribute> </attributes> </object> </association> <!-- ATTRIBUTE 3 --> <association> <label>Tags</label> <name>tags</name> <editable>true</editable> <identifying>true</identifying> <type>one-to-many</type> <object> <name>be.dfc.majas.foss4g.WayTag</name> <identifier> <label>Id</label> <name>id</name> <type>long</type> </identifier> <attributes> <attribute> <label>Key</label> <name>key</name> <editable>true</editable> <identifying>true</identifying> <type>string</type> </attribute> <attribute> <label>Value</label> <name>value</name> <editable>true</editable> <identifying>true</identifying> <type>string</type> </attribute> </attributes> </object> </association> </attributes> </featureType> Let's go over this step by step. We start by referring to the hibernate LayerModelFactory, therefore declaring we will be using Hibernate to retrieve data. Next is the name of our type. When using Hibernate, the name should always be a Javaclass that is part of the Hibernate mappings. We know that the Way class, is among the Hibernate mappings, and so are all the other java classes that are used in the Way class (User and WayTag). Then, like any VectorLayer, we define the identifier and the geometric attribute. Nothing really special so far; all of this can be found in the configuration manual. But this is where we start to configure the complex attributes that the Way class has. Well almost at least, because the first attribute is a simple one. We declare the timestamp field in the Way class as the "Creation Date" attribute, with the type "date". Attribute 2 however is not an attribute, but an association! In this case, we are trying to map the user's many-to-one relationship. Each Way has been created by a certain user, and we want this attribute definition to show his or her name. So we declare the type: "<type>many-to-one>/type>", and let it map on an object. This object has to exactly what Hibernate expects! In the Way class, the user is stored in an object of the type "be.dfc.majas.foss4g.User", and so it is in the Geomajas attribute mapping. Now this new object has in turn an identifier and it's own attribute mappings. The identifier is used to identify the different user, while the attribute is used to portray the attribute. Attribute 3 is again an association, but this time it maps to the tags list. The tags-list was in the Way class declared by Hibernate's one-to-many relationship, so it is in the GeoMajas configuration. Again we include the object to which the attribute maps: the WayTag class ( "be.dfc.majas.foss4g.WayTag"), followed by an identifier and an attributes list. Actually in attribute2, only one attribute of the associated object is needed, this time, we can specify as many as we want. |




