欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Simple Java Persistence API (JPA) Demo JavaJPANetbeansOODerby

程序员文章站 2022-03-16 12:12:51
...
Simple Java Persistence API (JPA) Demo


One of the most visible obstacles to effective learning, IMHO, are wizards and various sorts of automation tricks used to simplify life for a developer. Now, before you jump off the edge and narrate a list of the benefits that tools in general provide let me just clarify that I am not against tools but newbies should not be introduced to tools when they are learning to develop software. I tend to think that it takes focus away from the details of the development process. So, I have been playing around with Netbeans 6.0 and I am quite impressed by the new features and amazed by how productive they can make developers. As with all tools, you tend to get used to the way Netbeans does things and soon enough you wouldn’t know how to find your way around a compiler argument.

One of the most interesting features in Netbeans 6.0 is its support for JPA (Disclaimer: that is not a scientific stance … feel free to let me know of any better JPA experiences). While Netbeans 6.0 makes developing CRUD applications easy, I felt like I was learning how the Netbeans team view JPA and I would like to have an appreciation for the technology on its own or at least as far low level as I can go at this point.

I set out to implement a small application without the support of Netbeans or any IDE. A text editor with syntax highlight support would suffice for this task. After starting out with a single class, I discovered that my Java development environment was not setup nicely; I guess, I never got around to customizing it after formatting my hard disk. The process of setting up the environment made me realize that I couldn’t possibly do the demo with a text editor and the compiler alone. I needed a tool to help me with the compilation and of course subsequent execution. Besides, such a tool would also allow the demo to be somewhat portable. I decided to setup Apache Ant. I especially didn’t want to have to add all the jar files that I need for this demo to some environment variable and of course doing that would tie the demo to my machine.
Overview of Object Relational Mapping

The most common and popular method of storing data is in relational database though this by no means means that it is the only way of storing data. Most business application need to interact with a relational database at some point in their running and due to data-centric business operations, the data stored in the relational databases will last longer than most of the applications used to handle it. Object oriented software development has caught on and most of the popular programming languages are either purely (to some extend) OO or are capable of supporting OO concepts. OO is great for application logic and relational databases are great for data storage and manipulation but clearly these two are based on two different paradigms. The definition of a class is not so different from a definition of a table but there are concepts in either of these paradigms that doesn’t neatly map into the other. For example there is no equivalent of OO’s inheritance in the relational paradigm but inheritance can be represented. The mismatch between these two paradigms is usually reference to as the Impedance mismatch.

In order to develop applications, software developers have had to overcome the impedance mismatch and earlier efforts had been tedious since it is upon the developer to retrieve the data from the relational database and then transform it into objects which are then handled by the business logic. When it is time to send the business objects to the database, it is once again necessary to transform the object into a format that makes sense to a relational database. This back and forth movement between the OO and relational paradigms can be automated to some degree by defining an bridge between the two such that this bridge is able to transform back and forth with minimal involvement of the developer.

This is the need that ORM products address and most of them prescribe the definition of the mapping between the OO and relational paradigms; earlier ORM technologies made use of XML to specify the mapping between classes and tables. While the EJB standard require objects to implement interfaces and extend classes in order to benefit from ORM. The EJB approach typically targeted large scale enterprise application which left the small-medium scale application uncovered with regard to persistence though yes you can argue that JDBC was adequate for the needs of the small-medium scale application developers. For the Java community TopLink and Hibernate have been popular ORM products in the commercial and open source space respectively. For the Java community, it became necessary to standardize the persistence technologies that are in use and that’s how the JPA (Java Persistence API) comes into play.
The Idea of the Application

The application in this scenario manages the rooms in a hotel. From outset, the emphasis of the application will be more on the model than its capabilities – after all the objective is to get a hands on experience with JPA. This also mean that any UI enhancements will be added later on (… at least eventually?Simple Java Persistence API (JPA) Demo
            
    
    
        JavaJPANetbeansOODerby ).

The application will enable the user to keep track of hotels; hotels have rooms and rooms have features and characteristics that distinguish them from all others.
Objectives

    * Demonstrate the basic CRUD capabilities of JPA without the use of an IDE
    * Portability across database products supported by JPA
    * The application should be self contained – doesn’t need a large, network based, multi-user RDBMS to implement

The Design

The application will be designed in 3-tiers though for the moment, I am more interested in the database layer and application layer. So, for the database … we will not worry too much about which database we are using because one of the objectives is to have the demo portable across the vendor divide which of course means that we are not going to leverage features specific to a particular database product (not that we need to do that).

The original database was developed on Apache Derby in order to achieve the objective of having the application self contained. Apache Derby can be configured as a network server but in this application it is configured as an embedded database. There are obvious performance implications as a result of using an embedded database but portability in the design should make it easy to switch to the network server installation of Apache Derby or move up to something much larger like MySQL or perhaps even Microsoft SQL Server. While working on the application, I couldn’t have Apache Derby’s tools connected to the database and run the application successfully at the same time. Multiple access to the database was not allowed.

A bit of a background on the database selection process: the original intention was to use SQLite as the database but the JPA implementation distributed with Sun’s GlassFish Application Server does not support SQLite as a database at the time. Other embedded database products considered included HSQL and PointBase and of course Apache Derby.

For the business layer, I have the following objects identified:

      Object Name Description
      Hotel Network operator client
      Room A Hotel room
      Feature A feature
      FeatureGroup Enables features to be group under one category and thus make it easier to ensure sensible feature assignments to rooms e.g. Single bed and double bed are two separate features but both can be assigned to the same room. Putting these features in one category would make it easy for the application to do validations so as to ensure sensible features are chosen for the rooms.
       

The Java Persistence API

From Sun:

    The Java Persistence API is a POJO persistence API for object/relational mapping. It contains a full object/relational mapping specification supporting the use of Java language metadata annotations and/or XML descriptors to define the mapping between Java objects and a relational database. It supports a rich, SQL-like query language (which is a significant extension upon EJB QL) for both static and dynamic queries. It also supports the use of pluggable persistence providers.

The JPA is developed as part of EJB 3.0 (JSR-220). EJB is a standard server-side component model implemented in application servers to enable them manage transactions, object distribution, concurrency, security, persistence, and resource management. These capabilities are automatically provided to distributed business applications that run in a compliant application. Included in the list of EJB capabilities is persistence and in EJB 3.0 that is the part of the architecture that is handled by JPA.
Entity

This is a Java class that represents an object in the domain model and as such its state can be persisted to a data store for late retrieval. Java Persistence API does its magic on what is commonly referred to as a Plain Old Java Object or simply POJO. A normal Java class is marked as an entity either through annotations or in an XML mapping descriptor.

Entities support inheritance, polymorphic associations, and polymorphic queries. Both abstract and concrete classes can be entities. Entities may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes.

The state of an entity is represented by instance variables, which may correspond to Java-Beans properties. The state of the entity is available to clients only through the entity’s accessor methods (getter/setter methods) or other business methods. Instance variables must be private, protected, or package visibility. The mapped state of an entity needs to be accessible to the persistence provider at run time so that the provider can update the database when the state of the entity changes. There are two ways of accessing an entity’s state:

   1. Field Access: annotating the fields of the entity (as shown below) will cause the persistence provider to by-pass the getter and setter methods of the entity class. Notice that the getter/setter methods may or may not be present but they are ultimately ignored by the persistence provider.
   2. Property Access: when this method or mode of access is used the getter/setter methods must be present. The annotations are placed on the getter methods of the entity.

The JPA puts some requirements on a Entity class as follows:

    * The entity class must have a no-arg constructor. The entity class may have other constructors as well.
      The no-arg constructor must be public or protected.
    * The entity class must be a top-level class. An enum or interface should not be designated as an entity.
    * The entity class must not be final. No methods or persistent instance variables of the entity class may be
      final.
    * If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the
      entity class must implement the Serializable interface.

The Annotations

JPA leverages annotations to implement object relational mapping but it is by no means the only way to achieve mapping. To understand the need for annotations, just consider the number of frameworks and APIs that make use of XML for configuration. It has become complicated to manage the large number of XML that can be found in one JEE project.

JPA specific annotations are colored orange in the above code sample and their simple and brief explanation follows:

Code Sample:

package model;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.OneToMany;
import javax.persistence.JoinColumn;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;

import java.util.Date;
import java.util.Collection;

@Entity
@Table(name=”tblHotal”)
/**
* Hotel class
*
* @author Imma
*/
public class Hotel
{
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int id;
    @Column private String name;
    @Temporal(TemporalType.TIMESTAMP) private Date dateCreated;
    @OneToMany(mappedBy=”hotel”) private Collection<Room> rooms;

    public Hotel(){}

    public void setId(int id) { this.id = id; }
    public void setName(String name) { this.name = name; }
    public void setRooms(Collection<Room> rooms){ this.rooms = rooms; }
    public void setDateCreated(Date dateCreated) { this.dateCreated = dateCreated; }

    public int getId() { return this.id; }
    public String getName() { return this.name; }
    public Collection<Room> getRooms() { return this.rooms; }
    public Date getDateCreated() { return this.dateCreated; }
}




@Entity: marks an class as an entity. It is normally used to mark classes but can also be used to annotated subclasses and superclasses which may persist to different tables.

@Table: this annotation provides the mapping details between the entity class and the database table to which data about the entity is to be persisted and subsequently retrieved.

@Id: identifies a field of the class as  simple primary key. It can be used in conjunction with the @GeneratedValue annotation for simple primary keys that are automatically generated by the database using an auto increment feature that is common with most RDBMS.

@GeneratedValue: identifies an attribute such as a primary key field that is automatically generated.

@Column: this annotation tag is used to mark the column to which an attribute of an entity is persisted to. By default it takes on the name of the attributes which is being annotated. It can be placed on a field for field access or on a method for property access mode. In the above code sample the entity attribute name is annotated with the @Column annotation tag and since the name of the column is not specified, the JPA runtime assumes that name of the column is the same as the name of the attribute.

@Temporal: used to make a date, time or timestamp field.

@OneToMany: associations between entities can be bidirectional. For example, in the example application: a hotel has many rooms thus many rooms belong to one hotel. In order to fully express this relationship, two mappings are required. The relationship between a hotel and its rooms, with the hotel as the owning entity is marked using the @OneToMany annotation as shown in the code sample above. The inverse of this association is using the @ManyToOne annotation which is described next (after the code sample below).

The Room Class code is shown below; a hotel has many rooms and a room belongs to one and only one hotel. Notice how the relationship/association between the two classes is achieved using annotations and JPA. In addition to this, a room has features and a feature can below to more than one room. Conversely, a room has many features.

package model;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.ManyToOne;
import javax.persistence.ManyToMany;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;

import java.util.Date;
import java.util.Collection;

@Entity
@Table(name=”tblRoom”)

/**
* Room class
*
* @author Imma
*/

public class Room
{
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int id;
    @Column private String number;
    @ManyToOne @JoinColumn(name=”fkHotel”) private Hotel hotel;
    @ManyToMany
    @JoinTable(name=”tblRoomFeatures”, joinColumns=@JoinColumn(name=”fkRoom”),
               inverseJoinColumns=@JoinColumn(name=”fkFeature“))
    private Collection<Feature> features;

    public Room() {}

    public void setId(int id) { this.id = id; }
    public void setNumber(String name) { this.number = number; }
    public void setHotel(Hotel hotel) { this.hotel = hotel; }
    public void setFeatures(Collection<Feature> features) { this.features = features; }

    public int getId() { return this.id; }
    public String getNumber() { return this.number; }
    public Hotel getHotel() { return this.hotel; }
    public Collection<Feature> getFeatures() { return this.features; }

    public String getFeaturesAsString(){
        StringBuilder sb = new StringBuilder();
        sb.append(“{“);
        for(Feature f : features){
            sb.append(f.getName());
            sb.append(“, “);
        }

        if(sb.length() > 2)
        sb.delete(sb.lastIndexOf(“,”), sb.length());
        sb.append(“}”);

        return sb.toString();
    }
}




@ManyToOne: this is an annotation that is used to map a many to one relationship between entities. For example, in the code sample above a Room belongs to a hotel and a hotel has many rooms. I think it is easier to think of it as mapping many rooms to one hotel hence Many-To-One. Depending on how you named your columns in your database, you may need to use an additional annotation to complete the many to one mapping.

@JoinColumn: at the simplest level, this annotation is used to indicate columns in a database table that are involved in a relationship. This relationship could be a many to one as described above or a many-to-many relationship, as described below. In a many-to-one relationship, the @JoinColumn annotation is used to indicate the foreign key in the entity’s table.

@ManyToMany: this annotation is used to mark many-to-many associations between entities. In relational databases, this would involve the use of a joining table and the @JoinTable annotation is used to indicated this joining table as part of the @ManyToMany annotation.

@JoinTable: this annotation is basically used to mark a joining table for a many to many association. The joinColumns attribute of this annotation is used to indicate the columns that related the owning entity (Feature in the example below) to the other side of the relationship (Rooms in the example below). The inverse of the relationship is indicated using the inverseJoinColumns attribute of the @JoinTable annotation as indicated in the code sample below.

package model;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.ManyToOne;
import javax.persistence.ManyToMany;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;

import java.util.Date;
import java.util.Collection;

@Entity
@Table(name=”tblFeature”)

/**
* Feature class
*
* @author Imma
*/
public class Feature
{
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int id;
    @Column private String name;
    @Column private String description;
    @ManyToOne @JoinColumn(name=”fkFeatureGroup”) private FeatureGroup group;
    @ManyToMany
    @JoinTable(name=”tblRoomFeatures”, joinColumns=@JoinColumn(name=”fkFeature”),
               inverseJoinColumns=@JoinColumn(name=”fkRoom“))
    private Collection<Room> rooms;

    public Feature() {}

    public void setId(int id) { this.id = id; }
    public void setName(String name) { this.name = name; }
    public void setDescription(String description) { this.description = description; }
    public void setRooms(Collection<Room> rooms) { this.rooms = rooms; }
    public void setGroup(FeatureGroup group) { this.group = group; }

    public int getId() { return this.id; }
    public String getName() { return this.name; }
    public String getDescription() { return this.description; }
    public Collection<Room> getRooms() { return this.rooms; }
    public FeatureGroup getGroup() { return this.group; }
}




This is part one of this demo. Part II will dwell on how JPA comes to life. If you have any comments and questions, please let me know through the comments section of this blog.

出自:http://imma.wordpress.com/2007/11/26/simple-java-persistence-api-jpa-demo/