Skip to content

3. Entities

1. Mapping Entities. Beans

Once we have studied the project configuration and setup, it is time to start to map our entities and relationships. Starting with the relational model, we are going to use next entity Peli, based on:

SQL
1
2
3
4
5
6
7
CREATE TABLE `Peli` (
  `idPeli` int(11) NOT NULL AUTO_INCREMENT,
  `titulo` varchar(45) NOT NULL,
  `anyo` varchar(45) NOT NULL,
  `director` varchar(45) NOT NULL,
  PRIMARY KEY (`idPeli`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

You can find it in the resource folder in a DBCine1.sql script. The class to encapsulate this entity looks like:

Java
package Model;

import java.io.Serializable;

/**
 *
 * @author joange
 */
public class Peli implements Serializable{

    private Long idPeli;
    private String titulo;
    private int anyo;
    private String elDirector;

    public Peli() {
    }

    public Peli(String titulo, int anyo, String elDirector) {
        this.titulo = titulo;
        this.anyo = anyo;
        this.elDirector = elDirector;
    }
  // more to come
}

Tip

Remember that you can use Lombook library to create beans in a faster way.

2. Mapping files

The bean created in last section cannot be persisted yet. In order to do it, we must create an external file to the class, with the extension hbm.xml and with the same name as the class (Hibernate Mapping). The file location does not matter, but it is a good idea to save the model classes on one side and the mapping files on the other.

So we will create an orm package and within it, we will create the Peli.hbm.xml file. Let's go to explain it by example, because syntax is pretty big.

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="Model.Peli" table="Peli" >
        <id column="idPeli" name="idPeli" type="int">
            <generator class="native"/>
        </id>
        <property name="titulo" type="string"/>
        <property name="anyo" />
        <property column="director" name="elDirector" />
    </class>
</hibernate-mapping>

We point on basics:

  • Lines 1-3 are xml header, pointing to the dtd file with grammar in order to check that file is well-formed.
  • <hibernate-mapping> \(\rightarrow\) tell that this file is a mapping
  • <class> \(\rightarrow\) is the tag to specify what class we are mapping, and has two attributes:
  • name="Model.Peli" \(\rightarrow\) pointer to the class (Java file), without extension.
  • table="Peli" \(\rightarrow\) here we write the name of the table in the database who will contain the data.
  • It remains to say the fields in the class and its respective column. We distinguish between:
  • <property> \(\rightarrow\) for normal fields. It could appear several attributes:
    • name \(\rightarrow\) (mandatory) is the name of the field inside the class
    • column \(\rightarrow\) is the name of the matching column in the table. If no column is set, hibernate will suppose that it is the same as name attribute.
    • type \(\rightarrow\) is the java type of the column. By default, Hibernate will use the same as the specified by the class, but we must indicate to resolve ambiguous fields (look next figure, specially in Date and Time formats)
  • <id> \(\rightarrow\) (mandatory) is the field that will be used as primary key, and also could appear name, column and type attributes. Exists the possibility that primary key will be generated by the DBMS, and we will set how, with
    • <generator> \(\rightarrow\) set the engine to generate primary key, that can be set in several ways. With native we use the same method of the underlying database. On this tutorial you can get a full samples link

Attention

  • You have to save this file to be accessed in our project. A good option is to create a resources folder inside src/main and save these files inside.
  • Hibernate needs get/set methods in order to access fields of our objects. Nevertheless, maybe you don't want to create any methods, nut hibernate needs. The solution is to add a new attribute access=field that allows hibernate to get/set without getters and setters.
![Types](./img/Hybernate_Types.png){width=90%}

Note

We will study more options like foreign keys in next sections.

Now is time to create a little program to prove all the contents. Take a look to next code:

Java
// get a Session and start a transaction
Session laSesion=HibernateUtil.getSessionFactory().getCurrentSession();
laSesion.getTransaction().begin();

// Create new object
Peli p=new Peli("Piratas del caribe", 2003, "Gore Verbinsky");
System.out.println("Unsaved: "+ p);

// save in the database
Long idNueva=(Long)laSesion.save(p);

// Get the saved object (with another varaible)
Peli q=laSesion.get(Peli.class, idNueva);

System.out.println("Saved: " + q);

//close all
laSesion.getTransaction().commit();
laSesion.close();

Comments:

  • Notice than to save an Object (e.g. insert a row in the database), you only need to run Session.save(Object).
  • To get one object from database, there is an easy way, knowing class and primary key of the object. Method is Session.get(class,ID), and we get an onject of this class whit this ID.
  • Notice that when we create new object, it hasn't an ID (remember that is auto generated), but when we save it, a new ID was assigned.

Important

If we choose to create in hbm2ddl.auto property, as you will guess, the database will be empty. It's a good idea to create a file called imports.sql with some data needed to test our programs.

3. Mapping classes. Annotations

Before starting annotating classes, let's show that:

  • JDO (Java Data Objects) is a persistence standard developed by Apache, which includes a persistence framework based on post compilation. Although trying to achieve a more efficient technique, there are few commercial initiatives that follow this standard.

  • The alternative is JPA (Java Persistance API), a persistence standard incorporated into JDK 5, with various libraries that support it, and based on the principle of reflection, which uses metadata as well as information about the classes and the structure What a fan that the virtual machine can access the information collected from the objects to invoke their methods and build new instances. The two most widely implemented ORMs in Java: Hiberate and EJB use this standard.

Mapping classes is easy, and we only need to match every field with every column. The drawback is than we need to maintain two files: classes and mapping files. For this reason, we can join both elements, adding inside classes the specific annotations to do the match. JPA interface allow us to do this task.

Let's go to show a class with annotations, and compare with mapping files. Moreover, we will use Lombok to create the class, that use annotations too ;).

Java
@Data
@NoArgsConstructor
@Entity
@Table(name = "Car")
public class Car implements Serializable {

public Car(String plate, String brand, int year) {
    this.plate = plate;
    this.brand = brand;
    this.year = year;
}

static final long serialVersionUID = 137L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long idCar;

@Column
private String plate;

@Column(name = "marca")
private String brand;

@Column
private int year;
}

Info

Hibernate create firstly his annotations, in package org.hibenate.annotations, but from version 4, all these annotations were deprecated and we must take care, and import annotations from javax.persistence

Let's go to see main annotations, instead you will have lots of them in JPA documentation:

  • @Entity to focus that this class represent an entity in our database. Moreover, we must associate this entity to a @Table with the correct name.
  • To set database fields, we must use @Column. You could specify more options, specially if the name of the column is different of field name. Take a look of next picture for more options. An interesting option is to set an auto generated value, with @GeneratedValue. If you do not set name option, it is supposed to be same.
  • To mark a field as primary key you need @Id. In this case you do not need to set with @Column

4. Componenets

Let's go to revise a special design patters, who is aggregation. Is when we need a special entity that only make sense inside another entity, for instance a wheel did not have sense outside a car. If the entity can exist by its own, is when we use a relation, and we will study in next section.

The aggregate class is also called a component.

Info

These components is also substituted by a 1:1 relationship with an existence constraint, when in the normalization process is joined in the same table.

To create component, the aggregated class must be defined with @Embeaddable (and obviously without @Entity). This annotation says to Hibernate that the existence of these objects must be inside another objects. Inside these objects, as fields, the objects must be marked as @Embeeded. Let's go to see a sample:

A component Wheel

Java
@Embeddable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Wheel {

    @Column
    int diameter;
    @Column
    char speedCode;
}

An aggregation CarWheel

Java
@Data
@NoArgsConstructor
@Entity
@Table(name = "Car")
public class CarWheel implements Serializable {

    public CarWheel(String plate, String brand, int year, Wheel wheel) {
        this.plate = plate;
        this.brand = brand;
        this.year = year;
        this.wheel = wheel;
    }

    static final long serialVersionUID = 137L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long idCar;

    @Column
    private String plate;

    @Column(name = "marca")
    private String brand;

    @Column
    private int year;

    @Embedded
    private Wheel wheel;

}
}

When Hibernate creates the table, be notice that no table Wheel will be created. Against, all fields are inside CarWheel. Two last fiels are properties from Wheel class.

SQL
1
2
3
4
5
6
7
8
9
Hibernate: 
create table Car (
    idCar bigint not null auto_increment, 
    marca varchar(255), 
    plate varchar(255), 
    year integer, 
    diameter integer, 
    speedCode char(1), 
  primary key (idCar)) engine=InnoDB

Attention A problem could appear when you try to store two embedded components, for instance two addresses from an employee, because as hibernate creates fields from component class, fields name will be duplicated. In order to avoid this drawback, the solution is to rename the component class fields, as follows (inside the aggregate class):

Java
1
2
3
4
5
6
7
8
9
@Embedded
private Wheel wheel1;

@Embedded
@AttributeOverrides({
        @AttributeOverride(name="diameter", column = @Column(name="diameter2") ),
        @AttributeOverride(name="speedCode", column = @Column(name="speedCode2") )
})
private Wheel wheel2;

Notice that:

  • First embedded field do not need nothing special.
  • In second, we most override the attribute names, starting an @AttributeOverrides with several @AttributeOverride, marking old name and new column name.

Tip

This situation is not usual, but is important to solve it when you need it.

5. Exercise

Do the following task:

  • Create a maven project and add all the dependencies that you need.
  • Create two classes by you own, but thinking that they will be related in a future. For instance, Teacher and Topic, or Driver and Car, or Pet and Vet. At this moment you don't need to create any relation.
  • Each class must have an ID, and almost 4 fields, with several data types (do not create all String fields).
  • Map both classes:
  • One with an external XML file.
  • The other with JPA annotations.
  • Create a main App when you ask the user for data and store (and save) in the same database