[SpringBoot2.4.3] uploadを任意の場所にファイルを保存

Controller.java
L new FileOutputStream(${path})で、保存場所を指定する

	@RequestMapping(value="upload", method=RequestMethod.POST)
    public void handle(
            HttpServletResponse response,
            @RequestParam MultipartFile file
            ) {
        if(file.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        try {
            BufferedInputStream in = new BufferedInputStream(file.getInputStream());
            BufferedOutputStream out = new BufferedOutputStream(
                    new FileOutputStream("./target/" + file.getOriginalFilename()));
        } catch (IOException e) {
            throw new RuntimeException("Error uploading file.", e);
        }
    }

なるほど、resourcesのstaticの中に置くこともできますね。

new FileOutputStream(“./src/main/resources/static/file/” + file.getOriginalFilename())

### filenameをreturn
res.getWriter().write(file.getOriginalFilename());

OK、後は基本機能としてはログインのみなんだよなー

[SpringBoot2.4.3] ファイルアップロードのcontroller

import javax.servlet.http.HttpServletResponse;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/contract")
public class ContractController {

	@GetMapping("input")
	public String contract() {
		return "contract/input";
	}
	
	@RequestMapping(value="upload", method=RequestMethod.POST)
    public void handle(
            HttpServletResponse response,
            @RequestParam MultipartFile file
            ) {
        if(file.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        try {
            BufferedInputStream in = new BufferedInputStream(file.getInputStream());
            BufferedOutputStream out = new BufferedOutputStream(
                    new FileOutputStream(file.getOriginalFilename()));
        } catch (IOException e) {
            throw new RuntimeException("Error uploading file.", e);
        }
    }
}

html

<form action="/contract/upload" method="post" enctype="multipart/form-data">
    <div class="form-group">
    <div class="custom-file">
      <input type="file" name="file" class="custom-file-input" id="inputFile">
    </div>
  </div>
  <br>
<button type="submit" class="btn btn-primary" id="upload" value="upload">登録</button>	
</form>

BufferedInputStreamとBufferedOutputStreamの使い方がイマイチよくわからんな。。

[SpringBoot2.4.3] ファイルアップロード機能を作る

まず、webpackで簡単にフロントを作成します

続いて、そのまま、src/main/resources/templates/contract/input.html に流し込みます。

<form action="/upload" enctype="multipart/form-data">
    <div class="form-group">
    <div class="custom-file">
      <input type="file" name="file" class="custom-file-input" id="inputFile">
      <!-- <label class="custom-file-label" for="inputFile"></label> -->
    </div>
  </div>
  <br>
<button type="button" class="btn btn-primary">登録</button>	
</form>

続いてControllerを作ります。適当にContractController.javaとします。

package com.example.demo;

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

@Controller
@RequestMapping("/contract")
public class ContractController {

	@GetMapping("input")
	public String contract() {
		return "contract/input";
	}
}

ここからfile保存を実装したい。。。

[SpringBoot2.4.3] Service

package com.example.demo;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Service;

@Service
public class MyDataService {
	
	@PersistenceContext
	private EntityManager entityManager;
	
	@SuppressWarnings("unchecked")
	public List<MyData> getAll(){
		return (List<MyData>) entityManager
				.createQuery("from MyData").getResultList();
	}
	
	public MyData get(int num) {
		return (MyData)entityManager
				.createQuery("from MyData where id = " + num)
				.getSingleResult();
	}
	
	public List<MyData> find(String fstr){
		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		CriteriaQuery<MyData> query = builder.createQuery(MyData.class);
		Root<MyData> root = query.from(MyData.class);
		query.select(root).where(builder.equal(root.get("name"), fstr));
		List<MyData> list = null;
		list = (List<MyData>) entityManager.createQuery(query).getResultList();
		return list;
	}
}

[SpringBoot2.4.3] エンティティの連携

複数のテーブルが関連して動かしたい場合は、「アソシエーション」という機能によりエンティティ同士を連携して処理する

@OneToOne,@OneToMany, @ManyToOne, @ManyToMany

package com.example.demo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name = "msgdata")
public class MsgData {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	@NotNull
	private long id;
	
	@Column
	private String title;
	
	@Column(nullable = false)
	@NotEmpty
	private String message;
	
	@ManyToOne
	private MyData mydata;
	
	public MsgData() {
		super();
		mydata = new MyData();
	}
	
	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 String getMessage() {
		return message;
	}
	
	public void setMessage(String message) {
		this.message = message;
	}
	
	public MyData getMyData() {
		return mydata;
	}
	
	public void setMydata(MyData mydata) {
		this.mydata = mydata;
	}
}

MsgDataRepository.java

package com.example.demo.repositories;

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

import com.example.demo.MsgData;

@Repository
public interface MsgDataRepository extends JpaRepository<MsgData, Long>{

}

MsgDataDao.java

package com.example.demo;

import java.io.Serializable;
import java.util.List;

public interface MsgDataDao<T> {
	
	public List<MsgData> getAll();
	public MsgData findById(long id);
}

MsgDataDaoImpl.java

package com.example.demo;

import java.util.List;
import javax.persistence.EntityManager;

import org.springframework.stereotype.Repository;

@SuppressWarnings("rawtypes")
@Repository
public class MsgDataDaoImpl implements MsgDataDao<MsgDataDao>{
	
	private EntityManager entityManager;
	
	public MsgDataDaoImpl() {
		super();
	}
	public MsgDataDaoImpl(EntityManager manager) {
		entityManager = manager;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<MsgData> getAll(){
		return entityManager
				.createQuery("from MsgData")
				.getResultList();
	}
	
	@Override
	public MsgData findById(long id) {
		return (MsgData)entityManager
				.createQuery("from MsgData where id = "
						+ id).getSingleResult();
	}

}

showMsgData.html

	<h1 th:text="${title}">MyMsg page</h1>
	<p th:text="${msg}"></p>
	<table>
		<form method="post" action="/msg" th:object="${formModel}">
		<input type="hidden" name="id" th:value="*{id}">
		<tr>
			<td><label for="title">タイトル</label></td>
			<td><input type="text" name="title" th:value="*{title}"></td>
		</tr>
		<tr>
			<td><label for="message">メッセージ</label></td>
			<td><textarea name="message" th:text="*{message}"></textarea></td>
		</tr>
		<tr>
			<td><label for="mydata">MYDATA_ID</label></td>
			<td><input type="text" name="mydata"></td>
		</tr>
		<tr>
			<td></td>
			<td><input type="submit"></td>
		</tr>
		</form>
	</table>
	<hr>
	<table>
		<tr><th>ID</th><th>名前</th><th>タイトル</th></tr>
		<tr th:each="obj : ${datalist}">
			<td th:text="${obj.id}"></td>
			<td th:text="${obj.mydata.name}"></td>
			<td th:text="${obj.title}"></td>
		</tr>
	</table>

MsgDataController.java

package com.example.demo;

import java.util.List;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.example.demo.repositories.MsgDataRepository;

@Controller
public class MsgDataController {

		@Autowired
		MsgDataRepository repository;
		
		@PersistenceContext
		EntityManager entityManager;
		
		MsgDataDaoImpl dao;
		
		@RequestMapping(value = "/msg", method=RequestMethod.GET)
		public ModelAndView msg(ModelAndView mav) {
			mav.setViewName("showMsgData");
			mav.addObject("title", "Sample");
			mav.addObject("msg","MsgDataのsampleです");
			MsgData msgdata = new MsgData();
			mav.addObject("formModel", msgdata);
			List<MsgData> list = (List<MsgData>)dao.getAll();
			mav.addObject("datalist", list);
			return mav;
		}
		
		@RequestMapping(value="/msg", method=RequestMethod.POST)
		public ModelAndView msgform(
				@Valid @ModelAttribute MsgData msgdata,
				Errors result,
				ModelAndView mav) {
			if(result.hasErrors()) {
				mav.setViewName("showMsgData");
				mav.addObject("title", "Sample [Error]");
				mav.addObject("msg", "値を再チェックして下さい");
				return mav;
			} else {
				repository.saveAndFlush(msgdata);
				return new ModelAndView("redirect:/msg");
			}
		}
		
		@PostConstruct
		public void init() {
			System.out.println("ok");
			dao = new MsgDataDaoImpl(entityManager);
		}
		
}

んん?あれ?

[SpringBoot2.4.3] Repository

Repository

@Repository
public interface MyDataRepository extends JpaRepository<MyData, Long> {
	
	@Query("SELECT d FROM MyData d ORDER BY d.name")
	List<MyData> findAllOrderByName();
// 省略

Controller

	@RequestMapping(value="/", method=RequestMethod.GET)
	public ModelAndView index(
			ModelAndView mav) {
		mav.setViewName("index");
		mav.addObject("msg","this is sample content.");
		Iterable<MyData> list = repository.findAllOrderByName();
		mav.addObject("datalist", list);
		return mav;
	}

よく出来てるなー
エンティティ自体にクエリを持たせるか、リポジトリに用意するか、どちらでもできる。

[SpringBoot2.4.3] JPQLを活用

SQLクエリーに似た簡易言語
本格的な検索処理はJPQLを理解する必要がある
1.EntityManagerの用意
2.Queryの作成
3.結果(List)の取得

html

	<table>
	<form action="/find" method="post">
		<tr>
			<td>FIND:</td>
			<td><input type="text" name="fstr" size="20" th:value="${value}"></td></td>
		</tr>
		<tr>
			<td></td>
			<td><input type="submit"></td>
		</tr>
	</form>
	</table>

controller

	@RequestMapping(value="/find", method=RequestMethod.GET)
	public ModelAndView find(ModelAndView mav) {
		mav.setViewName("find");
		mav.addObject("title", "Find Page");
		mav.addObject("msg","MyData sample");
		mav.addObject("value", "");
		Iterable<MyData> list = dao.getAll();
		mav.addObject("datalist", list);
		return mav;
	}
	@RequestMapping(value="/find", method=RequestMethod.POST)
	public ModelAndView search(HttpServletRequest request,
			ModelAndView mav) {
		mav.setViewName("find");
		String param = request.getParameter("fstr");
		if(param =="") {
			mav = new ModelAndView("redirect:/find");
		} else {
			mav.addObject("title","Find result");
			mav.addObject("msg","「" + param + "」の検索結果");
			mav.addObject("value", param);
			List<MyData> list = dao.find(param);
			mav.addObject("datalist", list);
		}
		return mav;
	}

MyDataDao

public interface MyDataDao <T> extends Serializable {
	public List<T> getAll();
	public T findById(long id);
	public List<T> findByName(String name);
	public List<T> find(String fstr);
}
	@Override
	public List<MyData> find(String fstr){
		List<MyData> list = null;
		String qstr = "from MyData where id = :fstr";
		Query query = entityManager.createQuery(qstr).setParameter("fstr", Long.parseLong(fstr));
		list = query.getResultList();
		return list;
	}

[SpringBoot2.4.3] DAOに検索メソッドを追加

MyDataDao.java

import java.io.Serializable;
import java.util.List;

public interface MyDataDao <T> extends Serializable {
	public List<T> getAll();
	public T findById(long id);
	public List<T> findByName(String name);
}

エンティティの追加
MyDataDaoImpl.java

	@Override
	public MyData findById(long id) {
		return (MyData)entityManager.createQuery("from MyData where id = " + id).getSingleResult();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<MyData> findByName(String name){
		return (List<MyData>)entityManager.createQuery("from MyData where name = " + name).getResultList();
	}

完璧に理解するにはもう少し時間がかかりそうやな

[SpringBoot2.4.3] DAO

データベースアクセスする際のオブジェクト
Data Access Object

Dao interface

package com.example.demo;

import java.io.Serializable;
import java.util.List;

public interface MyDataDao <T> extends Serializable {
	public List<T> getAll();
}

MyDataDaoImpl.java

package com.example.demo;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;

@Repository
public class MyDataDaoImpl implements MyDataDao<MyData> {
	private static final long serialVersionUID = 1L;
	
	private EntityManager entityManager;
	
	public MyDataDaoImpl() {
		super();
	}
	public MyDataDaoImpl(EntityManager manager) {
		this();
		entityManager = manager;
	}
	
	@Override
	public List<MyData> getAll(){
		Query query = entityManager.createQuery("from MyData");
		@SuppressWarnings("unchecked")
		List<MyData> list = query.getResultList();
		entityManager.close();
		return list;
	}

}

Controller

@Controller
public class HelloController {

	@Autowired
	MyDataRepository repository;
	
	@PersistenceContext
	EntityManager entityManager;
	
	MyDataDaoImpl dao;
	
	@PostConstruct
	public void init() {
		dao = new MyDataDaoImpl(entityManager);
		MyData d1 = new MyData();
		d1.setName("yamada");
		d1.setAge(20);
		d1.setMail("yamada@gmail.com");
		d1.setMemo("090999999");
		repository.saveAndFlush(d1);
		MyData d2 = new MyData();
		d2.setName("hanako");
		d2.setAge(18);
		d2.setMail("hanako@gmail.com");
		d2.setMemo("080888888");
		repository.saveAndFlush(d2);
		MyData d3 = new MyData();
		d3.setName("sachiko");
		d3.setAge(35);
		d3.setMail("sachiko@gmail.com");
		d3.setMemo("070777777");
		repository.saveAndFlush(d3);
	}	
	
	@RequestMapping(value="/", method=RequestMethod.GET)
	public ModelAndView index(
			@ModelAttribute("formModel") MyData mydata,
			ModelAndView mav) {
		mav.setViewName("index");
		mav.addObject("msg","this is sample content.");
		Iterable<MyData> list = dao.getAll();
		mav.addObject("datalist", list);
		return mav;
	}
// 省略

HTML

	<h1>Helo page</h1>
	<p th:text="${msg}"></p>
	<table>
		<form method="post" action="/" th:object="${formModel}">
		<tr>
			<th>ID</th><th>名前</th><th>メール</th><th>年齢</th><th>メモ(tel)</th>
		</tr>
		<tr th:each="obj: ${datalist}">
			<td th:text="${obj.id}"></td>
			<td th:text="${obj.name}"></td>
			<td th:text="${obj.mail}"></td>
			<td th:text="${obj.age}"></td>
			<td th:text="${obj.memo}"></td>
		</tr>
		</form>
	</table>

なんやとおおおおおおおおおおおおお

[SpringBoot2.4.3] PhoneValidatorクラスの作成

PhoneValidator.java

package com.example.demo;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PhoneValidator implements ConstraintValidator<Phone, String>{
	
	@Override
	public void initialize(Phone phone) {
		
	}
	
	@Override
	public boolean isValid(String input, ConstraintValidatorContext cxt) {
		if(input == null) {
			return false;
		}
		return input.matches("[0-9()-]*");
	}
}

### アノテーションクラス
validation classのためのアノテーションは色々用意しなければならない。

package com.example.demo;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;

@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@ReportAsSingleViolation
public @interface Phone {
	
	String message() default "please input a phone number.";
	
	Class<?>[] groups() default {};
	
	Class<? extends Payload>[] payload() default{};

}

### Phoneバリデータの使用
MyData.java

	@Column(nullable = true)
	@Phone
	private String memo;

html

		<tr>
			<td><label for="memo">メモ</label></td>
			<td><textarea name="memo" th:text="*{memo}" cols="20" rows="5"  th:errorclass="err"></textarea>
			<div th:if="${#fields.hasErrors('memo')}" th:errors="*{memo}" th:errorclass="err">
			</div></td>
		</tr>

おおお、javaだとなんか異様に感動するな