こんな感じで作ってみた。
画面はしょっぱいですが、TODOアプリです。
pomの準備
とりあえずmavenでプロジェクトを作成したら、pomを編集します。お約束ですね。最近みないで打てるようになってきました。
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>okazuki</groupId><artifactId>todoapp</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>todoapp</name><url>http://maven.apache.org</url><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.2.5.RELEASE</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><version>9.4-1201-jdbc41</version></dependency></dependencies></project>
今回はPostgreSQLを使うのでそれのJDBCドライバも追加しています。
application.propertiesの編集
DBの接続関連の情報を追加してます。仮にtodoappという名前のDBにtodoappというユーザーでtodoappというパスワードでつなぐことにします。
spring.datasource.url=jdbc:postgresql://localhost:5432/todoapp spring.datasource.username=todoapp spring.datasource.password=todoapp spring.datasource.driver-class-name=org.postgresql.Driver spring.jpa.hibernate.ddl-auto=none spring.jpa.show-sql=true
DBの作成
PostgreSQLに適当にDBを作ります。上のDBを作ります。作成するテーブルは、こんな感じでさくっと。
CREATETABLE todoitems ( id serial NOTNULL, title text, done boolean, CONSTRAINT todoitems_pkey PRIMARY KEY (id) )
idのserialは、todoitem_id_seqというシーケンスからとってくるようになります。(GUIで作ったらそうなった)
Entityの作成
JPAのEntityを作成します。eclipseって自動生成機能ってあるのかな…わからなかったので手書きしました。
package okazuki.todoapp.entities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.SequenceGenerator; import javax.persistence.Table; @Entity@Table(name = "todoitems") publicclass TodoItem { @Id@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "todoitems_id_seq") @SequenceGenerator(name = "todoitems_id_seq", sequenceName = "todoitems_id_seq") private Long id; private String title; private Boolean done; public Long getId() { return id; } publicvoid setId(Long id) { this.id = id; } public String getTitle() { return title; } publicvoid setTitle(String title) { this.title = title; } public Boolean getDone() { return done; } publicvoid setDone(Boolean done) { this.done = done; } }
リポジトリの作成
今回はTODOが完了してるかしてないかというのを取得したいのでDoneプロパティで絞込できるようにメソッドをはやしたリポジトリを作成します。ここらへん便利ですね。Spring JPA
package okazuki.todoapp.repositories; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import okazuki.todoapp.entities.TodoItem; publicinterface TodoItemRepository extends JpaRepository<TodoItem, Long> { public List<TodoItem> findByDoneOrderByTitleAsc(boolean done); }
Controllerの作成
次にコントローラを作成します。初期表示、アイテムを完了にする、完了にしたものをもどす、新規に追加するの4つのメソッドを持っています。
package okazuki.todoapp.controllers; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import okazuki.todoapp.entities.TodoItem; import okazuki.todoapp.forms.TodoItemForm; import okazuki.todoapp.repositories.TodoItemRepository; @Controllerpublicclass HomeController { @Autowired TodoItemRepository repository; @RequestMappingpublic String index(@ModelAttribute TodoItemForm todoItemForm, @RequestParam("isDone") Optional<Boolean> isDone) { todoItemForm.setDone(isDone.isPresent() ? isDone.get() : false); todoItemForm.setTodoItems(this.repository.findByDoneOrderByTitleAsc(todoItemForm.isDone())); return"index"; } @RequestMapping(value = "/done", method = RequestMethod.POST) public String done(@RequestParam("id") long id) { TodoItem item = this.repository.findOne(id); item.setDone(true); this.repository.save(item); return"redirect:/?isDone=false"; } @RequestMapping(value = "/restore", method = RequestMethod.POST) public String restore(@RequestParam("id") long id) { TodoItem item = this.repository.findOne(id); item.setDone(false); this.repository.save(item); return"redirect:/?isDone=true"; } @RequestMapping(value = "/new", method = RequestMethod.POST) public String newItem(TodoItem item) { item.setDone(false); this.repository.save(item); return"redirect:/"; } }
TodoItemFormは、以下のようになっています。
package okazuki.todoapp.forms; import java.util.List; import okazuki.todoapp.entities.TodoItem; publicclass TodoItemForm { privateboolean isDone; private List<TodoItem> todoItems; public List<TodoItem> getTodoItems() { return todoItems; } publicvoid setTodoItems(List<TodoItem> todoItems) { this.todoItems = todoItems; } publicboolean isDone() { return isDone; } publicvoid setDone(boolean isDone) { this.isDone = isDone; } }
Viewの作成
最後にViewを作成します。見た目適当です。
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head><metacharset="UTF-8"/><title>Insert title here</title></head><body><h2>Todo app</h2><a th:unless="${todoItemForm.done}" th:href="@{/?isDone=true}">完了したアイテムの表示</a><a th:if="${todoItemForm.done}" th:href="@{/?isDone=false}">TODOの表示</a><hr /><h3>TODOの追加</h3><formmethod="post" th:action="@{/new}"><inputtype="text"name="title" /><inputtype="submit"value="追加" /></form><h3>TODOリスト</h3><table><thead><tr><th>Title</th><th></th></tr></thead><tbody><tr th:each="todoItem : ${todoItemForm.todoItems}"><td th:text="${todoItem.title}">xxx</td><td><form th:unless="${todoItemForm.done}"method="post" th:action="@{/done}" th:object="${todoItem}"><inputtype="hidden"name="id" th:value="*{id}" /><inputtype="submit"value="Done" /></form><form th:if="${todoItemForm.done}"method="post" th:action="@{/restore}" th:object="${todoItem}"><inputtype="hidden"name="id" th:value="*{id}" /><inputtype="submit"value="Restore" /></form></td></tr></tbody></table></body></html>
これくらいの量のコードでCRUD(Dは厳密にいうと無いけど)の画面ができるって割といい感じじゃないですかね。