The first thing we need to check is the list of entity objects that will map the database tables. The first one is the Customer @Entity class, as follows:
@Entity
@NamedQuery(name = "Customers.findAll",
query = "SELECT c FROM Customer c ORDER BY c.id",
hints = @QueryHint(name = "org.hibernate.cacheable", value =
"true") )
public class Customer {
@Id
@SequenceGenerator(
name = "customerSequence",
sequenceName = "customerId_seq",
allocationSize = 1,
initialValue = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"customerSequence")
private Long id;
@Column(length = 40)
private String name;
@Column(length = 40)
private String surname;
@OneToMany(mappedBy = "customer")
@JsonbTransient
public List<Orders> orders;
// Getters / Setters omitted for brevity
}
Let's go through the single annotations we have included in the entity class:
- The @Entity annotation makes this class eligible for persistence. It can be coupled with the @Table annotation to define the corresponding database table to a map. If it's not included, like in our case, it will map a database table with the same name.
- The @NamedQuery annotation (placed at the class level) is a statically defined SQL statement featuring a query string. Using named queries in your code improves how your code is organized since it separates the JPA query language from the Java code. It also avoids the bad practice of embedding string literals directly in your SQL, thus enforcing the use of parameters instead.
- The @Id annotation specifies the primary key of an entity, which will be unique for every record.
- The @SequenceGenerator annotation is used to delegate the creation of a sequence as a unique identifier for primary keys. You will need to check that your database is capable of handling sequences. On the other hand, although this isn't the default option, this is considered a safer alternative since the identifier can be generated prior to executing the INSERT statement.
- The @Column annotation is used to tell Hibernate ORM that the Java field maps a database column. Note that we have also specified a constraint in terms of the size of the column. Since we will let Hibernate ORM create our database structures from Java code, all the constraints that are declared in the Java class will effectively turn into database constraints.
- Finally, we had to apply two annotations on top of the orders field:
- The @OneToMany annotation defines a one-to-many relationship with the Orders table (that is, one customer is associated with many orders).
- The @JsonbTransient annotation prevents mapping the field to the JSON representation (since the reverse mapping for this relationship is included in the Orders class, mapping this field to JSON would cause a StackOverflow error).
The Customer entity, in turn, references the following Orders class, which provides the other side of the one-to-many annotation:
@Entity
@NamedQuery(name = "Orders.findAll",
query = "SELECT o FROM Orders o WHERE o.customer.id = :customerId ORDER BY o.item")
public class Orders {
@Id
@SequenceGenerator(
name = "orderSequence",
sequenceName = "orderId_seq",
allocationSize = 1,
initialValue = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"orderSequence")
public Long id;
@Column(length = 40)
public String item;
@Column
public Long price;
@ManyToOne
@JoinColumn(name = "customer_id")
@JsonbTransient
public Customer customer;
// Getters / Setters omitted for brevity
}
It's worth noting that the named query for this class is slightly more elaborated since Orders.findAll NamedQuery uses a parameter in order to filter the orders by a specific customer.
Since the Customer structure and the Orders structure make up a bidirectional association, we need to map the corresponding Customer field to the @javax.persistence.ManyToOne annotation.
We also have included the @javax.persistence.JoinColumn annotation to indicate that this entity is the owner of the relationship. In database terms, this means that the corresponding table has a column with a foreign key for the referenced table. Now that we have a class where data will be stored, let's inspect the Repository class, which is used to access data from the RDBMS.