[SpringBoot2.4.3] テストアプリケーションを作る

gradleで作ります。

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-mustache'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.flywaydb:flyway-core'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'org.postgresql:postgresql'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

YAMLファイルの作成
src/main/resources/application.yml

spring:
  datasource:
    url:jdbc:postgresql://localhost:5432/test
    driverClassName:org.postgresql.Driver
    username:root
    password:
  mvc:
   favicon:
     enabled:false

ん? Nullにするとエラーになるな

V1__Create.sql

create table tsubuyaki (
	id serial primary key,
	txt varchar(100) not null,
	version integer not null default 0,
	updated_time timestamp not null default current_timestamp,
	created_time timestamp not null default current_timestamp
);

Model

package com.example.demo.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

import org.hibernate.validator.constraints.NotEmpty;

@Entity
public class Tsubuyaki extends TimestampEntity {
	
	@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
	public long id;
	
	@NotEmpty
	public String txt;
	
	@Version
	public long version;
}
package com.example.demo.model;

import java.sql.Timestamp;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;

@MappedSuperclass
public abstract class TimestampEntity {
	
	public Timestamp updatedTime;
	
	@Column(updatable=false)
	public Timestamp createdTime;
	
	@PrePersist
	public void prePersist() {
		Timestamp ts = new Timestamp((new Date()).getTime());
		this.createdTime = ts;
		this.updatedTime = ts;
	}
	
	@PreUpdate
	public void preUpdate() {
		this.updatedTime = new Timestamp((new Date()).getTime());
	}
}

Repository

package com.example.demo.repository;

import org.springframework.data.repository.CrudRepository;

import com.example.demo.model.Tsubuyaki;

public interface TsubuyakiRepository extends
CrudRepository<Tsubuyaki, Long>{
	Iterable<Tsubuyaki> findAllByOrderByUpdatedTimeDesc();
}

controller

package com.example.demo.controller;

import java.util.Collections;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
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 org.springframework.web.bind.annotation.RestController;

import com.example.demo.model.Tsubuyaki;
import com.example.demo.repository.TsubuyakiRepository;

@RestController @RequestMapping("/tsubuyaki")
public class TsubuyakiController {
	
	@Autowired TsubuyakiRepository repo;
	
	@RequestMapping(method=RequestMethod.POST)
	public Map<String, Tsubuyaki> create(
			@Valid @RequestBody Tsubuyaki tsubuyaki
			){
			return Collections.singletonMap(
					"tsubuyaki", repo.save(tsubuyaki));
		
	}
	@RequestMapping(method=RequestMethod.GET)
	public Map<String, Tsubuyaki> read(){
			return Collections.singletonMap(
					"tsubuyaki", repo.findAllByOrderByUpdatedTimeDesc());
		
	}
	@RequestMapping(path="/{id}", method=RequestMethod.PUT)
	public void update(
			@PathVariable Long id, @RequestParam String txt
			){
			Tsubuyaki tsubuyaki = repo.findOne(id);
			tsubuyaki.txt = txt;
			repo.save(tsubuyaki);
	}
	@RequestMapping(path="/{id}", method=RequestMethod.DELETE)
	public void delete(
			@PathVariable Long id
			){
			repo.delete(id);
	}
	
}

ぐぬぬぬ。。。

[SpringBoot2.4.3] Scheduling

Controller

package com.example.demo;

import java.util.Date;
import java.text.SimpleDateFormat;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

@SpringBootApplication
@EnableScheduling
public class HelloController {
	
	private static final SimpleDateFormat
	fmt = new SimpleDateFormat("HH:mm:ss");
	
	@Scheduled(fixedRate = 5000)
	public void reportTime() {
		System.out.println(fmt.format(new Date()));
	}
	
	public static void main(String[] args) {
		SpringApplication.run(HelloController.class, args);
	}
}

==========================
CONDITION EVALUATION DELTA
==========================

Positive matches:
—————–

TaskSchedulingAutoConfiguration#taskScheduler matched:
– @ConditionalOnBean (names: org.springframework.context.annotation.internalScheduledAnnotationProcessor; SearchStrategy: all) found bean ‘org.springframework.context.annotation.internalScheduledAnnotationProcessor’; @ConditionalOnMissingBean (types: org.springframework.scheduling.annotation.SchedulingConfigurer,org.springframework.scheduling.TaskScheduler,java.util.concurrent.ScheduledExecutorService; SearchStrategy: all) did not find any beans (OnBeanCondition)

Negative matches:
—————–

None

Exclusions:
———–

None

Unconditional classes:
———————-

None

16:02:28
16:02:33
16:02:38
16:02:43
16:02:48
16:02:53
16:02:58
16:03:03
16:03:08
16:03:13
16:03:18
16:03:23
16:03:28
16:03:33
16:03:38

なんやこれは、すげえ

[SpringBoot2.4.3] MessageSource

application.propertiesの設定
メッセージを messages_ja.properties で設定できるようにする
application.properties

spring.messages.basename=messages
spring.messages.cache-seconds=-1
spring.messages.encoding=UTF-8

messages_ja.properties

key=\u3053\u3093\u306B\u3061\u306F\u3002

Controller

String message = msg.getMessage("key",null,Locale.JAPAN);

@RequestMapping(value="/msg", method=RequestMethod.GET)
public Map<String, String> msg(Locale locale){
	String message = msg.getMessage("key", null, locale);
	return Collections.singletonMap("message", message);
}

なるほどー

[SpringBoot2.4.3] Mustacheを使う

MustacheとはSpring Bootのテンプレートエンジンです。

pom.xml

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mustache</artifactId>
		</dependency>

controller

package com.example.demo;

import java.util.Date;
import java.util.Map;

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

@Controller
public class HelloController {
	
	@RequestMapping("/hello-mst")
	public String hello(
			@RequestParam(defaultValue="World")String name,
			Map<String, Object> model
			) {
		model.put("name", name);
		model.put("date", new Date());
		return "hello-mst";
	}
}

html

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Hello Mustache</title>
</head>
<body>
	<div>
		<p><b>Message:</b>Hello, {{name}}</p>
		<p><b>Date:</b>{{date}}</p>
	</div>
</body>
</html>

あら、うまくいかんね。

[SpringBoot2.4.3] バリデーション

package com.example.demo;

import javax.validation.Valid;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;
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.RestController;

@RestController
public class HelloController {
	
	@RequestMapping(value="/address", method=RequestMethod.POST)
	public Address create(@Valid @RequestBody Address address) {
		return address;
	}
	
	public static class Address {
		
		@NotEmpty
		@Size(min=7, max=7)
		public String zip;
		
		@NotEmpty
		public String address;
	}
}

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

htmlファイル
src/main/resources/public/file-upload.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>File Upload</title>
	<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css">
</head>
<body>
<div class="">
	<form id="form" enctype="multipart/form-data">
		<p><input type="file" name="file"></p>
		<p><input type="button" id="upload" value="upload"></p>
	</form>
	<span id="result" style="padding:3px"></span>

</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-alpha1/js/bootstrap.min.js"></script>
<script
  src="https://code.jquery.com/jquery-3.5.1.js"
  integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
  crossorigin="anonymous"></script>
<script>
$(function(){
	$('#upload').click(function(){
		var formData = new FormData(
			$('#form').get()[0]
		);
		$.ajax({
			url:'/upload',
			method:'post',
			data:formData,
			processData:false,
			contentType:false,
			cache: false
		}).done(function(data,status,jqxhr){
			$('#result').text('結果: 成功');
		}).fail(function(data, status, jqxhr){
			$('#result').text('結果: 失敗');
		});
	});
});
</script>
</body>
</html>

Controller

package com.example.demo;

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

import javax.servlet.http.HttpServletResponse;

import org.springframework.util.FileCopyUtils;
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.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class HelloController {
	
	@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);
		}
	}

publicに置くと、routingしなくて良いのか。。

ほう、こんなんなってるのか。。

[SpringBoot2.4.3] Serviceクラスを使用する

.com.example.demo.service
RandomNumberService.java

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class RandomNumberService {
	
	public int zeroToNine() {
		return (int)(Math.random() * 10);
	}
}

HelloController.java

package com.example.demo;

import java.util.Collections;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.service.RandomNumberService;

@RestController
public class HelloController {
	
	@Autowired
	RandomNumberService random;

	@RequestMapping("/random")
	public Map<String, Integer>random(){
		int value = random.zeroToNine();
		return Collections.singletonMap("value", value);
	}
}

http://localhost:8080/random
{“value”:2}

autowiredで呼び出すのか。。

[Spring Boot2.4.2] XMLで文字列表示

MainController.java

package com.example.demo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainController {
	public static void main(String[] args) {
		
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("applicationContext.xml");
		
		ISyain syain = context.getBean("testSyain", ISyain.class);
		
		System.out.println(syain.getHello());
		context.close();		
	}
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="testSyain" class="com.example.demo.Syain"></bean>
</beans>

ISyain.java

package com.example.demo;

public interface ISyain {
	public String getHello();
}

Syain.java

package com.example.demo;

public class Syain implements ISyain {
	@Override
	public String getHello() {
		return "Hello World!";
	}
}

なるほどー

[Spring Boot2.4.2] 例外処理

TestException.java

package com.example.demo;

public class TestException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	
	TestException(String msg){
		super(msg);
	}
}

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="utf-8" />
    <title>Check</title>
  </head>
  <body>
    <p th:text="${message1}"></p>
  </body>
</html>

MainController.java

package com.example.demo;

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

@Controller
@RequestMapping("/test1")
public class MainController {

    @GetMapping
	public String index(Model model)  {
	runSample();
	 return "test1/index";
	}
    
    void runSample()   {
		int i = 5;
		if (i == 5) {
			throw new TestException("独自の例外です"); 
		}
	}
}

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Feb 07 16:57:28 JST 2021
There was an unexpected error (type=Internal Server Error, status=500).
独自の例外です
com.example.demo.TestException: 独自の例外です

[Spring Boot2.4.2] JSONの送受信

src/main/resources/templates/test1/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <p id="p1"></p>
    <p id="p2"></p>
  </body>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
		$("#p1").text("");
		$("#p2").text("");
        var json1 = {
			bangou: "1",
			name: "鈴木",
		};
		$.ajax({
		url: "http://localhost:8080/test1/index",
		type: "POST",
		contentType: "application/json",
		data: JSON.stringfy(json1),
		dataType: "json",
		})
		.done(function (data1, textStatus, jqXHR){
			$("#p1").text(jqXHR.status);
			$("#p2").text(JSON.stringify(data1));
		})
		.fail(function (jqXHR, textStatus, errorThrown){
			$("#p1").text(jqXHR.status);
		})
		.always(function() {});
</script>
</html>

Syain.java

package com.example.demo;
import java.io.Serializable;

public class Syain implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private String bangou;
	private String name;
	
	public String getBangou() {
		return bangou;
	}
	public void setBangou(String bangou) {
		this.bangou = bangou;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

MainController.java
L @ResponseBodyはコントローラからの戻り値を返す
L

package com.example.demo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test1")
public class MainController {
	
	@PostMapping("/index")
	@ResponseBody
    public Syain output1(
    		@RequestBody Syain syain) {
				System.out.println(syain.getBangou());
				System.out.println(syain.getName());
				return syain;
	}
}

2021-02-07 12:07:42.608 INFO 44783 — [ restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 772 ms
2021-02-07 12:07:42.743 INFO 44783 — [ restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService ‘applicationTaskExecutor’
2021-02-07 12:07:42.889 INFO 44783 — [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2021-02-07 12:07:42.919 INFO 44783 — [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ”
2021-02-07 12:07:42.927 INFO 44783 — [ restartedMain] com.example.demo.TestApplication : Started TestApplication in 1.355 seconds (JVM running for 7.09)
2021-02-07 12:08:35.401 INFO 44783 — [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet ‘dispatcherServlet’
2021-02-07 12:08:35.401 INFO 44783 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet ‘dispatcherServlet’
2021-02-07 12:08:35.402 INFO 44783 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-02-07 12:08:37.410 WARN 44783 — [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method ‘GET’ not supported]

うーむ、上手く表示されんな。。