DAO Data Access Object
学習中自分なりの理解をコメントに残しました。
間違っている点がありましたら申し訳ございません。
動かす前提準備
PersonRepository.java
package com.example.sample1app.repositories;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.example.sample1app.Person;
@Repository
//このクラスがデータベースアクセスのためのものであることを示す
public interface PersonRepository extends JpaRepository<Person,Long> {
/*インターフェースがJpaRepository<Person,Long>クラスを継承しているということ
JpaRepository:新たなリポジトリを作るときのベースとなるもの
対象エンティティがPersonでプライマリーキーがLong型の値であることを示す*/
public Optional<Person>findById(Long name);
//findByIdによってIDを引数にPersonインスタンスを取り出す
//Optionalクラス:nullかもしれないクラスのこと(import java.util.Optional;により使用可能)
//nullだった場合もOptionalインスタンスが取り出される
}
SampleBootApp1Application.java
package com.example.sample1app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
@SpringBootApplication
@Controller
public class SampleBootApp1Application {
public static void main(String[] args) {
SpringApplication.run(SampleBootApp1Application.class, args);
}
}
Person.java
package com.example.sample1app;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@Entity
//エンティティクラスであることを示す、DB
//テーブルに保管されるデータをJava内でオブジェクトとして扱えるようにする
//データベースにアクセスするクラスは別で用意
@Table(name="people")
//テーブル名の設定、省略でクラス名が使用
public class Person {
//値を保管するメソッドはPrivate
@Id
//プライマリーキーとなるもの
@GeneratedValue(strategy = GenerationType.AUTO)
//プライマリーキーのフィールドに値を自動生成
//各カラム設定、テーブルのように名前を設定できる
//省略でフィールド名が使用
//length:最大文字数
//nullable:nullを許可するか
@Column
@NotNull
private long id;
@Column(length = 50,nullable = false)
@NotBlank
private String name;
@Column(length = 200,nullable = true)
@Email
private String mail;
@Column(nullable = true)
@Min(0)
@Max(200)
private Integer age;
@Column(nullable = true)
private String memo;
//Getter、SetterでPrivateにアクセス
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name){
this.name = name;
}
public String getMail() {
return mail;
}
public void setMail(String mail){
this.mail = mail;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
}
DBにアクセスするインターフェースを作成
PaersonDAO.java
package com.example.sample1app;
import java.io.Serializable;
import java.util.List;
//データベースにアクセスするインターフェース
public interface PersonDAO<T>extends Serializable {
public List<T> getAll();
//getAllというメソッドを宣言
public T findById(long id);
//IDを引数にエンティティを返す
public List<T> findByName(String name);
//nameを引数にエンティティを返す
}
インターフェースを実装するクラスを作成
PersonDAOPersonImpl.java
package com.example.sample1app;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import org.springframework.stereotype.Repository;
//データベースにアクセスするPersonDAOインターフェースを実装するクラス
@Repository
/* DAOはリポジトリではないが、クラスのインスタンスとしてBeanを
* 登録するアノテーションのため、これを利用してBeanをアプリに登録 */
public class PersonDAOPersonImpl implements PersonDAO<Person> {
@SuppressWarnings("unused")
private static final long seriaVersionUID = 1L;
//serialVersionUIDとは、インスタンスのバージョンのこと
//
@PersistenceContext
//EntityManagerのBeanを取得してフィールドに設定
private EntityManager entityManager;
//エンティティをデータベースに登録したり、
//削除したりするためのインタフェースを持つオブジェクト
public PersonDAOPersonImpl(){
super();
//継承元のコンストラクタを呼び出す
}
@Override
/*クラスを継承する時に、スーパークラスのメソッドを
サブクラスにおいて同じメソッド名で定義し直すこと。
メソッド名、メソッドの引数は同じ数、同じ順番*/
public List<Person> getAll(){
Query query = entityManager.createQuery("from Person");
//Queryクラスのオブジェクトを使用
/* entityManagerのcreateQueryメソッドによりクエリ作成
("from Person")はSelect*From Personと同じ意味*/
@SuppressWarnings("unchecked")
//list変数につけられたアノテーション
//uncheckedにすることでgetResultの戻り値List<Person>の総称型
//であるかの判定を行わない、ビルド時に警告が出ないようにしている
//なくてもよい
List<Person> list = query.getResultList();
/*作成されたクエリをgetResultList() で実行
Listインスタンスで取得*/
entityManager.close();
return list;
}
@Override
public Person findById(long id) {
return (Person)entityManager.createQuery("from Person where id =" + id).getSingleResult();
//.getSingleResult():クエリ実行後1つの値を返すということ
}
@SuppressWarnings("unchecked")
@Override
public List<Person> findByName(String name){
return (List<Person>)entityManager.createQuery("from Person where name = '" + name + "'").getResultList();
//nameは複数をかえすかもしれないのでList
}
}
Controllerクラスの実装
HelloController.java
package com.example.sample1app;
import java.util.List;
import java.util.Optional;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
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.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.example.sample1app.repositories.PersonRepository;
@Controller
public class HelloController {
@Autowired
PersonRepository repository;
/* アプリケーションに用意されているBeanオブジェクとの関連付けを行う
(SpringMVCにより自動的にインスタンスが生成されアプリケーション内で利用可能になったもの)
リポジトリの関連付け、今回はPersonRepositoryのインスタンス */
@Autowired
PersonDAOPersonImpl dao;
/* アプリが用意したインスタンスを利用、DAOが利用できるように
* PersonDAOPersonImplクラスに@Repositoryをつけたから、
* @Repositoryや@Controllerをつけたクラスはインスタンスが
* 自動生成されてアプリケーション内に保存されて、@Autowiredで呼び出せる*/
@RequestMapping("/")
public ModelAndView example(@ModelAttribute("formModel") Person Person,ModelAndView mav) {
mav.setViewName("index");
//HTMLファイル名
mav.addObject("title","JPA使用練習");
mav.addObject("msg","サンプルデータ");
List <Person> list = repository.findAll();
//findAll():JpaRepositoryに用意されたメソッド
//エンティティが自動的にすべて取り出される
mav.addObject("data",list);
return mav;
}
@RequestMapping(value="/",method=RequestMethod.POST)
@Transactional
public ModelAndView form(@ModelAttribute("formModel") @Validated Person Person,BindingResult result,ModelAndView mav) {
//@Validatedエンティティの値をチェック
//BindingResultでエラーチェックの結果を受け取る
ModelAndView res = null;
System.out.println(result.getFieldError());
if(!result.hasErrors()) {
//hasError:エラーがあればtrueを返すもの
repository.saveAndFlush(Person);
res = new ModelAndView("redirect:/");
}else{
mav.setViewName("index");
mav.addObject("title","JPA使用練習");
mav.addObject("msg","エラーです");
Iterable<Person>list = repository.findAll();
mav.addObject("datalist",list);
res = mav;
}
return res;
}
@PostConstruct
//コンストラクタ、インスタンスが生成された後に自動で呼び出されるもの
public void init() {
//ダミーデータ
Person p1 = new Person();
p1.setName("taro");
p1.setAge(39);
p1.setMail("taro@mail");
repository.saveAndFlush(p1);
Person p2 = new Person();
p2.setName("moka");
p2.setAge(26);
p2.setMail("moka@mail");
repository.saveAndFlush(p2);
Person p3 = new Person();
p3.setName("miki");
p3.setAge(23);
p3.setMail("miki@mail");
repository.saveAndFlush(p3);
}
//UPDATEメソッドの追加
@RequestMapping(value = "/edit/{id}",method = RequestMethod.GET)
public ModelAndView edit(@ModelAttribute Person Person,@PathVariable int id,ModelAndView mav) {
mav.setViewName("edit");
mav.addObject("title","edit Person");
Optional<Person>data=repository.findById((long)id);
//IDデータを検索、得られたエンティティをformModelに
mav.addObject("formModel",data.get());
return mav;
}
//フォームがPOST送信されupdateメソッドが呼び出される
@RequestMapping(value = "/edit",method = RequestMethod.POST)
@Transactional
public ModelAndView update(@ModelAttribute Person Person,ModelAndView mav) {
repository.saveAndFlush(Person);
return new ModelAndView("redirect:/");
}
//Deleteメソッドの追加
@RequestMapping(value = "/delete/{id}",method = RequestMethod.GET)
public ModelAndView delete(@PathVariable int id,ModelAndView mav) {
mav.setViewName("delete");
mav.addObject("title","delete Person");
mav.addObject("msg","削除しますか?");
Optional<Person>data=repository.findById((long)id);
//IDデータを検索、得られたエンティティをformModelに
mav.addObject("formModel",data.get());
return mav;
}
//フォームがPOST送信されdeleteメソッドが呼び出される
@RequestMapping(value = "/delete",method = RequestMethod.POST)
@Transactional
public ModelAndView remove(@RequestParam long id,ModelAndView mav) {
repository.deleteById(id);
return new ModelAndView("redirect:/");
}
// 検索メソッド
@RequestMapping(value="/find",method=RequestMethod.GET)
public ModelAndView example(ModelAndView mav) {
mav.setViewName("find");
//HTMLファイル名
mav.addObject("msg","検索画面");
Iterable <Person> list = dao.getAll();
//getAll():DAOクラスのメソッド
//DAOでDBにアクセスする処理をまとめているのでコントローラーからの処理は
//呼び出しだけでできる
mav.addObject("data",list);
return mav;
}
// 名前検索メソッド
@RequestMapping(value="/find",method=RequestMethod.POST)
public ModelAndView search(HttpServletRequest request,ModelAndView mav) {
mav.setViewName("find");
//HTMLファイル名
String param = request.getParameter("find_str");
if (param == "") {
mav = new ModelAndView("redirect:/find");
}else{
mav.addObject("title","Find Result");
mav.addObject("msg","「" + param + "」の検索結果" );
mav.addObject("value",param);
List<Person> list = dao.findByName(param);
mav.addObject("data",list);
}
return mav;
}
}
HelloController.java
//ID検索メソッド
@RequestMapping(value="/find",method=RequestMethod.POST)
public ModelAndView search(HttpServletRequest request,ModelAndView mav) {
mav.setViewName("find");
//HTMLファイル名
String param = request.getParameter("find_str");
//HTMLの検索バーfind_strの値をparamに取得
if (param == "") {
mav = new ModelAndView("redirect:/find");
//空なら同じページを返す
}else{
mav.addObject("title","FindResult");
mav.addObject("msg","「" + param + "」の検索結果" );
mav.addObject("value",param);
Person data = dao.findById(Integer.parseInt(param));
//検索値を引数にfindByIdを実行してdataに格納
Person [] list = new Person [] {data};
//dataの値をPerson配列に格納、出力
mav.addObject("data",list);
}
return mav;
}
Viewの作成
find.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>検索ページ</title>
<style>
html{
font-size: 16px;
font-family: sans-serif;
text-align: center;
background-color:#F2F2F2
}
body{
margin: 0,0,0,0;
}
h1{
color:#404040
}
p{
color:#668EA2
}
form{
padding-bottom: 15px;
margin:auto;
}
table{
margin: auto;
table-layout: fixed;
width: 400px;
border-color: #000000; border: 1px solid;
}
thead{
background-color:#668EA2;
color: #FFFFFF;
}
textarea{
height: 140px;
}
.err{
color: red;
}
.mb-3{
margin:auto;
width: 400px;
padding: 10px;
}
input[type="submit"]{
border: none;
font-weight: bold;
width: 130px;
height: 30px;
background-color:#3E525C;
color: #FFFFFF;
font-size: 1rem;
border-radius: 100px;
}
</style>
</head>
<body>
<h1 class="display-4 mb-4" th:text="${title}"></h1>
<p th:text="${msg}"></p>
<form method="post" action="/find">
<div class="input-group">
<input type="text" class="form-control me-1"
name="find_str" th:value="${value}"/>
<span class="input-group-btn">
<input type="submit" class="btn btn-primary px-4"
value="Click"/>
</span>
</div>
</form>
<table class="table">
<thead>
<tr><th>ID</th><th>Name</th><th>Mail</th><th>Age</th></tr>
</thead>
<tbody>
<tr th:each="item : ${data}">
<td th:text="${item.id}"></td>
<td th:text="${item.name}"></td>
<td th:text="${item.mail}"></td>
<td th:text="${item.age}"></td>
</tr>
</tbody>
</table>
</body>
</html>
初期画面
名前検索結果
ID検索結果
わからなかったところメモ
private static final long seriaVersionUID = 1L;
//serialVersionUIDとは、インスタンスのバージョンのこと
(https://qiita.com/taumax/items/1559439efe0607595465)
Beanとは
情報を一時的に預かってくれる仕組み
(https://qiita.com/s_hino/items/0e3234b8feefe2d14315)
EntityManager
エンティティをデータベースに登録したり、
削除したりするためのインタフェースを持つオブジェクト
List<T>
例)public List getAll();
データ型を決めずに宣言しているということ
(https://layerprogram.com/javagenerics/)
HttpServletRequest
フォームから値を受け取り処理を行う際@RequestParamと同じように使用できる
-> @RequestParamはHttpServletRequestのgetParameterを呼び出して
受け取る操作を自動的に行いその結果を引数として設定するものということ