Spring Boot 自分用メモ crossfish21

src/main/resources/config  messages_ja.properties

```
#バリデーションエラー時のメッセージ設定
javax.validation.constraints.NotEmpty.message={0}は入力必須です
javax.validation.constraints.NotNull.message={0}は入力必須です
javax.validation.constraints.Min.message={0}は{1}以上にしてください
javax.validation.constraints.Max.message={0}は{1}以下にしてください
#DecimalMax と DecimalMin は{2}が指定の数値になるので注意
javax.validation.constraints.DecimalMax.message={0}は{2}以下にしてください
javax.validation.constraints.DecimalMin.message={0}は{2}以下にしてください
javax.validation.constraints.Digits.message={0}の整数部と小数部の桁数が正しくありません
javax.validation.constraints.Email.message={0}はEメール形式にしてください
javax.validation.constraints.Future.message={0}は未来の日付で入力してください
javax.validation.constraints.Past.message={0}は過去の日付で入力してください
javax.validation.constraints.Pattern.message={0}は指定のパターンにマッチしません
#Size Length Range は{2}がminの値、{1}がmaxの値になるので注意
javax.validation.constraints.Size.message={0}の文字列長は{2}～{1}の範囲にしてください
org.hibernate.validator.constraints.Length.message={0}の文字列長は{2}～{1}の範囲にしてください
org.hibernate.validator.constraints.Range.message={0}は{2}～{1}の数値にしてください


#型変換に失敗した場合のエラーメッセージ設定
typeMismatch={0}を変換できません
typeMismatch.int={0}を数値に変換できません
typeMismatch.double={0}を数値に変換できません
typeMismatch.float={0}を数値に変換できません
typeMismatch.long={0}を数値に変換できません
typeMismatch.short={0}を数値に変換できません
typeMismatch.java.lang.Integer={0}を数値に変換できません
typeMismatch.java.lang.Double={0}を数値に変換できません
typeMismatch.java.lang.Float={0}を数値に変換できません
typeMismatch.java.lang.Long={0}を数値に変換できません
typeMismatch.java.lang.Short={0}を数値に変換できません
typeMismatch.java.math.BigDecimal={0}を数値に変換できません
typeMismatch.java.math.BigDecimal={0}を数値に変換できません
typeMismatch.java.math.BigInteger={0}を数値に変換できません
typeMismatch.java.time.LocalDate={0}を日付に変換できません
typeMismatch.java.time.LocalDateTime={0}をタイムスタンプに変換できません
typeMismatch.java.time.LocalTime={0}を時刻に変換できません
```

---
src/main/resources/config   messages.properties

```
 message.text01=MESSAGE_TEXT01
 message.text02=MESSAGE_TEXT02
 message.text03=MESSAGE_TEXT03
 message.text04=\u65E5\u672C\u8A9E\u30E1\u30C3\u30BB\u30FC\u30B8
```
---
src/main/resources/static/css  base.css

```
#wrapper{
	padding: 100px;
}

h3{
	margin-bottom: 25px;
}
```
---
src/main/resources/  application.properties

```
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/hsystem?currentSchema=schemah
spring.datasource.username=role21
spring.datasource.password=yellowAzarashi21
server.port=8091
spring.messages.basename=config/messages
spring.messages.cache-duration=-1
spring.messages.encoding=UTF-8
spring.thymeleaf.prefix=classpath:/page/
spring.thymeleaf.mode=HTML
spring.thymeleaf.cache=false
#spring.thymeleaf.cache=falseにすると、テンプレートの変更がアプリの再起動をしなくても反映される。ただし、本番では必ずtrueにすること
spring.thymeleaf.cache=false
spring.jpa.show-sql=true
```
---
src/main/resources/  hibernate.properties

```
hibernate.jdbc.lob.non_contextual_creation = true
```
---
com.crossfish.p1.config  MessageConfig.java

```
package com.crossfish.p1.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@SuppressWarnings("deprecation")
@Configuration
public class MessageConfig extends WebMvcConfigurerAdapter{
    @Autowired
    private MessageSource messageSource;

    @Bean
    public LocalValidatorFactoryBean validator()
    {
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
        localValidatorFactoryBean.setValidationMessageSource(messageSource);
        return localValidatorFactoryBean;
    }

    @Override
    public Validator getValidator()
    {
        return validator();
    }
}
```
---
com.crossfish.p1.controller  PageController.java

```
package com.crossfish.p1.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("page")
public class PageController {

    @RequestMapping("main_menu")
    public String mainMenu() {
    	return "/page/main_menu";
    	// メンメニューへ
    }

    @RequestMapping("outsourcing_worktime_register")
    public String outsourcingWorktime() {
    	return "/page/outsourcing_worktime_register";
    	// 外注者の稼働時間登録画面へ
    }
}
```
---
com.crossfish.p1.controller  RegisterController.java

```
package com.crossfish.p1.controller;

import java.io.InputStream;

import javax.servlet.http.HttpSession;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.multipart.MultipartFile;

import com.crossfish.p1.services.OutsourcingWorkingTimeServiceReadOnly;
import com.crossfish.p1.services.OutsourcingWorkingTimeServiceTransaction;

/*
 * @author Benten
 * DBへデータを登録する処理のコントローラー
 */
@Controller
@RequestMapping("register")
public class RegisterController {

  @Autowired
  HttpSession session;

  @Autowired
  private OutsourcingWorkingTimeServiceTransaction outsourcingWorkingTimeServiceTransaction;

  @Autowired
  private OutsourcingWorkingTimeServiceReadOnly outsourcingWorkingTimeServiceReadOnly;


  /*
   * @param multipartFile  アップされたファイル
   * @param model  Modelインスタンス
   * @return String 移動先のページのパス
   */
  @RequestMapping(value = "outsourcing_worktime_register_execute",method = RequestMethod.POST)
  public String outsourcingWorktimeRegisterExecute(
      @RequestParam("file01") MultipartFile multipartFile, Model model) {

    if (multipartFile.isEmpty()) {
      // ファイルがアップされていない場合
      model.addAttribute("errorMessage", "ファイルが指定されていません");
      return "/page/outsourcing_worktime_register";
    }

    Integer int1 = outsourcingWorkingTimeServiceReadOnly.getLastRivisionNumber("2018年度予算", "001");
    outsourcingWorkingTimeServiceTransaction.insert();


    try (InputStream input = multipartFile.getInputStream()) {

      try (XSSFWorkbook outsourcingWorktimeBook = (XSSFWorkbook) WorkbookFactory.create(input)) {

        XSSFSheet targetSheet = outsourcingWorktimeBook.getSheet("外注者の稼働時間");

        if (targetSheet == null) {  // シートが取得できなかった場合
          System.out.println("シートの取得に失敗");
        }

        String departmentString = ""; // 所属部署の18桁コード
        String versionString = ""; // バージョン名

        Row row03 = targetSheet.getRow(2); // 3行目を取得

        Cell cell01 = row03.getCell(5);

        if (cell01.getCellTypeEnum() != CellType.BLANK
            && cell01.getCellTypeEnum() == CellType.STRING) {
          // 一応文字列型であることと、空白でないことをチェック
          departmentString = cell01.getStringCellValue();
        }

        Row row05 = targetSheet.getRow(4);  // 3行目を取得
        cell01 = row05.getCell(2);
        versionString = cell01.getStringCellValue();

      } catch (Exception e) {
        e.printStackTrace();
      }

    } catch (Exception e) {
      e.printStackTrace();
    }

    return "/page/outsourcing_worktime_register";
  }
}
```
---
com.crossfish.p1.entities  OutsourcingWorkingTime.java

```
package com.crossfish.p1.entities;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Data
@IdClass(value = OutsourcingWorkingTimeKey.class)     // コメント
@Table(name = "outsourcing_working_time",schema="schemah")
public class OutsourcingWorkingTime  implements Serializable {

	private static final long serialVersionUID = 1L;

  @Id
  @Column(name = "version_name")
  private String versionName;

  @Id
  @Column(name = "department_code")
  private String departmentCode;

  @Id
  @Column(name = "revision")
  private Integer revision;

  @Id
  @Column(name = "staff_no")
  private String staffNo;

  @Column(name = "staff_name")
  private String staffName;

  @Column(name = "company_name")
  private String companyName;

  @Column(name = "basetime_4")
  private Double basetime4;

  @Column(name = "overtime_4")
  private Double overtime4;

  @Column(name = "businessPlace_4")
  private String businessPlace4;

  @Column(name = "basetime_5")
  private Double basetime5;

  @Column(name = "overtime_5")
  private Double overtime5;

  @Column(name = "businessPlace_5")
  private String businessPlace5;

  @Column(name = "basetime_6")
  private Double basetime6;

  @Column(name = "overtime_6")
  private Double overtime6;

  @Column(name = "businessPlace_6")
  private String businessPlace6;

  @Column(name = "basetime_7")
  private Double basetime7;

  @Column(name = "overtime_7")
  private Double overtime7;

  @Column(name = "businessPlace_7")
  private String businessPlace7;

  @Column(name = "basetime_8")
  private Double basetime8;

  @Column(name = "overtime_8")
  private Double overtime8;

  @Column(name = "businessPlace_8")
  private String businessPlace8;

  @Column(name = "basetime_9")
  private Double basetime9;

  @Column(name = "overtime_9")
  private Double overtime9;

  @Column(name = "businessPlace_9")
  private String businessPlace9;

  @Column(name = "basetime_10")
  private Double basetime10;

  @Column(name = "overtime_10")
  private Double overtime10;

  @Column(name = "businessPlace_10")
  private String businessPlace10;

  @Column(name = "basetime_11")
  private Double basetime11;

  @Column(name = "overtime_11")
  private Double overtime11;

  @Column(name = "businessPlace_11")
  private String businessPlace11;

  @Column(name = "basetime_12")
  private Double basetime12;

  @Column(name = "overtime_12")
  private Double overtime12;

  @Column(name = "businessPlace_12")
  private String businessPlace12;

  @Column(name = "basetime_1")
  private Double basetime1;

  @Column(name = "overtime_1")
  private Double overtime1;

  @Column(name = "businessPlace_1")
  private String businessPlace1;

  @Column(name = "basetime_2")
  private Double basetime2;

  @Column(name = "overtime_2")
  private Double overtime2;

  @Column(name = "businessPlace_2")
  private String businessPlace2;

  @Column(name = "basetime_3")
  private Double basetime3;

  @Column(name = "overtime_3")
  private Double overtime3;

  @Column(name = "businessPlace_3")
  private String businessPlace3;
}

```
---
com.crossfish.p1.entities  OutsourcingWorkingTimeKey

```
package com.crossfish.p1.entities;

import java.io.Serializable;

import lombok.Data;

@Data
public class OutsourcingWorkingTimeKey implements Serializable {
// OutsourcingWorkingTimeエンティティの複合主キーを設定するクラス

  private String versionName;
  private String departmentCode;
  private Integer revision;
  private String staffNo;
}
```
---
com.crossfish.p1.repositories    OutsourcingWorkingTimeRepository

```
package com.crossfish.p1.repositories;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.crossfish.p1.entities.OutsourcingWorkingTime;
import com.crossfish.p1.entities.OutsourcingWorkingTimeKey;

public interface OutsourcingWorkingTimeRepository
    extends JpaRepository<OutsourcingWorkingTime, OutsourcingWorkingTimeKey> {

// 最終のリビジョン番号を取得する
  @Query(value = "SELECT DISTINCT revision FROM outsourcing_working_time o WHERE version_name=:versionName AND "
		  + "department_code=:departmentCode ORDER BY revision DESC LIMIT 1", nativeQuery = true)
// 先頭にvalue = を、末尾に, nativeQuery = trueをつける。valueはObject型になる。メソッドの戻り値として使える
// outsourcing_working_timeはテーブル名だが、Entityクラスの名前ではなくDBのテーブル名をそのまま記述することに注意
  public Integer getLastRivisionNumber(
		  @Param("versionName")String  versionName,
		  @Param("departmentCode")String  departmentCode);
}
```
---
com.crossfish.p1.services  OutsourcingWorkingTimeServiceReadOnly.java

```
package com.crossfish.p1.services;

public interface OutsourcingWorkingTimeServiceReadOnly{

    public Integer getLastRivisionNumber(String  versionName,String  departmentCode);

}
```
---
com.crossfish.p1.services  OutsourcingWorkingTimeServiceReadOnlyImple

```
package com.crossfish.p1.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.crossfish.p1.repositories.OutsourcingWorkingTimeRepository;

@Service
@Transactional(readOnly = true)
public class OutsourcingWorkingTimeServiceReadOnlyImple implements OutsourcingWorkingTimeServiceReadOnly {

    @Autowired
    private OutsourcingWorkingTimeRepository outsourcingWorkingTimeRepository;


    @Override
    public Integer getLastRivisionNumber(String  versionName,String  departmentCode) {

    	Integer rivisionList = outsourcingWorkingTimeRepository.getLastRivisionNumber
    			(versionName, departmentCode);

    	return rivisionList;
    }
}
```
---
com.crossfish.p1.services  OutsourcingWorkingTimeServiceTransaction

```
package com.crossfish.p1.services;

public interface OutsourcingWorkingTimeServiceTransaction{

    public void insert();
}
```
---
com.crossfish.p1.services  OutsourcingWorkingTimeServiceTransactionImple

```
package com.crossfish.p1.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.crossfish.p1.entities.OutsourcingWorkingTime;
import com.crossfish.p1.repositories.OutsourcingWorkingTimeRepository;

@Service
@Transactional(readOnly = false)
public class OutsourcingWorkingTimeServiceTransactionImple implements OutsourcingWorkingTimeServiceTransaction {

    @Autowired
    private OutsourcingWorkingTimeRepository outsourcingWorkingTimeRepository;

    @Override
    public void insert() {
      OutsourcingWorkingTime outsourcingWorkingTime = new OutsourcingWorkingTime();
      outsourcingWorkingTime.setVersionName("2018年度予算");
      outsourcingWorkingTime.setDepartmentCode("002");
      outsourcingWorkingTime.setRevision(4);
      outsourcingWorkingTime.setStaffNo("7Y4001005");
      outsourcingWorkingTime.setStaffName("高橋朝子");
      outsourcingWorkingTime.setCompanyName("会社222");
      outsourcingWorkingTime.setBasetime4(156.23);
      outsourcingWorkingTime.setOvertime4(200.582);
      outsourcingWorkingTime.setBusinessPlace4("事業所100");
      outsourcingWorkingTime = outsourcingWorkingTimeRepository.saveAndFlush(outsourcingWorkingTime);
    }
}
```

---
STSのコード

```
package p1.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import p1.repository.R02;
import p1.repository.entity.Table01;
import p1.repository.entity.Table01.Sex;
import p1.service.S01;
import p1.service.S02;

@Controller
@RequestMapping("c01")
public class C01 {
	
	@Autowired
	private S01 s01;  // S01はサービスクラス
	
    @Autowired
    private S02 s02;
    
    @Autowired
    private R02 r02;  // R02はリポジトリ
    
	
    @RequestMapping("01")  // リポジトリを使わないで、テーブルのデータ一覧を取得
    public String list01(Model model) {
    	model.addAttribute("table01Data", s01.getTable01List());
    	return "01/list01";
    }
    
    @RequestMapping("02")  // リポジトリを使って、テーブルのデータ一覧を取得
    public String list02(Model model) {
    	model.addAttribute("table01Data", s02.getTable01List());
    	return "01/list01";
    }
    
    @RequestMapping("03/{id}")  // id指定で、1つのデータを取得。存在しないidを指定するとエラーになる
    public String getOne(Model model,@PathVariable Integer id) {
    	model.addAttribute("table01Data", s02.getOne(id));
    	// getOne()は、サービスクラスに実装している
    	// id指定で、データを参照するEntityをリターンする。該当するデータが存在しない場合はエラー発生
    	return "01/list01";
    }
    
    @RequestMapping("04/{id}")  // id指定で、1つのデータを取得。存在しないidを指定するとエラーになる
    public String findById(Model model,@PathVariable Integer id) {
    	
    	Optional<Table01> table01 = s02.findById(id);
    	// findById()は、サービスクラスに実装している
    	// id指定で、Entityをリターンする。該当するデータが存在しない場合はnullが返るのでOptionalにする
    	
    	if(!table01.equals(Optional.empty())) {  // データが存在する場合。.equals(Optional.empty())にすることに注意
            model.addAttribute("table01Data", table01.get());  // .get()にすることに注意
            return "01/list01";
    	}else {
            return "error/error01";
    	}
    }
    
    @RequestMapping("05/{id1}/{id2}/{id3}")  // 3つのid指定で、データを取得
    // findAllById(List<Integer>) は、Postgresではエラーになってしまうらしい
    public String multiId(Model model, @PathVariable Integer id1, @PathVariable Integer id2, @PathVariable Integer id3) {
    	
    	List<Integer> idList = new ArrayList<>();
    	idList.add(id1);
    	idList.add(id2);
    	idList.add(id3);
    	model.addAttribute("table01Data", r02.findAllById(idList));
    	// findAllById()は、リポジトリに存在している
    	return "01/list01";
    }
    
    @RequestMapping("06/{id1}/{id2}")  // idがid1～id2の範囲のデータを取得
    public String betweenId(Model model, @PathVariable Integer id1, @PathVariable Integer id2) {
    	model.addAttribute("table01Data", r02.findByIdBetween(id1,id2));
    	return "01/list01";
    }
    
    @RequestMapping("07/{firstName}")  // firstNameで検索(完全一致)
    public String firstName(Model model, @PathVariable String firstName) {
    	model.addAttribute("table01Data", r02.findByFirstName(firstName));
    	return "01/list01";
    }
    
    @RequestMapping("08/{str}")  // firstNameにstrが含まれるデータを検索(firstName like %str% になる)
    public String findByFirstNameContains(Model model, @PathVariable String str) {
    	model.addAttribute("table01Data", r02.findByFirstNameContains(str));
    	return "01/list01";
    }
    
    @RequestMapping("17/{str}")  // firstNameにstrが含まれないデータを検索(firstName not like %str% になる)
    public String findByFirstNameNotContains(Model model, @PathVariable String str) {
    	model.addAttribute("table01Data", r02.findByFirstNameNotContains(str));
    	return "01/list01";
    }
    
    @RequestMapping("33/{str}")  // firstNameがstrで始まるデータを検索(firstName like str% になる)
    public String findByFirstNameStartsWith(Model model, @PathVariable String str) {
    	model.addAttribute("table01Data", r02.findByFirstNameStartsWith(str));
    	return "01/list01";
    }
    
    @RequestMapping("34/{str}")  // firstNameがstrで終わるデータを検索(firstName like %str になる)
    public String findByFirstNameEndsWith(Model model, @PathVariable String str) {
    	model.addAttribute("table01Data", r02.findByFirstNameEndsWith(str));
    	return "01/list01";
    }
    
    @RequestMapping("09/{id}/{firstName}")  // idとfirstNameのAND条件で検索
    public String findByIdAndFirstName(Model model, @PathVariable Integer id, @PathVariable String firstName) {
    	model.addAttribute("table01Data", r02.findByIdAndFirstName(id,firstName));
    	return "01/list01";
    }
    
    @RequestMapping("10/{id}/{firstName}")  // idとfirstNameのOR条件で検索
    public String findByIdOrFirstName(Model model, @PathVariable Integer id, @PathVariable String firstName) {
    	model.addAttribute("table01Data", r02.findByIdOrFirstName(id,firstName));
    	return "01/list01";
    }
    
    @RequestMapping("11/{id}")  // idが～未満の条件で検索
    public String findByIdLessThan(Model model, @PathVariable Integer id) {
    	model.addAttribute("table01Data", r02.findByIdLessThan(id));
    	return "01/list01";
    }
    
    @RequestMapping("12/{id}")  // idが～より大きいの条件で検索
    public String findByIdGreaterThan(Model model, @PathVariable Integer id) {
    	model.addAttribute("table01Data", r02.findByIdGreaterThan(id));
    	return "01/list01";
    }
    
    @RequestMapping("13")  // mailAddressがnullのデータを検索
    public String findByMailAddressIsNull(Model model) {
    	model.addAttribute("table01Data", r02.findByMailAddressIsNull());
    	return "01/list01";
    }
    
    @RequestMapping("14")  // mailAddressがnullではないデータを検索
    public String findByMailAddressNotNull(Model model) {
    	model.addAttribute("table01Data", r02.findByMailAddressNotNull());
    	return "01/list01";
    }
    
    @RequestMapping("15/{id1}/{id2}")  // idがid1～id2の範囲のデータを取得し、id昇順で並べ替え
    public String findByIdBetweenOrderByIdAsc(Model model, @PathVariable Integer id1, @PathVariable Integer id2) {
    	model.addAttribute("table01Data", r02.findByIdBetweenOrderByIdAsc(id1,id2));
    	return "01/list01";
    }
    
    @RequestMapping("16/{id1}/{id2}")  // idがid1～id2の範囲のデータを取得し、id降順で並べ替え
    public String findByIdBetweenOrderByIdDesc(Model model, @PathVariable Integer id1, @PathVariable Integer id2) {
    	model.addAttribute("table01Data", r02.findByIdBetweenOrderByIdDesc(id1,id2));
    	return "01/list01";
    }
    
    @RequestMapping("18/{id1}/{id2}/{id3}")  // idがid1,id2,id3のいずれかであるデータを検索(in (id1,id2,id3) になる)
    public String findByIdIn(Model model, @PathVariable Integer id1, @PathVariable Integer id2, @PathVariable Integer id3) {
    	List<Integer> idList = new ArrayList<>();
    	idList.add(id1);
    	idList.add(id2);
    	idList.add(id3);
    	model.addAttribute("table01Data", r02.findByIdIn(idList));
    	return "01/list01";
    }
    
    @RequestMapping("19/{id1}/{id2}/{id3}") // idがid1,id2,id3のいずれでもないデータを検索(not in (id1,id2,id3) になる)
    public String findByIdNotIn(Model model, @PathVariable Integer id1, @PathVariable Integer id2, @PathVariable Integer id3) {
    	List<Integer> idList = new ArrayList<>();
    	idList.add(id1);
    	idList.add(id2);
    	idList.add(id3);
    	model.addAttribute("table01Data", r02.findByIdNotIn(idList));
    	return "01/list01";
    }
    
    @RequestMapping("20/{str}")  // firstNameにstrが含まれるデータを検索。like %str%の形にする
    // firstName like str になるのだが、前後に%をつけないといけないので、like %str%にするならContainsのほうが簡単。前方一致・後方一致に使うべきだろう
    public String findByFirstNameLike1(Model model, @PathVariable String str) {
    	str = "%" + str + "%";
    	model.addAttribute("table01Data", r02.findByFirstNameLike(str));
    	return "01/list01";
    }
    
    @RequestMapping("21/{str}")  // firstNameにstrが含まれるデータを検索。前方一致
    public String findByFirstNameLike2(Model model, @PathVariable String str) {
    	str = str + "%";
    	model.addAttribute("table01Data", r02.findByFirstNameLike(str));
    	return "01/list01";
    }
    
    @RequestMapping("22/{str}")  // firstNameにstrが含まれるデータを検索。後方一致
    public String findByFirstNameLike3(Model model, @PathVariable String str) {
    	str = "%" + str;
    	model.addAttribute("table01Data", r02.findByFirstNameLike(str));
    	return "01/list01";
    }
    
    @RequestMapping("23")  // 総デー多数を取得する
    public String dataCount(Model model) {
    	model.addAttribute("dataCount", r02.count());
    	// R02 リポジトリに結びついているTable01の総データ数を取得
    	return "other/dataCount";
    }
    
    @RequestMapping("24/{id}")  // 指定のidのデータが存在するかを調べる
    public String dataExists(Model model, @PathVariable Integer id) {
    	
    	Table01 table01 = new Table01();
    	table01.setId(id);  // 検索条件のidを設定
    	
    	Example<Table01> example = Example.of(table01);
    	// Exampleは検索条件を設定するクラス?なのかな
    	model.addAttribute("result", r02.exists(example));
    	// 設定したidのデータが存在するかを取得する。戻り値はboolean
    	// ※リポジトリのインターフェイスにはexists()は無くても動作する
    	
    	return "other/result01";  // 結果を表示するページへ
    }
    
    @RequestMapping("25")  // Sexがmaleのデータの数を取得
    public String maleDataCount(Model model) {
    	
    	Table01 table01 = new Table01();
    	table01.setSex(Sex.male);  // 検索条件のsexを設定
    	
    	Example<Table01> example = Example.of(table01);
    	model.addAttribute("result", r02.count(example));
    	// データ数を取得する
    	return "other/result01";  // 結果を表示するページへ
    }
    
    @RequestMapping("26")  // Sexがmaleのデータを全取得
    public String maleData(Model model) {
    	
    	Table01 table01 = new Table01();
    	table01.setSex(Sex.male);  // 検索条件のsexを設定
    	
    	Example<Table01> example = Example.of(table01);
    	model.addAttribute("table01Data", r02.findAll(example));
    	// 検索条件に合うデータを全取得
    	return "01/list01";  // 結果を表示するページへ
    }
    
    @RequestMapping("27")  // Sexがmaleで、idが5のデータを全取得
    public String maleAnd5Data(Model model) {
    	
    	Table01 table01 = new Table01();
    	table01.setSex(Sex.male);  // 検索条件のsexを設定
    	table01.setId(5);  // 2番目の条件も設定できる
    	
    	Example<Table01> example = Example.of(table01);
    	model.addAttribute("table01Data", r02.findAll(example));
    	return "01/list01";  // 結果を表示するページへ
    }
    
    @RequestMapping("28")  // birthday昇順でソートする
    public String sort1(Model model) {
    	model.addAttribute("table01Data", r02.findAll(new Sort("birthday")));
    	// birthday昇順でソート
    	return "01/list01";  // 結果を表示するページへ
    }

    @RequestMapping("29")  // id降順でソートする
    public String sort2(Model model) {
    	model.addAttribute("table01Data", r02.findAll(new Sort(Sort.Direction.DESC,("id"))));
    	// id降順でソート
    	return "01/list01";  // 結果を表示するページへ
    }
    
    // ※ソートの条件指定はもっと複雑にできるので、詳しくは調べて
    
    
    
    @RequestMapping("30")  // データがDBに反映されるタイミング
    @Transactional(readOnly=false)
    public String updateTiming(Model model) {
    	List<Table01> list = r02.findAll();
    	list.get(0).setFirstName("ZZZ");   // Entiryの値を変更しただけでは、DBへは反映されない
    	r02.findAll();   // テーブルにアクセスするので、この時点でupdateが実行され、DBへ反映される
    	list.get(0).setLastName("XXX");
    	r02.flush();  // 明示的にupdateを実行。DBへ反映される
    	list.get(0).setFirstName("YYY");  // DBへ反映されていない変更は、トランザクションの終了時にDBへ反映される
    	
    	return "01/list01";  // 結果を表示するページへ
    	
    	//ということを本には書いてあるけど、実際にはトランザクションを実行するまではDBには反映されていないみたい。
    	//ただ信頼できそうなサイトでも同じ事を書いてあるので、実際は反映されているのかも
    }
    
    
    @RequestMapping("31")  // データの更新と挿入
    @Transactional(readOnly=false)
    public String updateAndInsert(Model model) {
    	Table01 table01 = new Table01();
    	table01.setId(10);  // 存在するデータのidを指定
    	table01.setFirstName("PPP");
    	table01.setLastName("lll");
    	r02.save(table01);  // Entityの変更を保存。DBへの反映はまだされない。既存のデータなので更新になる
    	
    	Table01 table01_2 = new Table01();
    	table01_2.setId(20);  // 存在しないデータのidを指定
    	table01_2.setFirstName("EEE");
    	table01_2.setLastName("eee");
    	table01_2 = r02.saveAndFlush(table01_2);  
    	// Entityの変更を保存し、即座にDBに反映。既存のデータではないので挿入になる。saveでも同じ役割になる(DB反映はされないが)
    	
    	model.addAttribute("table01Data", table01_2);
    	return "01/list01";  // 結果を表示するページへ
    }
    
    
    @RequestMapping("32")  // データの削除
    @Transactional(readOnly=false)
    public String delete(Model model) {
    	
    	Table01 table01 = new Table01();
    	table01.setId(10);  // 存在するデータのidを指定
    	r02.delete(table01);  // データを削除
    	
    	Table01 table01_2 = new Table01();
    	table01_2.setId(30);  // 存在しないデータのidを指定
    	table01_2.setFirstName("EEE");  
    	// 存在しないidのEntityでも、Not Nullのフィールドは値を設定しないとエラーになる
    	table01_2.setLastName("eee");
    	r02.delete(table01_2);
    	// 存在しないデータを削除しても、エラーにはならないが意味は無い
    	
    	Table01 table01_3 = new Table01();
    	Table01 table01_4 = new Table01();
    	Table01 table01_5 = new Table01();
    	table01_3.setId(2);  // 存在するデータのidを指定
    	table01_4.setId(3);  // 存在するデータのidを指定
    	table01_5.setId(4);  // 存在するデータのidを指定
    	List<Table01> list = new ArrayList<>();
    	list.add(table01_3);
    	list.add(table01_4);
    	list.add(table01_5);
    	r02.deleteInBatch(list);   // listのEntityを全て削除する
    	
    	r02.deleteAll();  // 全データを削除する
    	
    	r02.deleteAllInBatch();  // 全データを削除する
    	
    	// ※ "InBatch" が付くメソッドは、１つのクエリーですべての処理を実行するので、多数のEntityを処理する場合は効率がいい
    	// "InBatch" が付かないメソッドは、Entityごとにクエリを発行するので処理が遅くなることに注意
    	return "01/list01";
    }   
}



package p1.restControllers;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController  // 文字列等のデータ、JsonやXML等を返すWebAPI用のコントローラとして使う
public class RestController01 {
	
	@RequestMapping("rest01")
	public String aaa() {
		return "rest01にアクセスがありました";
		// http://localhost:8090/rest01  にアクセスすれば、"rest01にアクセスがありました"と表示される
	}
	
	@RequestMapping("rest02")
    public void bbb(HttpServletResponse res) throws IOException {
	// HttpServletResponseで、指定のhtmlファイルの内容をhtml形式で出力する。RestControllerはこのような使い方が多いか
        File file = new File("src/main/resources/files/aaa.html");
        res.setContentLength((int) file.length());
        res.setContentType(MediaType.TEXT_HTML_VALUE);
        // MediaType.TEXT_HTML_VALUE で、HTML形式を指定。他にもファイルタイプを指定する定数がたくさんある
        FileCopyUtils.copy(new FileInputStream(file), res.getOutputStream());
        // aaa.htmlの内容をコピーして、レスポンスとして出力する。結果としてaaa.htmlをViewとして表示させたのと同じことになる
    }
}




package p1.controller;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import p1.repository.ProductMasterRepository;
import p1.repository.SalesDataRepository;
import p1.repository.StaffMasterRepository;
import p1.repository.WorkingDataRepository;
import p1.repository.entity.Product_master;
import p1.repository.entity.Sales_data;
import p1.repository.entity.Staff_master;
import p1.repository.entity.Working_data;

@Controller
@RequestMapping("hsystem")
public class ControllerH {
	
	@Autowired
	private StaffMasterRepository staffMasterRepository;
	
	@Autowired
	private SalesDataRepository salesDataRepository;
	
	@Autowired
	private WorkingDataRepository workingDataRepository;
	
    @Autowired
    private ProductMasterRepository productMasterRepository;
    
    @RequestMapping("01/{id}")
    public String getById(Model model, @PathVariable Integer id) {
    	Optional<Staff_master> data = staffMasterRepository.findById(id);
    	if(!data.equals(Optional.empty())) {
    		model.addAttribute("staffMasterData", data.get());
    		return "hsystem/staffMasterList";
    	}else {
    		return "error/error01";
    	}
    }
    
    @RequestMapping("02/{id}")
    public String getById2(Model model, @PathVariable Integer id) {
    	Optional<Sales_data> data = salesDataRepository.findById(id);
    	if(!data.equals(Optional.empty())) {
    		model.addAttribute("salesDataData", data.get());
    		return "hsystem/salesDataList";
    	}else {
    		return "error/error01";
    	}
    }
    
    @RequestMapping("03/{id}")
    public String getById3(Model model, @PathVariable Integer id) {
    	Optional<Working_data> data = workingDataRepository.findById(id);
    	if(!data.equals(Optional.empty())) {
    		model.addAttribute("workingDataData", data.get());
    		return "hsystem/workingDataList";
    	}else {
    		return "error/error01";
    	}
    }
    
    @RequestMapping("04/{id}")
    public String getById4(Model model, @PathVariable Integer id) {
    	Optional<Product_master> data = productMasterRepository.findById(id);
    	if(!data.equals(Optional.empty())) {
    		model.addAttribute("productMasterData", data.get());
    		return "hsystem/productMasterList";
    	}else {
    		return "error/error01";
    	}
    }
    
    @RequestMapping("05/{id}")
    public String greaterId(Model model, @PathVariable Integer id) {
    	List<Sales_data> list = salesDataRepository.greaterId(id);
    	model.addAttribute("salesDataData", list);
    	return "hsystem/salesDataList";
    }
    
    @RequestMapping("06/{id1}/{id2}")
    public String betweenId(Model model, @PathVariable Integer id1, @PathVariable Integer id2) {
    	List<Sales_data> list = salesDataRepository.betweenId(id1,id2);
    	model.addAttribute("salesDataData", list);
    	return "hsystem/salesDataList";
    }
    
    @RequestMapping("07/{id1}/{id2}")
    @Transactional(readOnly=false)
    public String deleteBetweenId(Model model, @PathVariable Integer id1, @PathVariable Integer id2) {
    	int resultInt = salesDataRepository.deleteBetweenId(id1,id2);
    	System.out.println(resultInt);
    	List<Sales_data> list = salesDataRepository.findAll();
            model.addAttribute("salesDataData", list);
            return "hsystem/salesDataList";
    }
}



package p1.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import p1.repository.entity.Table01;

@Repository
public interface R02 extends JpaRepository<Table01, Integer> {
	
	public Optional<Table01> findById(Integer id);
	
	public List<Table01> findAllById(List<Integer> idList);  // 複数のidを指定してデータを取得
	// findAllById(List<Integer>) は、Postgresではエラーになってしまうらしい
	
	public List<Table01> findByIdBetween(Integer id1, Integer id2);   // idがid1～id2の範囲のデータを取得
	
	public List<Table01> findByFirstName(String firstName);   // firstNameで検索(完全一致)
	
	public List<Table01> findByFirstNameContains(String str);   // firstNameにstrが含まれるデータを検索(firstName like %str% になる)
	
	public List<Table01> findByFirstNameNotContains(String str);   // firstNameにstrが含まれないデータを検索(firstName not like %str% になる)
	
	public List<Table01> findByFirstNameStartsWith(String str);   // firstNameがstrで始まるデータを検索(firstName like str% になる)
	
	public List<Table01> findByFirstNameEndsWith(String str);   // firstNameがstrで終わるデータを検索(firstName like %str になる)
	
	public List<Table01> findByIdAndFirstName(Integer id,String firstName);   // idとfirstNameのAND条件で検索
	
	public List<Table01> findByIdOrFirstName(Integer id,String firstName);   // idとfirstNameのOR条件で検索
	
	public List<Table01> findByIdLessThan(Integer id);   // idが～未満の条件で検索
	
	public List<Table01> findByIdGreaterThan(Integer id);   // idが～より大きいの条件で検索
	
	public List<Table01> findByMailAddressIsNull();   // mailAddressがnullのデータを検索
	
	public List<Table01> findByMailAddressNotNull();   // mailAddressがnullではないデータを検索
	
	public List<Table01> findByIdBetweenOrderByIdAsc(Integer id1, Integer id2);   // idがid1～id2の範囲のデータを取得し、id昇順で並べ替え
	
	public List<Table01> findByIdBetweenOrderByIdDesc(Integer id1, Integer id2);   // idがid1～id2の範囲のデータを取得し、id降順で並べ替え
	
	public List<Table01> findByIdIn(List<Integer> idList);   // idがidListのいずれかであるデータを検索(in (id1,id2,id3) になる)
	
	public List<Table01> findByIdNotIn(List<Integer> idList);   // idがidListのいずれでもないデータを検索(not in (id1,id2,id3) になる)
	
	public List<Table01> findByFirstNameLike(String str);   
	// firstNameにstrが含まれるデータを検索(firstName like str になるのだが、前後に%をつけないといけないので、Contains,StartsWith,EndsWithのほうが簡単
}



package p1.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import p1.repository.entity.Sales_data;

public interface SalesDataRepository extends JpaRepository<Sales_data, Integer> {

	public Optional<Sales_data> findById(Integer id);
	
	@Query("SELECT s FROM Sales_data s WHERE s.id > :id")
	public List<Sales_data> greaterId(@Param("id")Integer id);
	
	@Query("SELECT s FROM Sales_data s WHERE s.id BETWEEN :id1 AND :id2")
	public List<Sales_data> betweenId(@Param("id1")Integer id1, @Param("id2")Integer id2);
	
	@Query("DELETE FROM Sales_data s WHERE s.id BETWEEN :id1 AND :id2")
	@Modifying  // 更新・削除の場合は@Modifyingが必要
	public int deleteBetweenId(@Param("id1")Integer id1, @Param("id2")Integer id2);
	
}

```