In this section, we shall create entities from database tables we created earlier. Select the project node in Project Explorer and go to File | New | Other. In the New wizard window, select JPA Entites from Tables from the JPA folder as shown in the following screenshot. Click on Next. Alternatively, you can right-click on the project node in Project Explorer and select Generate Entities from Tables from JPA Tools.
In Select Tables, select the database connection configured when adding the JPA project facet. Select the OE Schema. Select the CATALOG, EDITION, SECTION, and ARTICLE tables. Select the checkbox Update class list in persistence.xml and click on Next as shown in the following screenshot:
The entities to be generated have relationships between them. The Catalog
entity has a one-to-many relationship with the Edition
entity. The Edition
entity has a one-to-many relationship with the Section
entity and the Section
entity has a further one-to-many relationship with the Article
entity. In Table Associations, we shall define the associations between the tables. Click on the + button to create an association as shown in the following screenshot:
In Association Tables, select the tables to create an association between them. We will need to create an association for each of the relationships. Click on the button for the Table 1 field as shown in the following screenshot:
Select the CATALOG table and click on OK, as shown in the following screenshot:
Similarly, select EDITION as Table 2. The Association kind is Simple association by default, which is what we need. Now, click on Next as shown in the following screenshot:
Specify the join columns between the CATALOG and EDITION tables as ID using the Add button and click on Next as shown in the following screenshot:
As the Catalog
entity has a one-to-many relation with the Edition
entity, in Association Cardinality
select One to many and click on Finish as shown in the following screenshot:
The table association between the CATALOG and EDITION tables gets defined and the table join also gets defined.
We need to set the cascade value for the association. Cascading is used to cascade database table operations to related tables. Select the button adjacent to the Cascade field for the catalog property as shown in the following screenshot:
Cascade specifies the operations that must be cascaded to the target of the association. The join table is defined on the owning side of the association and cascade operations may be initiated from the owning side. In a one-to-many relationship, the many side is always the owning side of the relationship, which makes the Edition the owning side. But, in a bi-directional relationship operations may be initiated from the non-owning side by specifying a join table on the non-owning side also. The mappedBy element is only specified on the non-owning side. In the Edition entity, select the Cascade operations to be persist, merge, and refresh. The remove option is not included as we don't want to delete the one-side entity if a many-side entity is deleted as other many-side entities may still be present. Click on OK as shown in the following screenshot:
Similarly, select the Cascade button for the Catalog entity as shown in the following screenshot:
Set the Cascade
element to all as we want to delete all Edition
entities if the associated Catalog
entity is deleted as shown in the following screenshot:
The Cascade values get specified. Similarly, define associations between the EDITION and SECTION, and SECTION and ARTICLE tables. After the associations are defined, click on Next as shown in the following screenshot:
In this section, we shall customize the default entity generation. The aspects of entities that may be customized are the Table Mapping (primary key generator, sequence name, entity access, association fetch, and collection properties type) and Java class. EclipseLink
creates entity identifiers (primary keys) using one of the strategies specified in the JPA specification; sequence objects, identity columns, tables, or provider specified strategy. Oracle database supports sequences, therefore we shall use sequence as the Key generator. Specify a sequence generator with Sequence name to be the pattern $table_seq
. The $table
variable shall be replaced by the table name. Specify Entity access as Property, and Associations fetch as Eager. With Eager fetching the associated entities are immediately fetched when an entity is retrieved. Select Collection properties type as java.util.List. Specify the
Source Folder path and Package for the entities and click on Next.
The provision to customize individual entities is provided, but we don't need to customize individual entities as we already customized the default entity generation which applies to all the entities. Now, click on Finish as shown in the following screenshot:
The entities Catalog, Edition, Section, and Article get created as shown in the following screenshot:
A JPA diagram that shows the relationships between the different entities may also be created. Right-click on perstistence.xml and select Open Diagram as shown in the following screenshot:
Click on each of the entities in the Project Explorer tab and drag the entities to the JPA Diagram tab. The relationships diagram between the entities gets displayed as shown in the following screenshot:
The entities are generated automatically by the JPA project facet and may need some customization, such as adding named queries. Next, we construct the entities, and add additional configuration for the entity relationships. We shall discuss the subsequent sections including the entity code automatically generated. If some of the subsequent additions are already in the entities, those code additions do not have to be re-added.
The Catalog
entity is the persistent class for the CATALOG
database table. The class is annotated with the @Entity
annotation that specifies the class to be an entity bean named queries findCatalogAll
, which selects all Catalog
entities, and findCatalogByJournal
, which selects a Catalog
entity by Journal
, are specified using the @NamedQueries
and @NamedQuery
annotations.
@Entity @NamedQueries({ @NamedQuery(name="findCatalogAll", query="SELECT c FROM Catalog c"), @NamedQuery(name="findCatalogByJournal", query="SELECT c FROM Catalog c WHERE c.journal = :journal") })
The entity class implements the Serializable
interface to serialize a cache enabled entity bean to a cache when persisted to a database. To associate a version number with a serializable class by serialization runtime, specify a serialVersionUID
variable.
private static final long serialVersionUID = 1L;
The Catalog
entity includes variables id
(primary key), journal
of type String
, and editions
for the Edition
entities associated with a Catalog
entity.
private int id; private String journal; private List<Edition> editions;
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
The @Id
annotation specifies the identifier property. The @Column
annotation specifies the column name associated with the property. The nullable
element is set to false
as the primary key is not nullable. As we selected the primary key generator to be of the type sequence
, the @SequenceGenerator
annotation is specified with a sequence name. The generation strategy is specified with the @GeneratedValue
annotation. The getter and setter methods for the identifier property are also specified.
@Id @Column(name="ID", nullable=false) @SequenceGenerator(name="CATALOG_ID_GENERATOR", sequenceName="CATALOG_SEQ",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="CATALOG_ID_GENERATOR") public int getId() { return this.id; } public void setId(int id) { this.id = id; }
Similarly, specify getter and setter methods for the journal
property. The @OneToMany
annotation specifies the bi-directional many-to-one association to Edition
. The mappedBy
element is specified on the non-owning side of the relationship, which is the Catalog
entity. The cascade
element is set to ALL
as we discussed earlier. The fetch
element is set to EAGER
. A join table is included on the non-owning side to initiate cascade operations using the @JoinTable
annotation. The join columns are specified using the @JoinColumn
annotation. The getter and setter methods for the Edition
collection are also specified.
@OneToMany(mappedBy="catalog", cascade={CascadeType.ALL}, fetch=FetchType.EAGER) @JoinTable(name="CATALOGEDITIONS", joinColumns= @JoinColumn(name="catalogId", referencedColumnName="ID"), inverseJoinColumns= @JoinColumn(name="editionId", referencedColumnName="ID") ) public List<Edition> getEditions() { return this.editions; } public void setEditions(List<Edition> editions) { this.editions = editions; }
Methods addEdition
and removeEdition
are also included to add and remove Edition
entities. For a more detailed discussion on entity beans refer to the EJB 3.0 Database Persistence with Oracle Fusion Middleware 11g book.
The Catalog
entity class is listed as follows:
package ejb3; import java.io.Serializable; import javax.persistence.*; import java.util.List; /** * The persistent class for the CATALOG database table. * */ @Entity @NamedQueries({ @NamedQuery(name="findCatalogAll", query="SELECT c FROM Catalog c"), @NamedQuery(name="findCatalogByJournal", query="SELECT c FROM Catalog c WHERE c.journal = :journal") }) public class Catalog implements Serializable { private static final long serialVersionUID = 1L; private int id; private String journal; private List<Edition> editions; public Catalog() { } @Id @Column(name="ID", nullable=false) @SequenceGenerator(name="CATALOG_ID_GENERATOR", sequenceName="CATALOG_SEQ",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="CATALOG_ID_GENERATOR") public int getId() { return this.id; } public void setId(int id) { this.id = id; } public String getJournal() { return this.journal; } public void setJournal(String journal) { this.journal = journal; } //bi-directional many-to-one association to Edition @OneToMany(mappedBy="catalog", cascade={CascadeType.ALL}, fetch=FetchType.EAGER) @JoinTable(name="CATALOGEDITIONS", joinColumns= @JoinColumn(name="catalogId", referencedColumnName="ID"), inverseJoinColumns= @JoinColumn(name="editionId", referencedColumnName="ID") ) public List<Edition> getEditions() { return this.editions; } public void setEditions(List<Edition> editions) { this.editions = editions; } public void addEdition(Edition edition) { this.getEditions().add(edition); } public void removeEdition(Edition edition) { this.getEditions().remove(edition); } }
The Edition
entity includes the named queries findEditionAll
and findEditionBySection
. The class specifies variables serialVersionUID
, id
, edition
, catalog
, and sections
. As in the Catalog
entity, id
is the identifier property. The bi-directional many-to-one association to the Catalog
relationship is specified using the @ManyToOne
annotation. The Edition
entity is the owning side of the relationship. A bi-directional many-to-one association to Section
is also specified. Add and remove methods for the Catalog
and Section
entities are also specified. The discussion that applies to the Catalog
entity also applies to the other entities, and has been omitted here for the other entities.
The Edition
entity is listed as follows:
package ejb3; import java.io.Serializable; import javax.persistence.*; import java.util.List; /** * The persistent class for the EDITION database table. * */ @Entity @NamedQueries( { @NamedQuery(name = "findEditionAll", query = "SELECT e FROM Edition e") , @NamedQuery(name = "findEditionByEdition", query = "SELECT e from Edition e WHERE e.edition = :edition") } ) public class Edition implements Serializable { private static final long serialVersionUID = 1L; private int id; private String edition; private Catalog catalog; private List<Section> sections; public Edition() { } @Id @Column(name="ID", nullable=false) @SequenceGenerator(name="EDITION_ID_GENERATOR", sequenceName="EDITION_SEQ",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="EDITION_ID_GENERATOR") public int getId() { return this.id; } public void setId(int id) { this.id = id; } //bi-directional many-to-one association to Catalog @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}) @JoinTable(name = "EditionCatalog", joinColumns = { @JoinColumn(name = "editionId",referencedColumnName = "ID") } , inverseJoinColumns = { @JoinColumn(name = "catalogId", referencedColumnName ="ID")}) public Catalog getCatalog() { return this.catalog; } public void setCatalog(Catalog catalog) { this.catalog = catalog; } //bi-directional many-to-one association to Section @OneToMany(mappedBy="edition", cascade={CascadeType.ALL}, fetch=FetchType.EAGER) @JoinTable(name = "EditionSections", joinColumns = { @JoinColumn(name = "editionId", referencedColumnName = "ID") } , inverseJoinColumns = { @JoinColumn(name = "sectionId", referencedColumnName = "ID") } ) public List<Section> getSections() { return this.sections; } public void setSections(List<Section> sections) { this.sections = sections; } public void addSection(Section section) { this.getSections().add(section); section.setEdition(this); } public String getEdition() { return edition; } public void setEdition(String edition) { this.edition = edition; } public void removeSection(Section section) { this.getSections().remove(section); } }
The Section
entity has the named queries findSectionAll
and findSectionBySectionName
, and the properties id
, sectionname
, articles
, and edition
. A bi-directional many-to-one association to Article
and a bi-directional many-to-one association to Edition
are specified. Add and remove methods for the Edition
and Article
entities are also specified.
The Section
entity is listed as follows:
package ejb3; import java.io.Serializable; import javax.persistence.*; import java.util.List; /** * The persistent class for the SECTION database table. * */ @Entity @NamedQueries({ @NamedQuery(name="findSectionAll", query="SELECT s FROM Section s"), @NamedQuery( name="findSectionBySectionName", query="SELECT s from Section s WHERE s.sectionname = :section") }) public class Section implements Serializable { private static final long serialVersionUID = 1L; private int id; private String sectionname; private List<Article> articles; private Edition edition; public Section() { } @Id @Column(name="ID", nullable=false) @SequenceGenerator(name="SECTION_ID_GENERATOR", sequenceName="SECTION_SEQ",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SECTION_ID_GENERATOR") public int getId() { return this.id; } public void setId(int id) { this.id = id; } public String getSectionname() { return this.sectionname; } public void setSectionname(String sectionname) { this.sectionname = sectionname; } //bi-directional many-to-one association to Article @OneToMany(mappedBy="section", cascade={CascadeType.ALL}, fetch=FetchType.EAGER) @JoinTable(name = "SectionArticles", joinColumns = { @JoinColumn(name="sectionId", referencedColumnName="ID")}, inverseJoinColumns = { @JoinColumn(name="articleId", referencedColumnName="ID")}) public List<Article> getArticles() { return this.articles; } public void setArticles(List<Article> articles) { this.articles = articles; } //bi-directional many-to-one association to Edition @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}) @JoinTable(name = "SectionEdition", joinColumns = { @JoinColumn(name = "sectionId", referencedColumnName = "ID") } , inverseJoinColumns = { @JoinColumn(name = "editionId", referencedColumnName = "ID") } ) public Edition getEdition() { return this.edition; } public void setEdition(Edition edition) { this.edition = edition; } public void addArticle(Article article) { this.getArticles().add(article); article.setSection(this); } public void removeArticle(Article article) { this.getArticles().remove(article); } }
The Article
entity has a named query findArticleAll
, and the properties id
, title
, and section
. A bi-directional many-to-one association to Section
is also defined.
The Article
entity is listed as follows:
package ejb3; import java.io.Serializable; import javax.persistence.*; /** * The persistent class for the ARTICLE database table. * */ @Entity @NamedQueries({ @NamedQuery(name="findArticleAll", query="SELECT a FROM Article a"), @NamedQuery( name="findArticleByTitle", query="SELECT a from Article a WHERE a.title = :title") }) public class Article implements Serializable { private static final long serialVersionUID = 1L; private int id; private String title; private Section section; public Article() { } @Id @Column(name="ID", nullable=false) @SequenceGenerator(name="ARTICLE_ID_GENERATOR", sequenceName="ARTICLE_SEQ",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ARTICLE_ID_GENERATOR") public int getId() { return this.id; } public void setId(int id) { this.id = id; } public String getTitle() { return this.title; } public void setTitle(String title) { this.title = title; } //bi-directional many-to-one association to Section @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}) @JoinTable(name = "ArticleSection", joinColumns = { @JoinColumn(name="articleId", referencedColumnName="ID")}, inverseJoinColumns = { @JoinColumn(name="sectionId", referencedColumnName="ID")}) public Section getSection() { return this.section; } public void setSection(Section section) { this.section = section; } }
In the Project Explorer, the entity beans will show some errors if the sequences or join tables have not yet been created.
The persistence.xml
configuration file specifies a persistence provider to be used for persisting the entities to the database. The persistence unit is specified using the persistence-unit
element. The transaction-type
is set to JTA
by default. The persistence provider is specified as org.eclipse.persistence.jpa.PersistenceProvider
. The jta-data-source
element specifies the JTA data source. The entity classes are specified using the class element. The eclipselink.target-server
property specifies the target server as WebLogic 10g
. The eclipselink.target-database
property specifies the target database as Oracle
. The DDL generation strategy is set to create-tables
using the eclipselink.ddl-generation
property. Other EclipseLink properties (http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#Using_EclipseLink_JPA_Extensions_for_Sch) may also be specified as required. The persistence.xml
configuration file is listed as follows:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="em" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/OracleDS</jta-data-source> <class>ejb3.Article</class> <class>ejb3.Catalog</class> <class>ejb3.Edition</class> <class>ejb3.Section</class> <properties> <property name="eclipselink.target-server" value="WebLogic_10"/> <property name="eclipselink.target-database" value="Oracle"/> <property name="eclipselink.ddl-generation" value="create-tables"/> <property name="eclipselink.logging.level" value="FINEST"/> </properties> </persistence-unit> </persistence>
The eclipselink.ddl-generation
property may be set to create-tables
or drop-and-create-tables
, which drops tables if already created and creates new tables. In development the drop-and-create-tables
may be used
as the schema may need to be updated and data cleared. We have set eclipselink.ddl-generation
to create-tables
, which creates tables only if the tables are not already created. The JPA specification does not mandate the creating of tables and only some JPA persistence providers create tables. If it is not known whether a persistence provider supports table generation it is better to create the tables, sequences, and join tables as we have also set the eclipselink.ddl-generation
value to create-tables
.