現状のスキル感
- エンジニア歴2年程度
- Servlet, JSPを研修を通して理解できたレベル感。
- VB.NET, Pythonを多少かじった程度。
- SpringBootはほぼ初見。
きっかけ
- SpringBootを試しで使ってみたので、備忘録として掲載します。
- 制作日数2~3日程で質の担保はできませんので、ご了承下さい。
- 見た目や例外処理、バリデーションチェックなどはほぼ実装していません。
- 内容について誤りや改善点などあればご教授頂けると幸いです。
家計簿アプリのイメージスライド
- 家計簿アプリ
- CRUD処理を実装
- 表示の単位は一覧、年別、年別月別を用意
- 登録、更新、削除は一般的な内容
ソースコード
TOP画面のHTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/styles.css}" rel="stylesheet">
<title>家計簿</title>
</head>
<body>
<div th:replace="~{block/header::headerAccount}"></div>
<h1>家計簿一覧画面</h1>
<table class="table table-striped">
<tr>
<th>No</th>
<th>日時</th>
<th>種別</th>
<th>品名</th>
<th>金額</th>
</tr>
<tr th:each="account, accountStat : ${list}">
<td th:text="${accountStat.count}"></td>
<td th:text="${account.date}"></td>
<td th:if="${account.type == 1}">食費</td>
<td th:if="${account.type == 2}">趣味</td>
<td th:if="${account.type == 3}">教養</td>
<td th:text="${account.item}"></td>
<td th:text="${account.price} + '円'"></td>
</tr>
</table>
<table class="table table-striped">
<tr>
<th>合計金額</th>
<td th:text="${totalPrice} + '円'"></td>
<tr>
</table>
<script th:src="@{/js/bootstrap.bundle.min.js}"></script>
</body>
</html>
- トップページアクセス時にデータベースから家計簿情報を取得、表示。
- cssはbootstrapを使用しています。ヘッダーやテーブルの装飾など。
- thymeleafを利用してth:eachタグでリストの情報を表示している。
- No情報は画面表示時に1から採番しています。(DBはAUTO_INCREMENTの為。)
Controller
package com.example.demo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.demo.entity.Account;
import com.example.demo.service.AccountService;
/**
* @author takaaki
* コントローラークラス
*/
@Controller
@RequestMapping("/account")
public class AccountController {
private final AccountService service;
@Autowired
public AccountController(AccountService service) {
this.service = service;
}
// 全件表示を行う
@GetMapping
public String account(Model model) {
List<Account> list = service.findAll();
int totalPrice = service.getTotalPrice();
model.addAttribute("list", list);
model.addAttribute("totalPrice", totalPrice);
return "account/index";
}
// 新規登録画面へ遷移
@GetMapping("/insert")
public String goInsert() {
return "account/insert";
}
// 新規登録画面へ遷移
@PostMapping("/insert")
public String insert(Model model, Account account) {
account = service.insertAccount(account);
model.addAttribute("account", account);
return "account/insertComplete";
}
// 削除画面へ遷移
@GetMapping("/delete")
public String goDelete(Model model) {
List<Account> list = service.findAll();
int totalPrice = service.getTotalPrice();
model.addAttribute("list", list);
model.addAttribute("totalPrice", totalPrice);
return "account/delete";
}
// 削除確認画面へ遷移
@GetMapping("/deleteConfirm")
public String deleteConfirm(Model model, @RequestParam String id) {
Account account = service.findAcountById(id);
model.addAttribute("account", account);
return "account/deleteConfirm";
}
// 削除処理を行う
@PostMapping("/delete")
public String delete(Model model, @RequestParam String id) {
Account account = service.findAcountById(id);
model.addAttribute("account", account);
service.deleteAcountById(id);
return "account/deleteComplete";
}
// 更新画面へ遷移
@GetMapping("/update")
public String goUpdate(Model model) {
List<Account> list = service.findAll();
int totalPrice = service.getTotalPrice();
model.addAttribute("list", list);
model.addAttribute("totalPrice", totalPrice);
return "account/update";
}
// 更新入力画面へ遷移
@GetMapping("/updateInput")
public String updateInput(Model model, @RequestParam String id) {
Account account = service.findAcountById(id);
model.addAttribute("account", account);
return "account/updateInput";
}
// 更新確認画面へ遷移
@PostMapping("/updateConfirm")
public String updateConfirm(Model model, Account account) {
model.addAttribute("account", account);
return "account/updateConfirm";
}
// 更新処理を行う
@PostMapping("/update")
public String update(Model model, Account account) {
model.addAttribute("account", account);
service.updateAccount(account);
return "account/updateComplete";
}
// 年別集計画面へ遷移
@GetMapping("/findByYear")
public String goFindByYear() {
return "account/findByYear";
}
// 年別集計処理を行う
@PostMapping("/findByYear")
public String findByYear(Model model, @RequestParam String year) {
List<Account> list2 = service.findAccountByYear(year);
int totalPrice = service.getTotalPrice();
model.addAttribute("totalPrice", totalPrice);
model.addAttribute("year", year);
model.addAttribute("list", list2);
return "account/findByYear";
}
// 年別月別集計画面へ遷移
@GetMapping("/findByYearAndMonth")
public String goFindByYearAndMonth() {
return "account/findByYearAndMonth";
}
// 年別月別集計処理を行う
@PostMapping("/findByYearAndMonth")
public String findByYearAndMonth(Model model, @RequestParam String year, @RequestParam String month) {
List<Account> list2 = service.findAccountByYearAndMonth(year, month);
int totalPrice = service.getTotalPrice();
model.addAttribute("totalPrice", totalPrice);
model.addAttribute("year", year);
model.addAttribute("month", month);
model.addAttribute("list", list2);
return "account/findByYearAndMonth";
}
}
- 全ての画面への遷移を担当。
- DBにアクセスする為にサービスクラスに処理を投げている。
Service
package com.example.demo.service;
import java.util.Calendar;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.dao.AccountDAO;
import com.example.demo.entity.Account;
/**
* @author takaaki
* ロジック担当
*/
@Service
public class AccountService {
private final AccountDAO dao;
// 合計金額格納用
private int totalPrice;
@Autowired
public AccountService(AccountDAO dao) {
this.dao = dao;
}
// 全件検索
public List<Account> findAll() {
List<Account> list = dao.findAll();
totalPrice = 0;
for (Account account : list) {
totalPrice += account.getPrice();
}
return list;
}
// 新規登録
public Account insertAccount(Account account) {
dao.insertAccount(account);
return account;
}
// 1件検索
public Account findAcountById(String id) {
return dao.findAcountById(id);
}
// 1件削除
public void deleteAcountById(String id) {
dao.deleteAccountById(id);
}
// 1件更新
public void updateAccount(Account account) {
dao.updateAccount(account);
}
// 年別処理を行う
public List<Account> findAccountByYear(String year) {
String startDate = year + "-01-01";
String endDate = year + "-12-31";
List<Account> list = dao.findAccountByYear(startDate, endDate);
totalPrice = 0;
for (Account account : list) {
totalPrice += account.getPrice();
}
return list;
}
// 年別月別処理を行う
public List<Account> findAccountByYearAndMonth(String year, String month) {
int yearInt = Integer.parseInt(year);
int monthInt = Integer.parseInt(month);
String startDate = year + "-" + month + "-" + "01";
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, yearInt);
calendar.set(Calendar.MONTH, monthInt - 1);
int result = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
String endDate = year + "-" + month + "-" + result;
List<Account> list = dao.findAccountByYearAndMonth(startDate, endDate);
totalPrice = 0;
for (Account account : list) {
totalPrice += account.getPrice();
}
return list;
}
public int getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(int totalPrice) {
this.totalPrice = totalPrice;
}
}
- DAOクラスにアクセスしてCRUD処理を実行する。
- 年別処理は検索範囲を「入力した月-01-01」から「入力した月-12-31」に設定。
- 年別月別処理は検索範囲を動的に取得。末尾の日が年度によって異なる為。
Repository
package com.example.demo.dao;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.Account;
/**
* @author takaaki
* DAOクラス
*/
@Repository
public class AccountDAO {
private final JdbcTemplate jdbcTemplate;
@Autowired
public AccountDAO(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 全件検索処理
public List<Account> findAll() {
String sql = "SELECT id, date, type, item, price FROM account order by date, id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql);
List<Account> list = new ArrayList<>();
for (Map<String, Object> result : resultList) {
Account account = new Account();
account.setId((int) result.get("id"));
Date date = (Date) result.get("date");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String strDate = dateFormat.format(date);
account.setDate(strDate);
account.setType((int) result.get("type"));
account.setItem((String) result.get("item"));
account.setPrice((int) result.get("price"));
list.add(account);
}
return list;
}
// 新規登録処理
public void insertAccount(Account account) {
jdbcTemplate.update("insert into account (date, type, item, price) values (?,?,?,?)",
account.getDate(), account.getType(), account.getItem(), account.getPrice());
}
// ID検索処理
public Account findAcountById(String id) {
String sql = "SELECT id, date, type, item, price FROM account where id = ?";
Map<String, Object> result = jdbcTemplate.queryForMap(sql, id);
Account account = new Account();
account.setId((int) result.get("id"));
Date date = (Date) result.get("date");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String strDate = dateFormat.format(date);
account.setDate(strDate);
account.setType((int) result.get("type"));
account.setItem((String) result.get("item"));
account.setPrice((int) result.get("price"));
return account;
}
// 削除処理
public void deleteAccountById(String id) {
jdbcTemplate.update("delete from account where id = ?", id);
}
// 更新処理
public void updateAccount(Account account) {
jdbcTemplate.update("update account set date = ?, type = ?, item = ?, price = ? where id = ?",
account.getDate(), account.getType(), account.getItem(), account.getPrice(), account.getId());
}
// 年別検索処理
public List<Account> findAccountByYear(String startDate, String endDate) {
String sql = "SELECT id, date, type, item, price FROM account "
+ "where date between ? and ? order by date, id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql,
startDate, endDate);
List<Account> list = new ArrayList<>();
for (Map<String, Object> result : resultList) {
Account account = new Account();
account.setId((int) result.get("id"));
Date date = (Date) result.get("date");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String strDate = dateFormat.format(date);
account.setDate(strDate);
account.setType((int) result.get("type"));
account.setItem((String) result.get("item"));
account.setPrice((int) result.get("price"));
list.add(account);
}
return list;
}
// 年別月別検索処理
public List<Account> findAccountByYearAndMonth(String startDate, String endDate) {
String sql = "SELECT id, date, type, item, price FROM account "
+ "where date between ? and ? order by date, id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql,
startDate, endDate);
List<Account> list = new ArrayList<>();
for (Map<String, Object> result : resultList) {
Account account = new Account();
account.setId((int) result.get("id"));
Date date = (Date) result.get("date");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String strDate = dateFormat.format(date);
account.setDate(strDate);
account.setType((int) result.get("type"));
account.setItem((String) result.get("item"));
account.setPrice((int) result.get("price"));
list.add(account);
}
return list;
}
}
- 購入日をEntityがString型、DBはDate型で設計したのでキャスト処理が必要。
全部を書き出すには分量が多すぎるので、次回以降に処理の詳細を記載していこうと思います。