Creating a RESTful Web Service - Part 1/5
I have been doing a lot of work with data access services recently so I figure it's time to share what I have discovered. Don't be scared off that this series is broken into 5 parts, I will keep them short:
- Part 1 - The Database
- Part 2 - Mapping the Database to JPA Entities
- Part 3 - Mapping JPA entities to XML (using JAXB)
- Part 4 - The RESTful Service
- Part 5 - The Client
In this series of posts we will use a number of standard Java EE technologies to quickly create a RESTful data access service:
- JSR-317 - Java Persistence Architecture (JPA)
- JSR-222 - Java Architecture for XML Binding (JAXB)
- JSR-220 - Enterprise JavaBeans (EJB)
- JSR-311 - The Java API for RESTful Web Services (JAX-RS)
Database Model
The following database model will be used for this example. This particular database stores customer related information. I am using Oracle Database XE, but you could use almost any database with a JDBC driver.
CUSTOMER Table
1
2
3
4
5
6
7
| CREATE TABLE "CUSTOMER" ( "ID" NUMBER NOT NULL ENABLE, "FIRST_NAME" VARCHAR2(50), "LAST_NAME" VARCHAR2(50), CONSTRAINT "CUSTOMER_PK" PRIMARY KEY ("ID") ENABLE )/ |
ADDRESS Table
1
2
3
4
5
6
7
8
9
| CREATE TABLE "ADDRESS" ( "ID" NUMBER NOT NULL ENABLE, "STREET" VARCHAR2(50), "CITY" VARCHAR2(50), CONSTRAINT "ADDRESS_PK" PRIMARY KEY ("ID") ENABLE, CONSTRAINT "ADDRESS_FK" FOREIGN KEY ("ID") REFERENCES "CUSTOMER" ("ID") ENABLE )/ |
PHONE_NUMBER Table
1
2
3
4
5
6
7
8
9
10
| CREATE TABLE "PHONE_NUMBER" ( "ID" NUMBER NOT NULL ENABLE, "TYPE" VARCHAR2(50), "NUM" VARCHAR2(50), "ID_CUSTOMER" NUMBER, CONSTRAINT "PHONE_NUMBER_PK" PRIMARY KEY ("ID") ENABLE, CONSTRAINT "PHONE_NUMBER_FK" FOREIGN KEY ("ID_CUSTOMER") REFERENCES "CUSTOMER" ("ID") ENABLE )/ |
JDBC Resource & Connection Pool
Next we need to configure a connection pool on our application server. I will be using GlassFish Server Open Source Edition version 3.0.1. These steps will vary depending on the application server you are using.
- Copy the JDBC driver (ojdbc14.jar) to
/glashfish/lib - Launch the Administration Console
- Create a Connection Pool:
- Name = CustomerService
- Resource Type = 'javax.sql.ConnectionPoolDataSource'
- Database Vendor = Oracle (or whatever database vendor is appropriate to your database)
- Click Next and fill in the following "Additional Properties":
- User (e.g. CustomerService)
- Password (e.g. password)
- URL (e.g. jdbc:oracle:thin:@localhost:1521:XE)
- Use the "Ping" button to test your database connection
- Create a JDBC Resource called "CustomerService"
- JNDI Name = CustomerService
- Pool Name = CustomerService (name of the connection pool you created in the previous step)
Java Persistence Architecture (JPA) is the Java EE standard for mapping POJOs to a relational database. In this example we will use JPA to interact with our database data we set up in part 1.
JPA Entities
The following JPA entities can be created by hand, or the Eclipse Dali project can be used (http://www.eclipse.org/webtools/dali/). Dali has a useful feature where JPA entities can be generated from database tables.
Customer Entity
We want the customer, address, and phone number data to be treated as a unit. When a customer is added/removed we want the corresponding address and phone numbers to be added/removed. To accomplish this we will specify CascadeType.ALL on the address and phoneNumber relationships.
In addition to the database mappings, I've added a named query to the Customer entity. This query will give us all customers from a particular city.
Address Entity
PhoneNumber Entity
META-INF/persistence.xml
For this example I will use the EclipseLink JPA implementation. If you are using another JPA implementation the configuration willl vary slightly.
Packaging/Deployment
Ultimately we will package all the META-INF/pesistence.xml and our JPA entities in a JAR file. We will hold off actually creating the JAR until we apply the XML representation using JAXB in part 3.
- Part 1 - The Database
- Part 2 - Mapping the Database to JPA Entities
- Part 3 - Mapping JPA entities to XML (using JAXB)
- Part 4 - The RESTful Service
- Part 5 - The Client
JPA Entities
The following JPA entities can be created by hand, or the Eclipse Dali project can be used (http://www.eclipse.org/webtools/dali/). Dali has a useful feature where JPA entities can be generated from database tables.
Customer Entity
We want the customer, address, and phone number data to be treated as a unit. When a customer is added/removed we want the corresponding address and phone numbers to be added/removed. To accomplish this we will specify CascadeType.ALL on the address and phoneNumber relationships.
In addition to the database mappings, I've added a named query to the Customer entity. This query will give us all customers from a particular city.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| package org.example;import java.io.Serializable;import javax.persistence.*;import java.util.Set;@Entity@NamedQuery(name = "findCustomersByCity", query = "SELECT c " + "FROM Customer c " + "WHERE c.address.city = :city")public class Customer implements Serializable { private static final long serialVersionUID = 1L; @Id private long id; @Column(name="FIRST_NAME") private String firstName; @Column(name="LAST_NAME") private String lastName; @OneToOne(mappedBy="customer", cascade={CascadeType.ALL}) private Address address; @OneToMany(mappedBy="customer", cascade={CascadeType.ALL}) private Set public long getId() { return this.id; } public void setId(long id) { this.id = id; } public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return this.lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Address getAddress() { return this.address; } public void setAddress(Address address) { this.address = address; } public Set return this.phoneNumbers; } public void setPhoneNumbers(Set this.phoneNumbers = phoneNumbers; } } |
Address Entity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| package org.example;import java.io.Serializable;import javax.persistence.*;@Entitypublic class Address implements Serializable { private static final long serialVersionUID = 1L; @Id private long id; private String city; private String street; @OneToOne @PrimaryKeyJoinColumn private Customer customer; public long getId() { return this.id; } public void setId(long id) { this.id = id; } public String getCity() { return this.city; } public void setCity(String city) { this.city = city; } public String getStreet() { return this.street; } public void setStreet(String street) { this.street = street; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; }} |
PhoneNumber Entity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| package org.example;import java.io.Serializable;import javax.persistence.*;@Entity@Table(name="PHONE_NUMBER")public class PhoneNumber implements Serializable { private static final long serialVersionUID = 1L; @Id private long id; private String num; private String type; @ManyToOne @JoinColumn(name="ID_CUSTOMER") private Customer customer; public long getId() { return this.id; } public void setId(long id) { this.id = id; } public String getNum() { return this.num; } public void setNum(String num) { this.num = num; } public String getType() { return this.type; } public void setType(String type) { this.type = type; } public Customer getCustomer() { return this.customer; } public void setCustomer(Customer customer) { this.customer = customer; } } |
META-INF/persistence.xml
For this example I will use the EclipseLink JPA implementation. If you are using another JPA implementation the configuration willl vary slightly.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <persistence version="1.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="CustomerService" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>CustomerService</jta-data-source> <class>org.example.Customer</class> <class>org.example.Address</class> <class>org.example.PhoneNumber</class> <properties> <property name="eclipselink.target-database" value="Oracle" /> <property name="eclipselink.logging.level" value="FINEST" /> <property name="eclipselink.logging.level.ejb_or_metadata" value="WARNING" /> <property name="eclipselink.logging.timestamp" value="false"/> <property name="eclipselink.logging.thread" value="false"/> <property name="eclipselink.logging.session" value="false"/> <property name="eclipselink.logging.exceptions" value="false"/> <property name="eclipselink.target-server" value="SunAS9"/> </properties> </persistence-unit></persistence> |
Packaging/Deployment
Ultimately we will package all the META-INF/pesistence.xml and our JPA entities in a JAR file. We will hold off actually creating the JAR until we apply the XML representation using JAXB in part 3.
No comments:
Post a Comment