Apache Wink と OpenJPA を使用して REST リソースを作成する

Apache Wink は RESTful な Web サービスを作成するためのフレームワークです。この記事では、Wink と OpenJPA (Java Persistence Architecture) を使用してリソースを実装、保存、取得、更新する方法と、OpenJPA を使用してリソースを永続化する方法について説明します。さらに、例を示しながら、Wink と OpenJPA を使用することによってエンド・ツー・エンドで CRUD (Create、Retrieve、Update、Delete) 操作を行う方法についても説明します。

Sathish Maiya, Staff Software Engineer, IBM

Sathish Maiya photoSathish Maiya はインドにある IBM Software Labs のスタッフ・ソフトウェア・エンジニアであり、WebSphere Partner Gateway Group に所属しています。彼はインドの Mysore University でコンピューター科学の学位を取得した他、インドの Mangalore University で生体医療工学の修士号を取得しています。彼はソフトウェアの設計、開発、テストを経験してきました。



Anish Chaube, Software Engineer, IBM

Anish Chaube photoAnish Chaube はインドにある IBM Software Labs の WebSphere Partner Gateway Group に所属するアソシエート・ソフトウェア・エンジニアです。彼は Bangalore の International Institute of Information Technology で情報技術の修士号を取得しています。彼はソフトウェアの開発とテストの両方を経験してきました。



Pranab K Das, Software Engineer, IBM

Pranab Das photoPranab Das はインドにある IBM Software Labs の WebSphere Partner Gateway Group に所属するソフトウェア・エンジニアです。彼は Banaras Hindu University でコンピューター科学の学位を取得しています。彼はソフトウェアの開発とテストを経験してきました。



Dilip Sathyanarayan, Software Engineer, IBM

Dilip SathyanarayanDilip Sathyanarayan は IBM のシニア・ソフトウェア・エンジニアです。彼はソフトウェアの開発、テストとテスト自動化、技術ドキュメント作成を経験してきました。現在はニューヨークの Marist College で情報システムの修士号を取得しようとしています。



2012年 1月 13日

はじめに

REST (REpresentational State Transfer) は World Wide Web のような分散型ハイパーメディア・システムのためのソフトウェア・アーキテクチャーのスタイルです。リソース、つまり特定の情報のソースは、REST の重要な部分です。各リソースはグローバルな識別子 (例えば HTTP における URI など) によって参照されます。ネットワークのコンポーネント (ユーザー・エージェントやオリジン・サーバーなどと呼ばれます) はリソースを操作するために、標準化されたインターフェース (HTTP) を使用して通信を行い、リソースの表現を交換します。

よく使われる略語

  • CRUD: Create, Retrieve, Update, Delete
  • HTTP: HyperText Transfer Protocol
  • REST: REpresentational State Transfer
  • URI: Universal Resource Identifier
  • XML: EXtensible Markup Language

Apache Wink は、RESTful な Web サービスを作成するために使用できるフレームワークです。Wink は REST のアーキテクチャー・スタイルに従ってサービスをモデル化する手段となるため、Wink を使用すると REST Web サービスを容易に作成および利用することができます。Wink には、サービスを構成するリソース、表現、そして統一的な手段を定義して実装するのに必要なインフラストラクチャーが用意されています。また、Apache OpenJPA フレームワークは、オブジェクトの永続化を実現する方法を定義するオープンソースのソフトウェアです。

この記事では、Wink、OpenJPA、そして REST サービスを使用して、リソースに対して HTTP 操作を実行する方法や、モデル化と URI の設計、さらには Wink のアノテーションを使用してサンプルのリソースに対して CRUD (Create、Retrieve、Update、Delete) 操作を行う方法について説明します。

この記事で使用するソース・コードはダウンロードすることができます。

リソースをモデル化する

すべてのリソースを XML や JSON といった特定の構造にモデル化する必要があります。リソースのすべての属性と値は、それぞれに対応する XML の属性名と属性値にマッピングされます。この記事では例として、「従業員」(Employee) をリソースと考えましょう。各従業員には、ID、名前、住所、e-メール、電話番号など、その従業員に一意の属性があります。リスト 1 は Employee リソースの XML の構造を示しています。

リスト 1. Employee リソースの XML の構造
<employee>		
     <employeeId>101</employeeId>
     <employeeName>employee Name</employeeName>
     <address>XYZ C Block, Bangalore-560025</address>
     <email>employee email</email>
     <telephone>5551234567</telephone>
</employee>

Apache Wink フレームワークは、XML または JSON と Java オブジェクトとの間のマーシャリングとアンマーシャリングを行うために JAXB アノテーションをサポートしています。OpenJPA は Java オブジェクトをリレーショナル・データストアに永続化するためのフレームワークとして機能します。


エンティティーをモデル化する

OpenJPA のエンティティーはデータストアのレコードを表現する永続オブジェクトです。この記事で説明する例では、_EmployeeEmployee オブジェクトを表現するエンティティー・モデルです。エンティティー・クラス _Employee の各永続インスタンスは一意のデータストア・レコード (一意に決まる従業員) を表現します。エンティティー・モデルを定義するためにはアノテーションを使用します。Employee の場合には、@Table (name="CF_EMPLOYEE") を使用して Employee のエンティティーを定義します。

すべてのエンティティー・クラスは 1 つまたは複数のフィールドを宣言する必要があり、それらのフィールド全体でインスタンスの永続識別子を構成します。この記事では従業員を例として説明しますが、従業員の一意の識別子として employeeId を使用します。OpenJPA はエンティティーの version フィールドを使用することで、同じデータストア・レコードに対して同時に変更が行われているかどうかを検出します。そのために @Version アノテーションを使用します。リスト 2 に抜粋したコードは Employee リソースのエンティティー・モデルを作成する方法を示しています。

リスト 2. Employee リソースに対する OpenJPA モデル
package openJpa.model;

/**
 * Persistent Class for Employee
 * OpenJPA enables writing persistent classes in a very transparent way
 */

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Version;

@Entity
/*Each entity represents a relational datastore record.
In this code all employee records will be stored in a table called CF_EMPLOYEE */
@Table(name="CF_EMPLOYEE")
/*
Databases allow Native sequences to be created , which are database
structures that generates increasing numeric values.
SequenceGenerator annotation represents a named database Sequence
*/
@SequenceGenerator(name="employeeSeqGen", sequenceName="native(Sequence=CF_EMPLOYEE_SEQ)")
public class _Employee implements Serializable {
	
	private static final long serialVersionUID = 1L;
	public _Employee() {
		super();
	}
	
/*All entity classes must declare one or more fields which together 
forms the persistent identity of an instance.Every employee will have an
unique employeeID which is the persistent identity */
	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="employeeSeqGen")
	private int employeeId;
/* OPEN JPA uses version field in entities to detect concurrent 
modifications to the same datastore record.Version field is optional
for any entity, but without one concurrent threads or processes might succeed
in making conflicting changes to the same record at the same time. */

	@Version
	private int version;
	
	private String employeeName;
	private String address;
	private String email;
	private int telephone;
	
	public void setEmployeeId(int employeeId) {
		this.employeeId = employeeId;
	}
	public int getEmployeeId() {
		return employeeId;
	}
	public void setVersion(int version) {
		this.version = version;
	}
	public int getVersion() {
		return version;
	}
	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
	public String getEmployeeName() {
		return employeeName;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getAddress() {
		return address;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getEmail() {
		return email;
	}
	public void setTelephone(int telephone) {
		this.telephone = telephone;
	}
	public int getTelephone() {
		return telephone;
	}
}

OpenJPA とデータストアとを接続する

エンティティー・モデルを作成したら、データストアとの接続を確立するための接続ロジックを実装する必要があります (データストアはどのようなタイプのデータベースでも構いません)。そのためには、OpenJPA とデータベースとの接続ロジックを含む別個のレイヤーを作成します。単純な方法としては、データベースに関係するすべての構成をプロパティー・ファイルに含め、データベース接続を抽象化するクラス (例えば OpenJpaAdminService など) を定義します。データベースに接続するためのロジックには getEntityManager() メソッドを使用します。このメソッドが OpenJpaAdminService から呼び出された場合にプロパティー・ファイルの参照が呼び出されるようにすると、OpenJPA は接続先のデータベースを認識することができます。

ダウンロードに含まれている例では、OpenJpaMediator.java クラスが OpenJPA に関するすべての操作を処理します。この例では、このクラスのコンストラクターの中で OpenJpaAdminService.getEntityManager() 関数を呼び出し、dbConnection.properties というプロパティー・ファイルをチェックしています。データベースへ接続するには以下の 2 通りの方法があります。

  • JDBC ドライバーを使用して接続の詳細を処理する一般的な方法
  • JNDI ルックアップを使用して JDBC 接続を取得する方法 (ダウンロード・ファイルの OpenJpaAdminService.java を参照)

REST リソースをモデル化する

Apache Wink は REST Web サービス (一意の URI で指定されるリソース) を単純に実装、利用するためのフレームワークです。各リソースは 1 つまたは複数の表現を持ち、Web サービスを呼び出すと、それらの表現がクライアントとサービスの間で交換されます。Wink には以下の特徴があります。

  • RESTful なアーキテクチャー・スタイルでリソースを定義、実装するために必要なインフラストラクチャーを備えているため、統一的なインターフェース、複数の表現、サービスのイントロスペクションを実現することができます。
  • API によって REST Web サービスをサポートしています。Wink は Java ベースの API であり、アノテーションを使用することによって Web サービスのクライアントやエンド・ポイントの作成およびデプロイメントを単純化します。

サーブレットを使用して RESTful なサービスを実装することもできますが、ビジネス・ロジックの実装に必要な HTTP コードが多くなりすぎる傾向があります。Wink は HTTP コードを完全に隠し、サーブレットを Java クラスの個々のメソッドに適切にバインドすることができます。


リソース・クラスを表現する

リソースはデータの取得や操作を行うための実用的なコンポーネントを表現します。リソース・クラスはビジネス・ロジックの実装を支援するリソース・メソッドを定義します。リスト 3 は Employee リソースに対するリソース・クラスの作成方法を示しています。

エンド・ユーザーはリソースを必ず XML または JSON で表現します。ユーザーがリソースのインスタンスを新たに作成または更新する必要がある場合、ユーザーはそのリソースに対応する XML または JSON を渡します。リソース・クラスの Wink アノテーションは実際に XML または JSON を Java オブジェクトにマーシャリングします。エンティティー・オブジェクト (_Employee) のインスタンスを定義するメリットとして、XML または JSON をマーシャリングすると、そのままデータストアに永続化できる状態のエンティティー・オブジェクトが得られます。つまり Wink フレームワークの機能を利用して OpenJPA のエンティティー・オブジェクトを永続化することができます。同様に、ユーザーがリソースのインスタンスを取得したい場合には、OpenJPA がデータストアからリソースを取得してエンティティー・オブジェクトを提供します。また Wink のアノテーションにより、取得したエンティティー・オブジェクトを XML または JSON にアンマーシャリングし、その XML または JSON をエンド・ユーザーに送信することもできます。

リスト 3. リソースを表現する
package rest.resource;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import openJpa.model._Employee;

/**
 * @XmlAccessorType Controls whether fields or Javabean properties are 
 * serialized by default
 * XmlAccessType.PROPERTY: Every getter/setter pair in a JAXB-bound class will
 * be automatically bound to XML, unless annotated by XmlTransient. Fields are 
 * bound to XML only when they are explicitly annotated by some of the JAXB 
 * annotations.
 *
 */
 
@XmlAccessorType(XmlAccessType.PROPERTY)
/**
 * @XmlRootElement: Maps a class to an XML element
 */
 
@XmlRootElement(name="employee")
public class Employee {
/**
 * Instantiate Enity Object of Employee to achieve integration of APACHE WINK 
 * and OPEN JPA	
 */
private _Employee modelEmployee;
	
	public Employee() {
		super();
		this.modelEmployee = new _Employee();
	}
	
	public Employee(_Employee modelEmployee) {
		this.modelEmployee = modelEmployee;
	}
	
	@XmlTransient
	public _Employee getEmployee() {
		return this.modelEmployee;
	}
	public void setEmployee(_Employee modelEmployee)
	{
		this.modelEmployee = modelEmployee;
	}
	/**
	@XmlElement: Maps a JavaBean property to a XML element derived from property name
	*/
	@XmlElement(name = "employeeName")
	public String getEmployeeName() {
		return modelEmployee.getEmployeeName();
	}
	
	public void setEmployeeName(String employeeName) {
		modelEmployee.setEmployeeName(employeeName);
		
	}
	
	@XmlElement(name = "address")
	public String getAddress() {
		return modelEmployee.getAddress();
	}
	
	public void setAddress(String address) {
		modelEmployee.setAddress(address);
	}
	
	@XmlElement(name = "email")
	public String getEmail() {
		return modelEmployee.getEmail();
	}
	
	public void setEmail(String email) {
		modelEmployee.setEmail(email);
	}
	
	@XmlElement(name = "telephone")
	public int getTelephone() {
		return modelEmployee.getTelephone();
	}
	
	public void setTelephone(int telephone) {
		modelEmployee.setTelephone(telephone);
	}
	
	@XmlElement(name = "employeeId")
	public int getEmployeeId() {
		return modelEmployee.getEmployeeId();
	}
	
	public void setEmployeeId(int employeeId) {
		modelEmployee.setEmployeeId(employeeId);
	}
	
	@XmlElement(name = "version")
	public int getVersion() {
		return modelEmployee.getVersion();
	}
	
	public void setVersion(int version) {
		modelEmployee.setVersion(version);
	}
}

CRUD 操作

このセクションでは、Wink フレームワークと OpenJPA を使用して Employee リソースに対して実行する基本的な CRUD 操作について説明します。各操作に対して一意の URI が定義され、また各操作には 1 つの HTTP メソッドがあります。Create 操作と Update 操作の場合、リソースのデータ (Employee の詳細) を Content-Body として表現する必要があります。Employee リソースの Content-Body は JSON または XML です。Apache Wink は XML、JSON、HTML、Atom をサポートしています。表 1 は CRUD 操作の設計を示しています。

表 1. CRUD 操作
操作説明リソース/URIリソースのデータ
CREATEユーザーは新しいリソースを作成することができます。この記事の例での CREATE 操作には、新しい Employee の作成や既存のデータベースへの新しい Employee の追加などがあります。

HTTP メソッド: POST
/employeeContent-Body には Employee の詳細が特定の表現で含まれます。RESTful なインターフェースでは必ず、そのインターフェースがクライアントに対してどのような種類の表現をサポートするかを決定することができます。例えば、リスト 1 の XML 構造は新しい Employee を作成するための Content-Body になる場合があります。
RETRIEVEユーザーは Employee ID を使用して既存の従業員の詳細情報を取得することができます。取得されたデータは、クライアントに対して REST でサポートされる多様な形式で表現されます。例えば /employee/121: と指定すると ID が121 の従業員の詳細情報が得られます。

HTTP メソッド: GET
/employee/{id}GET 呼び出しには Content-Body はありません。GET は、安全で読み取り専用の冪等な呼び出しでなければならず、決してリソースの状態を変更してはなりません。
UPDATEユーザーは既存のリソースを更新することができます。この記事の例での UPDATE 操作には、データベース内にある既存の Employee の更新などがあります。

HTTP メソッド: PUT
/employeeContent-Body には Employee の詳細が特定の表現で含まれます。RESTful なインターフェースでは必ず、そのインターフェースがクライアントに対してどのような種類の表現をサポートするかを決定することができます。例えば、リスト 1 の XML 構造は Employee を更新するための Content-Body になる場合があります。
DELETEユーザーは Employee ID を使用して既存の従業員の詳細情報を削除することができます。削除後、そのユーザーはその従業員が適切に削除されたかどうかを知らせる HTTP レスポンスを受信します。例えば /employee/121: と指定すると ID が 121 の従業員の詳細情報が削除されます。

HTTP メソッド: DELETE
/employee/{id}DELETE 呼び出しには Content-Body がありません。

アクション・クラスの例

アクション・クラスは、HTTP を使用してリソースに対して実行可能なすべての CRUD 操作の実装を定義します。Apache Wink は HTTP リクエストを受信し、アクション・クラスに記述された該当のメソッドに対し、ラップされた HTTP リクエストをディスパッチします。HTTP リクエスト・パラメーター、リソース・メソッドの定義、MIME タイプを基に、各 HTTP リクエストをアクション・クラスのメソッドに対応させる必要があります。

リスト 4 は Employee リソースに対するアクション・クラスの作成方法を示しています。この例では Apache Wink による RESTful サービスを実装する EmployeeAction クラスを作成しています。このクラスは 4 つのメソッドで構成され、各メソッドが 1 つの CRUD 操作に対応しています。

リスト 4. EmployeeAction クラスを実装する
package rest.action;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import openJpa.mediator.EmployeeOpenJpaMediator;
import openJpa.model._Employee;
import rest.resource.Employee;

@Path(value = "/employee")
public class EmployeeAction {
	
	/**
	 * @param employee
	 * CREATE: @return This method is used to create new Employee
	 */
	@POST
	@Consumes(value = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	@Produces(value = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	public Response persistEmployee(Employee employee) {
		EmployeeOpenJpaMediator dao = new EmployeeOpenJpaMediator();
		_Employee _employee = employee.getEmployee();
		try {
			dao.beginTransaction();
			dao.persist(_employee);
			dao.commitTransaction();

		}  catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			dao.rollbackTransaction();
			return Response.status(400).entity(
					"Employee create failed!").build();
		} finally {
			dao.close();
		}
		Employee employeeCreated = new Employee(_employee);
		return Response.ok(employeeCreated).build();
	}
	
	/**
	 * @param id
	 * RETRIEVE: @return This method is used to retrieve a particular Employee 
	   through employeeId
	 */
	@GET
	@Path("/{id}")
	@Produces(value = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
	public Response findEmployee(@PathParam(value = "id") int id) {

		EmployeeOpenJpaMediator employeeDao = new EmployeeOpenJpaMediator();
		_Employee u = new _Employee();
		try {
		if ((u = (_Employee) employeeDao.getById(_Employee.class, id)) != null) {
			return Response.ok(new Employee(u)).build();
		} else {
			return Response.status(404).entity(
			"Employee id does not exist").build();
		}
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return Response.status(404).entity(
					"Employee id does not exist").build();
		}
	}
	
	/**
	 * @param employee
	 * UPDATE: @return this method is used to update a particular Employee 
	   already existing
	 */
	@PUT
	@Produces(value = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	@Consumes(value = { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	public Response updateEmployee(Employee employee) {
		EmployeeOpenJpaMediator ud = new EmployeeOpenJpaMediator();
		_Employee _employee = employee.getEmployee();
		try {
			ud.beginTransaction();
			ud.update(_employee);
			ud.commitTransaction();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			ud.rollbackTransaction();
			return Response.status(409).entity(
					"Employee update failed!").build();

		} finally {
			ud.close();
		}

		Employee employeeUpdated = new Employee(_employee);
		return Response.ok(employeeUpdated).build();
	}
	
	/**
	 * @param id
	 * DELETE: This method is used to delete a particular Employee through employeeId
	 */
	@DELETE
	@Path("/{id}")
	public Response deleteEmployee(@PathParam(value = "id") int id) {
		EmployeeOpenJpaMediator pd = new EmployeeOpenJpaMediator();
		_Employee _employee = (_Employee) pd.getById(_Employee.class, id);
		if(_employee!=null){
		try {
			pd.beginTransaction();
			pd.delete(_employee);
			pd.commitTransaction();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			pd.rollbackTransaction();
			return Response.status(400).entity(
					"Employee delete had an error!").build();
		} finally {
			pd.close();
		}
	return Response.ok().build();
		}
		else{
			return Response.status(400).entity(
			"Employee ID does not exist!").build();
		}
	}
}

アクション・クラスを呼び出す

REST サーブレットが初期化されると、実際に Application クラスを継承する RestApp クラスが呼び出されます。RestApp により、Apache Wink は該当の action クラスを取得して、action クラスに記述された該当の HTTP 操作を実行することができます。つまり action クラスが Wink フレームワークに登録されます。リスト 5 は EmployeeAction クラスに対して action クラスを呼び出すためのコードを示しています

リスト 5. EmployeeAction クラスを実装する (続き)
 package rest.app;

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
import rest.action.EmployeeAction;

public class RestApp extends Application {
	@Override
	public Set<Class<?>>: getClasses() {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		addResources(classes);
		return classes;
	}
	
	public Set<Class<?>> addResources(Set<Class<?>>classes)
	{
		classes.add(EmployeeAction.class);
		return classes;
	}
	
}

まとめ

この記事では、REST のアーキテクチャー、OpenJPA の実装、Apache Wink 標準により、RESTful なサービスの実装を単純化する方法を説明しました。OpenJPA と新しい Apache Wink フレームワークとを統合すると、REST サービスを利用してリソースに対して HTTP 操作を実行することができます。またこの記事では、OpenJPA のエンティティーのモデル化、リソースのモデル化、URI の設計、Wink のアノテーションを使用してサンプル・リソースに対して CRUD 操作を実行する方法についても説明しました。


ダウンロード

内容ファイル名サイズ
Code for REST resources modeled using OpenJPAOpenJPA.zip9KB

参考文献

学ぶために

製品や技術を入手するために

  • Apache Wink: Wink フレームワークの最新バージョンのソース・コードとバイナリー、およびその他のサンプル・プロジェクトを入手してください。
  • IBM 製品の評価版: IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2、Lotus、Rational、Tivoli、および WebSphere が提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=784163
ArticleTitle=Apache Wink と OpenJPA を使用して REST リソースを作成する
publish-date=01132012