こんな感じで作ってみた。
画面はしょっぱいですが、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を作ります。作成するテーブルは、こんな感じでさくっと。
CREATE TABLE todoitems ( id serial NOT NULL, 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") public class 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; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Boolean getDone() { return done; } public void 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; public interface 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; @Controller public class HomeController { @Autowired TodoItemRepository repository; @RequestMapping public 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; public class TodoItemForm { private boolean isDone; private List<TodoItem> todoItems; public List<TodoItem> getTodoItems() { return todoItems; } public void setTodoItems(List<TodoItem> todoItems) { this.todoItems = todoItems; } public boolean isDone() { return isDone; } public void setDone(boolean isDone) { this.isDone = isDone; } }
Viewの作成
最後にViewを作成します。見た目適当です。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="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> <form method="post" th:action="@{/new}"> <input type="text" name="title" /> <input type="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}"> <input type="hidden" name="id" th:value="*{id}" /> <input type="submit" value="Done" /> </form> <form th:if="${todoItemForm.done}" method="post" th:action="@{/restore}" th:object="${todoItem}"> <input type="hidden" name="id" th:value="*{id}" /> <input type="submit" value="Restore" /> </form> </td> </tr> </tbody> </table> </body> </html>
これくらいの量のコードでCRUD(Dは厳密にいうと無いけど)の画面ができるって割といい感じじゃないですかね。