4 Java Persistence
4.4 Development of an Example Application
4.5.3 Implementing the Entities
4.5.3 Implementing the Entities
If you recall the UML diagram of the project management scenario from Section 4.4.1, Project Management Scenario, you will notice that the modeled classes had a number of attributes in common. These are the ID (id) and the date of creation (createdAt). This property almost invites the use of the inheritance that is possible with JPA for persistent objects.
Inheritance
JPA provides a number of ways of implementing inheritance.6 However, in this case, it is again a question of not only knowing the new possibil-ities, but also knowing how they can meaningfully be used. When imple-menting the project management scenario, you could have considered deriving the Manager entity from the Employee entity. In fact, you can find this example in the literature and on the Internet.
Unfortunately, inheritance is possibly an unsuitable approach for mod-eling these facts, unless the identity of the manager is definitively fixed.
It is more realistic to make an existing employee into a manager, or pos-sible to downgrade a manager to an employee. However, it is not possi-ble simply to make an employee into a manager, rather a new object of the type manager needs to be instantiated, the values of the original employee object need to be copied, and finally the employee object needs to be deleted.
The problem becomes clear when you remember that the employee object has relationships with other objects. JPA entities behave like
tra-Figure 4.8 Persistence Unit
6 The optional TABLE_PER_CLASS strategy is not supported by the SAP JPA implementa-tion in SAP NetWeaver Composiimplementa-tion Environment 7.1.
Programming with Enterprise JavaBeans 3.0/Java Persistence API 4.5
ditional Java objects, that is, the relationships in which the Employee entity is involved must be suitably adjusted. This means, for example, that the Employee entity has to be removed from all of the projects in which it is involved, and the newly-generated Manager entity put in its place. No relationship can be neglected, to avoid an inconsistent data-base status. However, this is likely, unless the developer involved has internalized the entire domain model.
It is better to express this type of dynamic aspect of a domain model using attributes of the classes involved. In this case, a simple Boolean attribute isManager in the class Employee would be sufficient to express this fact. No other class would be affected by the changing of this attribute value.
Considered in more detail, an object that consists only of an ID and a creation date does not really qualify as an entity: Without the additional attributes, there would not really be any point in searching for an object of this type, or in instantiating it; in fact, it is difficult to think of a mean-ingful name for an object of this type, because it is actually only a set of attributes that are common to all entities in the domain. With transient objects, meta or status information of this type would normally be stored in an abstract superclass, from which concrete classes are then derived.
Mapped superclass
JPA analogously offers the mechanism of mapped superclasses, which allows precisely this property to be modeled for persistent objects. The attributes id and createdAt are declared, including getter methods, for a class annotated as a mapped superclass called BusinessEntity, and are also stored as database mapping.7 A separate table does not yet exist for
Basic Rules when Using Inheritance
The following basic rules apply when using inheritance:
왘 Inheritance relationships apply only for static facts.
왘 Dynamic facts are expressed using class attributes.
7 To avoid overdoing the consideration of the mapped superclass and its properties, take the database mapping as specified. The procedure for database mapping is explained in Section 4.5.3, Implementing the Entities.
Java Persistence
4
BusinessEntity; however, its attributes are mapped to the tables of the entities derived from it.
BusinessEntity class
Create BusinessEntity as an abstract Java class in package com.sap.
examples.persistence.entities of the EJB 3.0 project (see Listing 4.4).
왘 The attribute id is also identified as such for JPA with the annotation
@Id.
왘 The attribute creationDate of type java.util.Date is initialized by processing the constructor with the current date. Because java.util.
Date can be mapped to different database types, the @Temporal anno-tation specifies that Timestamp is a prerequisite at Open JDBC/data-base level.
@MappedSuperclass
public abstract class BusinessEntity {
@Id
@Column(name="ID") protected long id;
@Temporal(TemporalType.TIMESTAMP) @Column(name="CREATEDAT")
protected Date creationDate;
public BusinessEntity() { creationDate = new Date();
}
public Date getCreationDate() { return creationDate;
}
public long getId() { return id;
} }
Listing 4.4 Mapped superclass BusinessEntity
Programming with Enterprise JavaBeans 3.0/Java Persistence API 4.5
The next two sections discuss additional aspects that were rolled out to BusinessEntity.
ID Generation
Instead generating an ID, you use the automatic ID generation function of JPA. The SAP JPA implementation in its current form supports only the TABLE strategy, that is, a separate table is used to generate the ID val-ues. However, this is not created by default in the database schema of the server, and you therefore need to add it to the Dictionary project. Its name should be PER_ID_GEN, and it should have the two columns GEN_KEY and GEN_VALUE. GEN_KEY is the key field of type string and at runtime will receive the fully-qualified class name of the class for which the last-generated key, of type integer, is stored in the column GEN_VALUE (Figure 4.9).
Once you have rebuilt and deployed the Dictionary project, you need to tell the JPA runtime to use the new table for ID generation. There are two ways to do this:
왘 Declarationasdefaultinpersistence.xml
With this approach, you specify the table name as a Persistence Unit Property called com.sap.engine.services.orpersistence.genera-tor.auto.tablename (Figure 4.10).
Figure 4.9 Dictionary Table for ID Generation
Java Persistence
4
왘 Annotation
With this approach, a TableGenerator8 (or potentially several) is declared with name and table (@TableGenerator). The id attribute is assigned the @GeneratedValue annotation, which specifies the respec-tive strategy and the respecrespec-tive generator (Listing 4.5).
Annotation of the ID generation
@MappedSuperclass
@TableGenerator(name="idGen", table="PER_ID_GEN") public abstract class BusinessEntity {
@Id
@GeneratedValue (strategy=TABLE, generator="idGen") @Column(name="ID")
protected long id;
...
}
Listing 4.5 ID Generation Annotations
Transactional Integrity/Competing Changes
The JPA specification requires the use of the isolation level Read Com-mitted for database access, and does not make any statements about when within a transaction the changes are written to the database.
Figure 4.10 ID Generator Table Specification in persistence.xml
8 Stylistically, it would be more elegant to annotate @TableGenerator for the id attribute. A bug makes it necessary to do this at class level. This is corrected in subse-quent versions.
Programming with Enterprise JavaBeans 3.0/Java Persistence API 4.5
To protect against competing changes in this type of scenario, the JPA specification implicitly requires the use of optimistic locks. When the changes are written to the database, the WHERE clause compares the value of a special column with its before image while the statement’s UPDATE clause increments this value. This, therefore, checks whether the version has changed in the meantime, that is, whether a competing write pro-cess has changed the entity concerned.
Versioning
You will now build this versioning mechanism into BusinessEntity. You have already created the VERSION column in the relevant tables of the derived entities in Definition and Deployment of the Tables in Section 4.4.2, meaning that you do not need to adjust the Dictionary project again. The VERSION column is also a canonical candidate for BusinessEn-tity, because it must be an annotated attribute of every entity. How-ever, the version attribute should not be exposed and therefore does not receive any getter or setter methods (Listing 4.6).
@Version
@Column(name="VERSION") private long version;
Listing 4.6 Annotation of the Versioning
Object-Relational Mapping
With the implementation of BusinessEntity, you have already put in place good preliminary work for the implementation of the Employee, Department, Address, and Project entities. You learned in Chapter 3, SAP NetWeaver Developer Studio — Step-by-Step to a Sample Application, how entities without relations are created in the SAP NetWeaver Devel-oper Studio. This chapter, therefore, places the focus on the mapping of the relationships between entities, and shows how to map the entities to tables using Dali.
Note
Neither the version nor the id attribute should be changed. Read access is, of course, allowed, if the business logic requires it.
Java Persistence
4
1. In the J2EE perspective, first create the class Department, derived from BusinessEntity, in the package com.sap.examples.persis-tence.entities with the missing attributes name, description, and employees and their getter and setter methods. Then annotate this class as an entity.
2. Switch to the Java Persistence Perspective. Assume that the database connection CE1 that you created when preparing the EJB 3.0 project is active, that is, you are still connected to the database.
3. If you are no longer connected to the database, right-click the CE1 database connection in DatabaseExplorer and choose Reconnect…, enter the User ID and Password, and choose the OK button (Figure 4.11).
4. Start with the Department entity. The PersistenceOutline view recog-nizes the Department class annotated as an entity, and displays its per-sistent attributes without taking into account the inheritance hierar-chy (Figure 4.12).
Persistence properties view
5. In the PersistenceOutline view, navigate to the Department entity.
You can specify the associated table PER_DEPARTMENT in the Persis-Figure 4.11 Database Reconnect for the Java Persistence Perspective
Figure 4.12 Persistence Outline of the Department Entity
Programming with Enterprise JavaBeans 3.0/Java Persistence API 4.5
tence Properties here.9 Dali makes the selection of tables available through the CE1 connection (Figure 4.13).
Simple attributes
The procedure for the persistent attributes and relations is just as ele-gant. In the PersistenceOutline view, navigate to the name or descrip-tion attribute, and follow the same procedure for column selection (NAME, DESCRIPTION) as you did for table selection.
Relations
The PersistenceProperties view proves to be particularly helpful when specifying the mapping of the relations, because it restricts the selection to the appropriate annotations for the respective cardinalities.
For example, JPA requires, the use of a join table for the mapping the foreign key relationship of a unidirectional 1:n relationship (Mapas:One to Many in the Persistence Properties view),10 although from experi-ence a simple foreign key column would be expected. In this case, the PersistenceProperties view only provides the mapping of the foreign key relationship using a join table.
9 Because the database schema of the server contains a large number of tables, type a
“p” when selecting the table to quickly get to the tables with the prefix “PER.”
Figure 4.13 Selecting the Table as the Department Entity
10 A bidirectional 1:n relation is directly mapped using a foreign key column and does not require a join table.
Java Persistence
4
Join table 1. In the PersistenceOutline view, navigate to the employees attribute.
2. In the PersistenceProperties view, choose Mapas:OnetoMany. 3. After selecting the join table PER_EMP_X_DEP on the Join Table tab
page, choose the Edit… button to create the foreign key relationship with the joincolumn for the owning Department entity.11 The foreign key column (JoinColumn) is FK_DEPID. The column referenced by it (ReferencedColumn) is the ID column (table PER_DEP) of the owning Department entity.
4. In the same way, for the other (inverse) side of the relation, the col-umn FK_EMPID of the join table PER_EMP_X_DEP is selected as the inverse foreign key column (InverseJoinColumn) for the ID column of the entity table PER_EMPLOYEE. The PER_EMPLOYEE table is implied by the referenced Employee entity (Figure 4.14).
5.Listing 4.7 shows the annotations of the employee attribute of the department that this generates.
11 The entity that owns the relation is the one with the table that contains the join col-umn, i.e. the one that determines the updates to the relationship in the database.
Figure 4.14 Unidirectional 1:n Relation
Programming with Enterprise JavaBeans 3.0/Java Persistence API 4.5
@OneToMany
@JoinTable(
joinColumns = @JoinColumn(name = "FK_DEPID", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "FK_EMPID", referencedColumnName = "ID"), name = "PER_EMP_X_DEP")
protected List<Employee> employees =
new ArrayList<Employee>();
Listing 4.7 Annotations of the Employee Attribute
The bidirectional m:n relationship (Mapas:ManytoMany in the Persis-tenceProperties view) between Employee and Project requires an anno-tation on both sides, where the mapping is, of course, performed using a join table.
For the members relation of the Project entity, choose PER_EMPX_PRJ with its columns FK_PRJID and FK_EMPID, and the associated ID columns of the Project and Employee entities (PER_PROJECT and PER_EMPLOYEE tables) to build the join column or the inverse join column (Figure 4.15).
You do not need to specify the PER_PROJECT or PER_EMPLOYEE table, because it is uniquely determined by the referenced Project or Employee entity (Listing 4.8).
Figure 4.15 Bidirectional m:n Relationship
Java Persistence
4
@ManyToMany
@JoinTable(name="PER_EMP_X_PRJ",
joinColumns = @JoinColumn(name = "FK_PRJID", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "FK_EMPID", referencedColumnName = "ID")) protected List<Employee> members;
Listing 4.8 Annotations of the Members Attribute
The Employee entity is inversely annotated in the same way (Listing 4.9).
@ManyToMany
@JoinTable(name="PER_EMP_X_PRJ",
joinColumns = @JoinColumn(name = "FK_EMPID", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "FK_PRJID", referencedColumnName = "ID")) protected List<Project> projects;
Listing 4.9 Annotations of the Project Attribute
Foreign key column
The unidirectional 1:1 relationship (Mapas:One toOne) between the Employee entity and the Address entity is mapped using a foreign key relationship. Although it is theoretically unimportant which table of the two entities involved contains the foreign key column, the JPA specifies here that you must create it on the side of the relationship on which the unidirectional relation is annotated. In this case, this is the Employee entity, address attribute (Figure 4.16).
1. In the PackageExplorer view, choose the Employee class.
2. In the PersistenceOutline view, navigate to the address attribute.
3. In the PersistenceProperties view, choose Mapas:OnetoOne. 4.FK_ADDRID (table PER_EMPLOYEE) is the foreign key column (Join
Col-umn). The column referenced by it (ReferencedColumn), ID, belongs to the table of the Address entity.
5.Listing 4.10 shows the annotation of the address attribute of the Employee entity generated with the PersistenceProperties view.
Programming with Enterprise JavaBeans 3.0/Java Persistence API 4.5
@OneToOne
@JoinColumn(name="FK_ADDRID", referencedColumnName = "ID") protected Address address;
Listing 4.10 Annotations of the Address Attribute
While this section has looked at the detail of the annotation of the rela-tions and the special features to be taken into account in this context, the next section is oriented toward working with the entities, and therefore programming the application logic in the session façade.