- 追加された行はこの色です。
- 削除された行はこの色です。
* Spring BootでWebAPI作成 [#vc607534]
#setlinebreak(on);
#contents
-- 関連
--- [[AWS EC2上で Spring Bootアプリ起動]]
--- [[Spring BootからDynamoDBに接続する]]
** Spring Tool Suite (STS) のダウンロード [#yc41b823]
https://spring.io/tools/sts
** STSの日本語化 [#ke7e8aa4]
http://mergedoc.osdn.jp/
本体は不要なのでプラグインだけ落として readme を見て features と plugins を突っ込む。
** テスト用DBの準備(MySQL) [#ied5d235]
*** DB、ユーザ作成 [#z189607e]
#code(myterm2 nolinenums){{
/* DB作成 */
CREATE DATABASE example_db DEFAULT CHARACTER SET utf8;
/* ユーザ作成 */
CREATE USER 'example_user'@'%' IDENTIFIED BY 'example_pass';
/* 権限付与 */
GRANT ALL ON example_db.* TO 'example_user'@'%';
exit;
}}
*** 作成したユーザで接続し直してテーブル作成 [#g7c40d54]
#code(myterm2 nolinenums){{
mysql -h localhost -P 3306 -u example_user -p example_db
/* テーブル作成 */
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());
}}
** Spring Boot プロジェクトの作成 [#g8510bdd]
*** [新規] → [Spring スタータープロジェクト] [#s851f196]
#ref(create_project.png);
*** 変えたい所がされば変更して「次へ」 [#r9e730af]
#ref(create_project2.png);
*** O/Rマッパー、テンプレートエンジンなどを選択 [#z2b0db88]
※build.gradle の dependencies に反映されるだけなので、後からでも変更可能。
#ref(create_project3.png);
*** application.properties にDB接続情報を追加 [#r80432ad]
src/main/resources/application.properties
#mycode2(){{
spring.datasource.url=jdbc:mysql://localhost:3306/example_db
spring.datasource.username=example_user
spring.datasource.password=example_pass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.tomcat.maxActive=30
spring.datasource.tomcat.maxIdle=20
spring.datasource.tomcat.minIdle=10
spring.datasource.tomcat.initialSize=5
}}
※これらの値はOS環境変数などで上書き可能なので、ここではローカル用の設定をそのまま記述する。
※環境変数に定義する時の変数名は spring.datasource.url → SPRING_DATASOURCE_URL のように変換する。
*** テーブル定義の作成 [#h7bde0de]
しれっと lombok 使って setter 、getter を動的生成してるので、
lombok プラグインのインストールと、gradle.build の dependencies に compile('org.projectlombok:lombok:1.16.10') 等を追加する事。
src/main/java/com/example/demo/model/Book.java
#mycode2(){{
package com.example.demo.model;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@Data
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String isbn;
private String title;
private int price;
private int created_at;
private Date createdAt;
private Date updatedAt;
}
}}
*** Daoの作成 [#a73d64c0]
src/main/java/com/example/demo/dao/BookDao.java
#mycode2(){{
package com.example.demo.dao;
import java.util.List;
import com.example.demo.model.Book;
public interface BookDao {
List<Book> selectAll();
Book select(int id);
void insert(Book book);
int update(Book book);
int delete(int id);
}
}}
*** MyBatis用のDao定義を作成 [#q3a20b98]
src/main/resources/dao/BookDao.xml
#myhtml2(){{
<!--?xml version="1.0" encoding="UTF-8" ?-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.dao.BookDao">
<select id="selectAll" resultType="com.example.demo.model.Book">
select * from books
</select>
<select id="select" parameterType="int" resultType="com.example.demo.model.Book">
select * from books where id = #{id}
</select>
<insert id="insert" parameterType="com.example.demo.model.Book">
insert into books (isbn,title,price,created_at,updated_at)
values(#{isbn},#{title},#{price},now(),now())
<!-- MySQLの場合、以下で自動採番(AUTO_INCREMENT)されたIDをオブジェクトにセットできる -->
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@IDENTITY <!-- SELECT LAST_INSERT_ID() でも可 -->
</selectKey>
</insert>
<update id="update" parameterType="com.example.demo.model.Book">
update books set isbn = #{isbn}, title = #{title}, price = #{price}, updated_at = now() where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from books where id = #{id}
</delete>
</mapper>
}}
*** mybatis-config.xml を追加 [#iffc50a1]
参照: http://www.mybatis.org/mybatis-3/ja/configuration.html#settings
src/main/resources/mybatis-config.xml
#mycode2(){{
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
}}
*** MyBatis用のConfigクラスを追加 [#p4d765d3]
src/main/java/com/example/demo/config/SqlMappingConfig.java
#mycode2(){{
package com.example.demo.config;
import java.io.IOException;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
@Configuration
@MapperScan("com.example.demo.dao")
public class SqlMappingConfig {
/**
* SqlSessionFactoryBean格納クラス。
* @return SqlSessionFactoryBean。
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
ResourcePatternResolver resolver =
ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader());
factory.setConfigLocation(resolver.getResource("classpath:mybatis-config.xml"));
factory.setMapperLocations(resolver.getResources("classpath:dao/**/*.xml"));
return factory;
}
}
}}
*** コントローラの作成 [#m95af82c]
src/main/java/com/example/demo/controller/BookController.java
#mycode2(){{
package com.example.demo.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.dao.BookDao;
import com.example.demo.model.Book;
@RestController
@RequestMapping("/api")
public class BookController {
@Autowired
private BookDao bookMapper;
@RequestMapping(value = "/books", method = RequestMethod.GET)
public List<Book> selectAll() {
List<Book> books = bookMapper.selectAll();
for (Book book : books) {
System.out.println(book.toString());
}
return books;
}
@RequestMapping(value = "/book/{id}", method = RequestMethod.GET)
public Book select(@PathVariable("id") Integer id) {
Book books = bookMapper.select(id);
return books;
}
@RequestMapping(value = "/book", method = RequestMethod.POST)
public Book create(@ModelAttribute Book book) {
bookMapper.insert(book);
return book;
}
@RequestMapping(value = "/book/{id}", method = RequestMethod.PUT)
public Map<String,String> update(@PathVariable("id") Integer id, @ModelAttribute Book book) {
Map<String,String> results = new HashMap<String, String>();
book.setId(id);
int count = bookMapper.update(book);
results.put("result", count == 1 ? "OK" : "NG");
return results;
}
@RequestMapping(value = "/book/{id}", method = RequestMethod.DELETE)
public Map<String,String> delete(@PathVariable("id") Integer id) {
Map<String,String> results = new HashMap<String, String>();
int count = bookMapper.delete(id);
results.put("result", count == 1 ? "OK" : "NG");
return results;
}
}
}}
** ビルド [#v6a471cd]
#ref(gradle_build.png);
** 起動 [#z200e678]
#ref(gradle_boot_run.png);
** 動作確認 [#eef8c85a]
#myterm2(){{
# 一覧検索
curl -v http://localhost:8080/api/books
# 一意検索
curl -v http://localhost:8080/api/book/1
# 登録
curl -v -XPOST --data "title=TEST&isbn=XXXXX&price=1234" http://localhost:8080/api/book/
# 更新
curl -v -XPUT --data "title=UPDATE&isbn=YYYY&price=5678" http://localhost:8080/api/book/4
# 削除
curl -v -XDELETE http://localhost:8080/api/book/4
}}
** おまけ(動作確認用のページ追加) [#l9e08707]
src/main/java/com/example/demo/config/StaticResourceConfig.java
#mycode2(){{
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class StaticResourceConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
}}
src/main/resources/static/book.html
#myhtml2(){{
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
#booklist {
border-collapse: collapse;
}
#booklist th, #booklist td {
padding: 4px 10px;
border: 1px solid #333;
}
#booklist th {
background: #ccc;
}
#booklist td {
cursor: pointer;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="/js/ajax.js"></script>
</head>
<body>
<form action="/api/books" method="get" >
<input type="submit" value="一覧検索" data-method="get" data-callback="resultList" />
<table id="booklist">
<thead>
<tr>
<th>id</th>
<th>isbn</th>
<th>title</th>
<th>price</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</form>
<hr />
<form action="/api/book" method="get" >
id : <input type="text" name=id value="1" /><br />
<input type="submit" value="一意検索" data-method="get" />
</form>
<hr />
<form action="/api/book" method="post">
isbn : <input type="text" name="isbn" value="123-4567890123" /><br />
title : <input type="text" name="title" value="test1" /><br />
price : <input type="number" name="price" value="1000" /><br />
<input type="submit" value="登録" data-method="post" />
</form>
<hr />
<form action="/api/book">
id : <input type="text" name="id" value="1" /><br />
isbn : <input type="text" name="isbn" value="123-4567890123" /><br />
title : <input type="text" name="title" value="test2" /><br />
price : <input type="number" name="price" value="1000" /><br />
<input type="submit" value="更新" data-method="put" />
</form>
<hr />
<form action="/api/book">
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>
<script>
// 一覧検索結果の描画
window.resultList = function(result){
console.log("callback!");
$("#booklist tbody").empty();
if (result && result.length > 0) {
for (var i in result) {
var rowHTml = "<tr>"
+ "<td>"+result[i]["id"]+"</td>"
+ "<td>"+result[i]["isbn"]+"</td>"
+ "<td>"+result[i]["title"]+"</td>"
+ "<td>"+result[i]["price"]+"</td>"
+ "</tr>";
$("#booklist tbody").append(rowHTml);
}
}
};
jQuery(function($){
// 一覧の任意の行を選択時
$(document).on("click", "#booklist tbody td", function(){
var id = $(this).parents("tr").find("td").eq(0).text();
var isbn = $(this).parents("tr").find("td").eq(1).text();
var title = $(this).parents("tr").find("td").eq(2).text();
var price = $(this).parents("tr").find("td").eq(3).text();
$(document).find("[name=id]").val(id);
$(document).find("[name=isbn]").val(isbn);
$(document).find("[name=title]").val(title);
$(document).find("[name=price]").val(price);
});
});
</script>
</body>
</html>
}}
src/main/resources/static/js/ajax.js
#mycode2(){{
jQuery(function($){
$("form").each(function(){
$(this).find("input[type=submit]").on("click", function(e){
$btn = $(this);
$form = $(this).parents("form");
var method = $btn.data("method").toLowerCase();
var url = $form.attr("action");
var callbackFunc = null;
var callback = $btn.data("callback");
if (callback && window[callback]) {
callbackFunc = window[callback];
}
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;
});
if ((method == "put" || method == "delete" || method == "get") && data["id"]) {
url = url + "/" + data["id"];
delete(data["id"]);
}
console.log("url :" + url);
console.log("method:" + method);
console.log(data);
$.ajax({
"url" : url
,"data" : data
,"type" : method
,"dataType" : "json"
,"success" : function(result){
console.log("success!");
console.log(result);
var resultText = "url : " + url + "<br />"
+ "method : " + method + "<br />"
+ "result : " + JSON.stringify(result);
$("#result").html(resultText);
if (callbackFunc){
callbackFunc(result);
}
}
,"error" : function(a1){
console.log("error!");
console.log(a1);
}
});
return false;
});
});
});
}}
以上のファイルを作成すれば http://localhost:8080/static/book.html から CRUD の確認ができる。