JAX-RSによる簡単なWebサービス作成 †書きかけの記事 JAX-RS を使用してDBと連携する簡単なWebサービスを作成する手順を記載する。 ※環境構築は JAX-RSの環境構築 も参照 環境構築 †データベースの作成 †以下、MySQLターミナルから行う /* DB作成 */ CREATE DATABASE example_db DEFAULT CHARACTER SET utf8; /* ユーザ作成 */ CREATE USER example_user@localhost IDENTIFIED BY 'example_pass'; /* 権限付与 */ GRANT ALL ON example_db.* TO example_user@localhost; /* テーブル作成 */ CREATE TABLE `books` ( `id` int(11) NOT NULL AUTO_INCREMENT, `isbn` varchar(255) DEFAULT NULL, `title` varchar(255) DEFAULT NULL, `price` int(11) DEFAULT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /* データ作成 */ insert into books(isbn,title,price,created_at,updated_at) values('978-4822280536', 'デッドライン', 2376, NOW(), NOW()) ,('978-4873114798', 'プログラマが知るべき97のこと', 2052, NOW(), NOW()) ,('978-4873115658', 'リーダブルコード', 2592, NOW(), NOW()); Java、Tomcat のインストール †※7以降をインストール eclipse、Tomcatプラグインのインストール †※eclipse4.2以降だとmavenプラグイン等が最初から入っているので楽。(上記の jackson 等もMavenで取得できるので) 関連 jar を取得する †Gradle で取得する dependencies { compile "org.glassfish.jersey.containers:jersey-container-servlet:latest.release" compile "org.glassfish.jersey.media:jersey-media-json-jackson:latest.release" compile "mysql:mysql-connector-java:latest.release" } ※ 詳細は Gradleでjarの取得だけを行う を参照 手動で取得する場合は以下を参照。(ただし、バージョンの不整合等で動かないかもしれない)
プロジェクトの作成 †動的Webプロジェクトの作成 †プロジェクト名、コンテキストルート とも "MyService" として作成 jar のコピー †
server.xml の編集 †Context 配下にResource設定を追加 <Context docBase="MyService" <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/example_db" username="example_user" password="example_pass" /> </Context> ※ http://dev.mysql.com/doc/connector-j/en/connector-j-usagenotes-tomcat.html web.xml の編集 †・ ・ <!-- /ws で始まるURLをWebサービスとする --> <servlet> <servlet-name>javax.ws.rs.core.Application</servlet-name> </servlet> <servlet-mapping> <servlet-name>javax.ws.rs.core.Application</servlet-name> <url-pattern>/ws/*</url-pattern> </servlet-mapping> <!-- server.xmlで定義したDB接続への参照を定義 --> <resource-ref> <res-ref-name>jdbc/datasource</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </web-app> ※name、type、auth は server.xml で定義したものと合わせる。 サービスの親クラスの作成 †とりあえず動作確認に必要な実装だけ package example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; public class ServiceBase { /** * DB接続の取得 * @return DB接続 */ public Connection getConnection(){ Connection con = null; try { // Webアプリ実行用 Context context = new InitialContext(); DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/MySQLDB"); con = dataSource.getConnection(); } catch (Exception e) { // POJOでの実行用 try { Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection("jdbc:mysql://localhost:3306/example_db","example_user","example_pass"); con.setAutoCommit(false); } catch (Exception e1) { e1.printStackTrace(); } } return con; } /** * SQL実行(検索) * @param con DB接続 * @param sql SQL文 * @return 結果 * @throws SQLException */ public List<Map<String,Object>> selectQuery(Connection con, String sql, Object[] params) throws SQLException{ List<Map<String,Object>> list = new ArrayList<Map<String,Object>>(); PreparedStatement statement = null; try { statement = con.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { Object param = params[i]; if (param instanceof Integer) { statement.setInt(i+1, (Integer)param); } if (param instanceof String) { statement.setString(i+1, (String)param); } } } ResultSet result = statement.executeQuery(); java.sql.ResultSetMetaData metaData = result.getMetaData(); int colCnt = metaData.getColumnCount(); while(result.next()){ Map<String,Object> rec = new HashMap<String, Object>(); for (int i = 1; i <= colCnt; i++) { String colName = metaData.getColumnName(i); int colType = metaData.getColumnType(i); if (colType == java.sql.Types.VARCHAR){ rec.put(colName, result.getString(colName)); } if (colType == java.sql.Types.INTEGER){ rec.put(colName, result.getInt(colName)); } if (colType == java.sql.Types.TIMESTAMP){ Timestamp ts = result.getTimestamp(colName); if (ts != null) { rec.put(colName, ts.toString()); } } } list.add(rec); } } finally { try { if (statement != null) { statement.close(); } } catch (Exception e) { } } return list; } public List<Map<String,Object>> selectQuery(Connection con, String sql) throws SQLException{ return selectQuery(con, sql, null); } } DTOの作成 †package example.dto; import java.io.Serializable; import javax.ws.rs.FormParam; import javax.xml.bind.annotation.XmlRootElement; // ↓ このアノテーションは XMLで値を返却する場合のみ必要 @XmlRootElement public class BookDto implements Serializable { private static final long serialVersionUID = 1L; private int id; // ↓ 入力データをDtoで受け取る場合はこのアノテーションが必要 @FormParam("isbn") private String isbn; @FormParam("title") private String title; @FormParam("price") private int price; private java.util.Date createdAt; private java.util.Date updatedAt; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public java.util.Date getCreatedAt() { return createdAt; } public void setCreatedAt(java.util.Date createdAt) { this.createdAt = createdAt; } public java.util.Date getUpdatedAt() { return updatedAt; } public void setUpdatedAt(java.util.Date updatedAt) { this.updatedAt = updatedAt; } } サービスの作成 †値の受取、返却についての詳細は JAX-RSでの入力値の受取と返却 を参照 package example.service; import java.sql.Connection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import example.dto.BookDto; @Path("books") public class BooksResource extends ServiceBase { /** * 一覧取得<br /> * @return 結果(JSON文字列) */ @GET @Produces(MediaType.APPLICATION_JSON) public Map<String,Object> list() { List<Map<String,Object>> list = new ArrayList<Map<String,Object>>(); Connection con = null; try { con = getConnection(); list = selectQuery(con, "select * from books"); } catch (Exception e) { e.printStackTrace(); } finally { try { con.close(); } catch (Exception e) { } } // Mapを返却する場合 Map<String,Object> result = new HashMap<String,Object>(); result.put("list", list); // DTOを返却する事も可能 /* Map<String,Object> result = new HashMap<String,Object>(); List<BookDto> listDto = new ArrayList<BookDto>(); for (int i = 0; i < 10; i++) { BookDto book = new BookDto(); book.setid(i + 1); book.setIsbn("TEST" + i + 1); book.setTitle("タイトル" + i + 1); book.setPrice(1000 + i + 1); listDto.add(book); } result.put("list", listDto); */ // JSON文字列を組み立てて返却する場合(メソッドの戻り値を String に変更する事) //String jsonList = new ObjectMapper().writeValueAsString(list); //String result = "{ \"list\" : " + jsonList + " }"; return result; } /** * id指定検索 * @param id 対象データのid * @return 結果(JSON文字列) */ @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public BookDto show(@PathParam("id") Integer id) { // DTOを返却する場合(自動的にJSONに変換される) BookDto book = new BookDto(); book.setId(id); book.setIsbn("ISBN" + id); book.setTitle("タイトル" + id); book.setPrice(1000 + id); return book; } /** * 登録処理.<br /> * @param isbn ISBN * @param title タイトル * @param price 料金 * @return JSON */ @POST @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON , MediaType.APPLICATION_XML }) @Produces(MediaType.APPLICATION_JSON) public String create(@BeanParam BookDto book) { //public String create(@FormParam("isbn") String isbn, @FormParam("title") String title, @FormParam("price") String price) { String isbn = book.getIsbn(); String title = book.getTitle(); int price = book.getPrice(); System.out.println("isbn : " + isbn); System.out.println("title : " + title); System.out.println("price : " + price); // . // ここで登録処理を行う // . // JSON文字列を返却する場合 return "{ \"method\" : \"create\", \"result\" : \"success\" }"; } /** * 更新処理.<br /> * @param params 入力データ * @return 結果 */ @PUT @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON , MediaType.APPLICATION_XML }) @Produces(MediaType.APPLICATION_JSON) public Map<String,Object> update(@BeanParam BookDto book) { //public Map<String,Object> update(MultivaluedMap<String, String> params) { System.out.println("isbn : " + book.getIsbn()); System.out.println("title : " + book.getTitle()); System.out.println("price : " + book.getPrice()); // 入力データを表示 /* Iterator<Map.Entry<String,List<String>>> it = params.entrySet().iterator(); while (it.hasNext()) { Entry<String,List<String>> entry = it.next(); String key = entry.getKey(); List<String> vals = entry.getValue(); System.out.println(key + "=" + vals.get(0)); // ここでは値リストの1件目だけを表示(MultivaluedMap はキーに対して複数の値を持つ為) } */ // JSON文字列を組み立てて返却する事も可能(メソッドの戻り値を String にする事) //String result = ""; //result = "{ \"method\" : \"update\", \"result\" : \"success\" }"; // DTOを返却する事も可能(メソッドの戻り値を DTOのクラス名(Book)にする事) //BookDto result = new BookDto(); //result.setIsbn("test1"); //result.setTitle("test2"); // Mapを返却する事も可能(メソッドの戻り値を Map にする事) Map<String,Object> result = new HashMap<String,Object>(); result.put("method", "update"); result.put("result", "success"); result.put("var1" , "111"); result.put("var2" , "222"); return result; } /** * 削除処理.<br /> * @param id id * @return 結果 ※@Producesで指定した形式で返却される */ @DELETE @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public String delete(@PathParam("id") int id) { System.out.println("delete! id=" + id); return "{ \"method\" : \"delete\", \"result\" : \"success\" }"; } /** * 動作確認用.<br /> * @param args */ public static void main(String[] args) { // 一覧表示 System.out.println(new BooksResource().list()); // id指定検索 System.out.println(new BooksResource().show(1)); } } サービスの公開とサーバの開始 †サーバを作成し、公開および開始する 動作確認 †クライアント処理の作成 †index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script src="test.js"></script> </head> <body> <form action="ws/books" method="get" > <input type="submit" value="一覧検索" data-method="get" /> </form> <hr /> <form action="ws/books/1" method="get" > <input type="submit" value="一意検索" data-method="get" /> </form> <hr /> <form action="ws/books" method="post"> id : <input type="text" name="id" value="1" /><br /> title : <input type="text" name="title" value="test1" /><br /> <input type="submit" value="登録" data-method="post" /> </form> <hr /> <form action="ws/books"> id : <input type="text" name="id" value="1" /><br /> title : <input type="text" name="title" value="test2" /><br /> <input type="submit" value="更新" data-method="put" /> </form> <hr /> <form action="ws/books"> id : <input type="text" name="id" value="1" /><br /> <input type="submit" value="削除" data-method="delete" /> </form> <hr /> 結果 <div id="result" style="border:1px solid #000000;padding:10px;"></div> </body> </html> test.js $(document).ready(function(){ $("form").each(function(){ $(this).find("input[type=submit]").on("click", function(e){ $btn = $(this); $form = $(this).parents("form"); var method = $btn.data("method"); var url = $form.attr("action"); if (method == "delete") { url = url + "/" + $form.find("[name=id]").val(); } console.log("url :" + url); console.log("method:" + method); var data = {}; $form.find("[name]").each(function(){ var name = $(this).attr("name"); var val = $(this).val(); if ($(this).attr("type") == "checkbox") { val = ($(this).prop("checked") ? $(this).val() : data[name]) || ""; } else if ($(this).attr("type") == "radio"){ val = ($(this).prop("checked") ? $(this).val() : data[name]) || ""; } data[name] = val; }); console.log(data); $.ajax({ "url" : url ,"data" : data ,"type" : method ,"dataType" : "json" ,"success" : function(a1){ console.log("success!"); console.log(a1); var resultText = "url : " + url + "<br />" + "method : " + method + "<br />" + "result : " + JSON.stringify(a1); $("#result").html(resultText); } ,"error" : function(a1){ console.log("error!"); console.log(a1); } }); return false; }); }); }); http://localhost:8080/MyService/ にアクセスし、動作を確認する。 他、細々とした制御(validation等)を行いたい場合 †ResourceConfig などを継承した 独自クラスを作成し、web.xml に定義する。 |