The author who uses Spring for business studied "[Introduction to Spring Boot2 Programming](https://www.amazon.co.jp/Spring-Boot-2-Introduction to Programming-Yano Palm Tatsu / dp / 4798053473)" It's like a cheat sheet or reading memo. The target audience is those who have learned the basics of Java and have no experience with Spring Boot-the level who have begun to study the basics. Language is Java, Build tool is Maven, The template engine is Thymeleaf.
A framework (group) created by combining the library provided by Spring Framework and Spring MVC (framework) for high-speed WEB application development.
Since this article is used as a cheat sheet, it is omitted here.
Groovy is a language that is suitable for prototyping in a short time before full-scale development in Java. Groovy is omitted here.
@ RestController
It is used to build a system that can be accessed from the outside to retrieve necessary information.
Uses such as sending back necessary information in text or XML format when accessed.
@ RequestMapping
It means to execute this method when this address is accessed.
Write as @RequestMapping ("/ ")
and specify the path as an argument.
When the specified path is accessed, the method with this annotation will be executed.
SampleController.java
@RestController
public class SampleController {
@RequestMapping("/")
public String index() {
return "Hello SpringBoot World";
}
}
A template engine that can be used by adding to HTML tags. feature is,
--Prepared a unique attribute of "th: ◯◯" in the tag --Write the variable name in the form of "$ {}" and embed the value in that place
home.html
<body>
<h1>Hello!</h1>
<p class="msg" th:text="${msg}">this is sample.</p>
</body>
It is possible to write HTML with Groovy as a template engine, but it is omitted here for convenience of use.
[@Controller is described in Chapter 3](# -controller)
@ ResponseBody
For the method with @ResponseBody
, the return value becomes the content of the response as it is.
For controller methods with @ RestController
, the return value will be the content without adding @ResponseBody
.
Methods in controller classes with @Controller
often return the ModelAndView class, except when you need to return JSON or XML, you can add @ResponseBody
to the controller to add content. It can return itself.
Spring Boot uses the built-in Java server to run the app, so no deployment is required. It works well with things that run programs inside a server, such as cloud services. Although it has the disadvantage of increasing the file size, it is becoming the de facto standard for Web development in Java.
The definition of parent is as follows
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
The definition of dependencies is as follows
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The definitions of build and plugin are as follows
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
When you create a Spring Starter project with Spring Tool Suite (STS), a file called MyBootAppApplication.java
is created by default.
If @SpringBootApplication is attached, it will be a class that will be called when SpringBoot starts.
SpringApplication.run the annotated class will launch the app.
"Run" is a method for starting an application. For the argument, prepare the Class instance of the class to be executed and the data to be passed as a parameter.
MyBootAppApplication.java
@SpringBootApplication
public class MyBootAppApplication {
public static void main(String[] args) {
SpringApplication.run(MyBootAppApplication.class, args);
}
}
architecture | role |
---|---|
Model | Manage the data used by the application |
View | Handle screen display |
Controller | Control the entire process |
By using a path variable in the form of @RequestMapping ("/ {param} ")
, the value passed to the path part below the address can be received as a variable.
[What is @RequestMapping](# -requestmapping)
Indicates that the value is passed by a path variable.
@RequestMapping("/{num}")
public String index(@PathVariable int num){
int res = 0;
for(int i = 1; i<= num; i++){
res += i;
}
return "total:" + res;
}
REST services can also return a class as a return value other than String. The returned instance is converted to JSON format.
//Define DataObject class separately
public DataObject index(@PathVariable int id) {
return new DataObject();
}
When using a normal web page, add @Controller
before the controller class.
In this article, I want to use Thymeleaf as a template, so add the dependency to pom.xml as follows.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
@ Controller Used to render and display HTML pages using templates.
SampleController.java
@Controller
public class SampleController {
@RequestMapping("/")
public String home(){
return "home";
}
}
Model
is a class for managing data used in web pages.
Data can be passed by setting the value used in the template in this Model
.
Set the value with ʻaddAttribute. Specify the name of the value in the first argument and the complementary value in the second argument. Following the sample below, the value of the variable msg can be retrieved in the form of
$ {msg}on the template side. The return value cannot be used as
Model` (because it doesn't have template information).
@RequestMapping("/{num}")
public String home(@PathVariable int num, Model model) {
...
model.addAttribute("msg", "total:" + res);
return "home";
}
Manage the data used in the template and information about the view (template name, etc.). By returning this ModelAndView
as a return value, the set template will be used. Set the object as ʻaddObject (String entitySetName, Object entity)and set the name of the view to use with
setViewName (String viewName)`.
@RequestMapping("/{num}")
public ModelAndView index(@PathVariable int num, ModelAndView mav) {
...
mav.addObject("msg", "total:" + res);
mav.setViewName("home");
return mav;
}
Model
It manages the data used in the template collectively, but since it does not have view-related information, Model
cannot be used as a return value.
ModelAndView
Manage all the data used in the template and all the information about the view. By returning ModelAndView
as a return value, the set template will be used.
Prepare a form with <form method =" POST "action =" / ">
, and send it by POST with the same " / "
destination. In the ʻinput type =" text "tag, the value of $ value is displayed in the input field like
th: text =" $ {value} ". If there are multiple arguments for
@RequestMapping like
@RequestMapping (value = "/", method = RequestMethod.POST) , omit the argument name like
value = "/" for the mapping destination. Need to write without. The sample has the same address and distinguishes between
GET and
POSTfor methods. The value submitted from the form is processed by the send method.
@RequestParam is an annotation to specify the value submitted to the form, which passes the value entered in the form's
name = "text1" to this argument
str`.
[Stove Torah side]
SampleController.java
@Controller
public class SampleController {
@RequestMapping(value="/", method=RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("form");
mav.addObject("msg", "please put and send your name.");
return mav;
}
@RequestMapping(value="/", method=RequestMethod.POST)
public ModelAndView send(@RequestParam("text1")String str, ModelAndView mav) {
mav.addObject("msg", "Hello");
mav.addObject("value", "message");
mav.setViewName("form");
return mav;
}
[Template side]
form.html
<body>
<h1>Sample Form</h1>
<p th:text ="${msg}">This replaces msg</p>
<form method="POST" action="/">
<input type="text" name="text1" th:value="${value}"/>
<input type="submit" value="Click" />
</form>
</body>
The value submitted from the form is received by the send
method. The method has four arguments with @RequestParam
so that it can receive the value from the form. Specify the name of the parameter with value
, and specify the null constraint of the value with required
.
[Controller side]
SampleController.java
@Controller
public class SampleController {
@RequestMapping(value="/", method=RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
//GET request omitted
}
@RequestMapping(value="/", method=RequestMethod.POST)
public ModelAndView send(
//Checkbox: The value can get the selected state as a boolean value
@RequestParam(value="check1", required=false)boolean check1,
//Radio button: Pass the value of the selected item as a String value, but null if not selected
@RequestParam(value="radio1",required=false)String radio1,
//Selection list: When a single item is selected, value is passed as a String value, but if multiple items can be selected, it is passed as a String array, and if it is not selected, it is null.
@RequestParam(value="select1", required=false)String select1,
@RequestParam(value="select2", required=false)String[] select2,
ModelAndView mav) {
String res = "";
//Processing omitted
...
//Embed res in msg
mav.addObject("msg", res);
mav.setViewName("form");
return mav;
}
}
[Template side]
<body>
<h1>Form Controll Example</h1>
<form method="POST" action="/">
<div>
<input type="checkbox" id="check1" name="check1" />
<label for="check1">Checkbox</label>
</div>
<div>
<input type="radio" id="radioA" name="radio1" value="male" />
<label for="radioA">male</label>
</div>
<div>
<input type="radio" id="radioB" name="radio1" value="female" />
<label for="radioB">Female</label>
</div>
<div>
<select id="select1" name="select1" size="4">
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Yellow">yellow</option>
</select>
<select id="select2" name="select2" size="4" multiple="multiple">
<option value="SmartPhone">smartphone</option>
<option value="Tablet">Tablet</option>
<option value="Laptop">Laptop</option>
</select>
</div>
<input type="submit" value="Click" />
</form>
</body>
When accessing one address, there are ** forward ** and ** redirect ** as a method of moving to another address as needed.
[Controller side]
SampleController.java
@Controller
public class SampleController {
@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
mav.setViewName("home");
return mav;
}
//"/other"Is the address itself"/"It is changed to, and the displayed contents are also"/"Become a thing.
@RequestMapping("/other")
public String other() {
return "redirect:/";
}
//"/home"When you access, the displayed content remains the same"/"Show things.
@RequestMapping("/home")
public String home() {
return "forward:/";
}
}
[Template side]
home.html
<body>
<h1>Home Page</h1>
</body>
Frequently used classes can be used by directly describing them in constant expressions as constants "** # name **". Since it is a class constant, it can be used by directly calling class methods. Like "# dates. Class methods". The following example
Utility object | constant |
---|---|
#strings | String class constant |
#numbers | Number class constant |
#Bools | Boolean class constant |
#dates | Date class constant |
#objects | Object class constant |
#arrays | Array class constant |
#lists | List class constants |
#sets | Constants in the Set class |
#maps | Map class constants |
Create a Date class object in the first argument using the format
method of the Date class constant # dates
, and specify the format yyyy / MM / dd HH: mm: ss
in the second argument.
<p th:text="${#dates.format(new java.util.Date(), 'dd/MMM/yyyy HH:mm')}"></p>
Using the formatInteger
method of the constant # numbers
of the Number class, the integer is displayed as the first argument and the digits of the second argument are displayed, separated by commas by the third argument.
<p th:text="${#numbers.formatInteger(10000000, 3, 'COMMA')}"></p>
The toUpperCase
of the String class constant # strings
is a method that converts the argument text to all uppercase.
<p th:text="${#strings.toUpperCase('show uppercase')}"></p>
By writing like {param.id}
, you can receive the value sent in the form of "id = ○○" and use the parameters directly in the template without going through the controller. However, since the obtained value is an array, the value is taken out and used.
<!--/?id=hoge&name=Pass like fuga-->
<p th:text="'from parameter.. id=' + ${param.id[0] + ',name=' + param.name[0]}"></p>
Extract the value from the property file and use it in the template. The description method is # {specify value}
message.properties
article.title=this is home page.
article.message=this message from properties.
<h1 th:text="#{article.title}"></h1>
<p th:text="#{article.message}"></p>
If you access /? Id = fuga
, ** / hoge / fuga ** will be set for link.
<p><a href="home.html" th:href="@{/hoge/{id}(id=${param.id})}">link</a></p>
Variable expressions can use objects as well as numbers and text. The way to do this is to specify an object and use * {value}
to retrieve the value in the selected object.
<table th:object="${object}">
<tr><th>ID</th><td th:text="*{id}"></td></tr>
<tr><th>NAME</th><td th:text="*{name}"></td></tr>
<tr><th>MAIL</th><td th:text="*{value}"></td></tr>
</table>
By enclosing the text before and after it with "|", you can directly write the variable expression and concatenate the character strings.
<p th:text="|My name is *{name}. Mailaddress is *{value}.|"></p>
In Thymeleaf, when outputting text as a variable expression, all HTML tags are escaped for safety, so
You can release the escape by using th: utext
. However, if the value contains an HTML tag, it will work as it is, so if you create text based on the information sent by the user, you will be vulnerable to attacks such as XSS.
[Controller side]
@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
mav.setViewName("home");
mav.addObject("msg", "message 1<hr/>message 2<br/>message3");
return mav;
}
[Template side]
<p th:utext="${msg}">message.</p>
Change the output contents according to the result of the boolean value prepared in advance.
[Controller side]
@RequestMapping("/{id}")
public ModelAndView index(@PathVariable int id,
ModelAndView mav) {
mav.setViewName("check");
mav.addObject("id",id);
mav.addObject("num",id % 2 == 0);
mav.addObject("even","Even number!");
mav.addObject("odd","Odd number...");
return mav;
}
[Template side]
check.html
<p th:class="${num} ? 'even' : 'odd'"></p>
The value set is true
(non-zero number," 0 ",
In the case of (including text other than values such as "off" and "no"), this tag and the tags inside it are displayed.
Display this tag and internal tags if the value set is false
(including text such as number zero," 0 "," off "," no ").
[Controller side]
@RequestMapping("/{id}")
public ModelAndView index(@PathVariable int id,
ModelAndView mav) {
mav.setViewName("check");
mav.addObject("id",id);
mav.addObject("num",id >= 0);
mav.addObject("trueVal","POSITIVE!");
mav.addObject("falseVal","negative...");
return mav;
}
[Template side]
check.html
<p th:if="${num}" th:text="${id} + ' is ' + ${trueVal}"></p>
<p th:unless="${num}" th:text="${id} + ' is ' + ${falseVal}"></p>
Checks the value of the specified conditional expression, searches for the same value from the th: case
inside it, and outputs only that tag.
th: case =" * "
is a wildcard that catches anything that doesn't meet any of the conditions.
[Controller side]
@RequestMapping("/{month}")
public ModelAndView index(@PathVariable int month,
ModelAndView mav) {
mav.setViewName("index");
int m = Math.abs(month) % 12;
m = m == 0 ? 12 : m;
mav.addObject("month",m);
mav.addObject("check",Math.floor(m / 3));
return mav;
}
[Template side]
<div th:switch="${check}">
<p th:case="0" th:text="|${month} - Winter|"></p>
<p th:case="1" th:text="|${month} - Spring|"></p>
<p th:case="2" th:text="|${month} - Summer|"></p>
<p th:case="3" th:text="|${month} - Autumn|"></p>
<p th:case="4" th:text="|${month} - Winter|"></p>
<p th:case="*">...?</p>
</div>
In th: each
, prepare an array or collection as a value, and describe it likevalue: $ {list}
in the manner of an extended for statement. This will take the values from the list after the colon and assign them to the variable before the colon.
[Template side]
<table>
<tr>
<th>NAME</th>
<th>MAIL</th>
<th>TEL</th>
</tr>
<tr th:each="obj:${data}">
<td th:text="${obj[0]}"></td>
<td th:text="${obj[1]}"></td>
<td th:text="${obj[2]}"></td>
</tr>
</table>
If you declare it in the form of __ $ {variable} __
, it will be evaluated in advance and the variable expression will be executed.
[Controller side]
ArrayList<Pockemon> monsters = new ArrayList<>();
monsters.add(new Pockemon(0,"Hitokage","Fire"));
monsters.add(new Pockemon(1,"Fushigidane","Grass"));
monsters.add(new Pockemon(2,"Zenigame","Water"));
mav.addObject("num", 1);
mav.addObject("monsters",monsters);
return mav
[Template side]
<div th:object="${monsters.get(${num})}">
<p th:text="*{id}"></p>
</div>
Even if you do not prepare it in the form of an attribute like th: text
(There is a side that does not affect the page display even if Thymeleaf is not functioning), Thymeleaf directly between HTML tags Variable expression can be written. If you write th: inline =" text "
in the tag, you can write a variable expression like [[$ {variable}]]
inside it to enable inline processing.
[Controller side]
ArrayList<Pockemon> monsters = new ArrayList<>();
monsters.add(new Pockemon(0,"Hitokage","Fire"));
monsters.add(new Pockemon(1,"Fushigidane","Grass"));
monsters.add(new Pockemon(2,"Zenigame","Water"));
mav.addObject("monsters",monsters);
return mav
[Template side]
<tr th:inline="text" th:each="monster : ${monsters}">
<td>[[${monster.id}]]</td>
<td>[[${monster.name}]]</td>
<td>[[${monster.type}]]</td>
</tr>
Specify the value <script th: inline =" javascript ">
and ʻinline as
"javascript" instead of
"text" , and the variable expression is
/ * [[$ {variable expression}]] By writing] * / `in the comment-out form, it can be embedded in the script and used.
python
<script th:inline="javascript">
console.log(/*[[$(variable)]]*/);
</script>
A method called ** template fragment ** that combines multiple files to form a page. Insert the part described as a part in the file that can be cut and handled as a ** fragment ** in the specified location of another template.
[Templates that become parts]
part.html
<p th:fragment="frag_body">
[Incorporation side]
<p th:include="part::frag_body">
In Spring, JSP and Groovy can also be used as template engines, but they are omitted for convenience.
In Java, DB is used by using JPA (Java Persistence API persistence API). DB access is also performed in Spring using SpringBootStarterDataJPA.
If you manage the project with Maven, add the following dependency to pom.xml and use it.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
When using JPA, the data part is defined as a class called ** entity **. A table is defined in the DB and data is stored there as records, but it can be considered that the entity stores each record as a Java object.
Below is a sample of the ** Entity ** class that serves as a model.
@Entity
@Table(name="mydata")
public class MyData {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
private long usrId;
@Column(length = 50, nullable = false)
private String usrName;
@Column(length = 200, nullable = true)
private String usrAddress;
@Column(nullable = true)
private Integer age;
@Column(nullable = true)
private String comment;
public long getUsrId() { return usrId; }
public void setUsrId(long usrId) { this.usrId = usrId; }
public String getUsrName() { return usrName; }
public void setUsrName(String usrName) { this.usrName = usrName; }
public String getUsrAddress() { return usrAddress; }
public void setUsrAddress(String usrAddress) { this.usrAddress = usrAddress; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getComment() { return comment; }
public void setComment(String comment) { this.comment = comment; }
}
@ Entity
Annotation indicating that it is an entity class
@ Table(name="mydata")
Specifies the table assigned to this entity class. If name
is omitted, the class name will be used as the table name.
@ Id
Specifying the primary key
@ GeneratedValue(strategy = GenerationType.AUTO)
Automatically generate a value for the primary key field. The generation method is specified in strategy
, and in the sample, GenerationType.AUTO
and the value of enumeration type are specified to be automatically assigned.
@ Column
Specify the column name assigned to the field. If name
is omitted, the field name is used as it is as the column name.
@Column argument | Description |
---|---|
name | Specify column name |
length | Maximum length(Number of characters in String)Specify |
nullable | Specify whether to allow null |
Entity-> Class that allows the data stored in the table to be treated as an object in Java Prepared as repository-> interface, general-purpose DB access processing is automatically generated and implemented, so there is almost no need to write code.
Created by inheriting JpaRepository @ Repository Indicates that this class is a data access class.
@Repository
public interface MyDataRepository extends JpaRepository<MyData, Long> {
}
@ Autowired
In the sample, @Autowired
is used to inject the MyDataRepository instance into the field.
If you put @Autowired
in front of the instance variable, it will search for the corresponding class from the classes with @Component
, new it, and plunge it into the field. Although it says @ Component
, @ Controller
, @ Repository
, @ Service
are used properly depending on the layer.
All entities can be automatically retrieved by the method provided in JpaRepository, which is the inheritance source.
@Controller
public class SampleController {
@Autowired
MyDataRepository repository;
@RequestMapping("/")
public ModelAndView home(ModelAndView mav) {
mav.setViewName("home");
Iterable<MyData> list = repository.findAll();
mav.addObject("data", list);
return mav;
}
}
「Create」「Read」「Update」「Delete」 @ ModelAttribute It is used to automatically prepare an instance of the entity class. Specify the instance name in the argument. In the index method at the time of GET access, a new instance is created and assigned to the argument of MyData (all values etc. are initial values) In the form method called by POST access, the value of the submitted form is automatically collected and passed to the MyData instance.
repository.saveAndFlush(mydata);
The prepared entity is specified in the argument of the saveAndFlush
method provided in JpaRepository and is made persistent.
@ Transactional
The @Transactional
annotation is for the transaction function, and by attaching it to the method, the DB access executed in the method will be executed in a batch.
In the case of processing that rewrites data, transactions are important processing, such as preventing the DB from being accessed from the outside and causing problems with data integrity.
The argument readOnly = false
literally indicates that it is" read only (non-rewritable) ".
When readOnly = false
is set, the transaction changes from a read-only transaction to a transaction that allows data update such as saving.
SampleController.java
@Controller
public class SampleController {
@Autowired
MyDataRepository repository;
@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 = repository.findAll();
mav.addObject("datalist", list);
}
@RequestMapping(value = "/", method = RequestMethod.POST)
@Transactional(readonly = false)
public ModelAndView form(@ModelAttribute("formModel") MyData mydata,
ModelAndView mav){
repository.saveAndFlush(mydata);
return new ModelAndView("redirect:/");
}
}
<body>
<table>
<form method="post" action="/" th:object="${formModel}">
<tr>
<td><label for="name>name</label></td>
<td><input type="text" name="name" th:value="*{name}" /></td>
</tr>
<tr>
<td><label for="name>age</label></td>
<td><input type="text" name="age" th:value="*{age}" /></td>
</tr>
<tr>
<td><label for="name>Email</label></td>
<td><input type="text" name="mail" th:value="*{mail}" /></td>
</tr>
<tr>
<td><label for="name>Note</label></td>
<td><textarea name="memo" th:value="*{memo}" cols="20" rows="5"></textarea></td>
</tr>
</form>
</table>
<hr/>
<table>
<tr>
<th>ID</th>
<th>name</th>
</tr>
<tr th:each="obj : ${datalist}">
<td>${obj.id}</td>
<td>${obj.name}</td>
</tr>
<table>
</body>
@ PostConstruct Indicates that the method is called after the constructor creates an instance. The controller is instantiated only once at the beginning, and the instance is retained thereafter.
SampleComtroller.java
@PostConstruct
public void init(){
MyData d1 = new MyData();
d1.setName("tuyano");
d1.setAge(123);
d1.setMail("[email protected]");
d1.setMemo("this is my data!");
repository.saveAndFlush(d1);
MyData d2 = new MyData();
d2.setName("hanako");
d2.setAge(15);
d2.setMail("hanako@flower");
d2.setMemo("my girl friend.");
repository.saveAndFlush(d2);
MyData d3 = new MyData();
d3.setName("sachiko");
d3.setAge(37);
d3.setMail("sachico@happy");
d3.setMemo("my work friend...");
repository.saveAndFlush(d3);
}
【template】
Th: object =" $ {formModel} "
is specified in <form>
, but if the entity to be edited is set in this formModel
, it is done bytype = "hidden"
. The ID is stored in the hidden field.
<body>
<h1 th:text="${title}">Edit page</h1>
<table>
<form method="post" action="/edit" th:object="${formModel}">
<input type="hidden" name="id" th:value="*{id}" />
<tr><td><label for="name">name</label></td>
<td><input type="text" name="name" th:value="*{name}" /></td></tr>
<tr><td><label for="age">age</label></td>
<td><input type="text" name="age" th:value="*{age}" /></td></tr>
<tr><td><label for="mail">Email</label></td>
<td><input type="text" name="mail" th:value="*{mail}" /></td></tr>
<tr><td><label for="memo">Note</label></td>
<td><textarea name="memo" th:text="*{memo}"
cols="20" rows="5"></textarea></td></tr>
<tr><td></td><td><input type="submit" /></td></tr>
</form>
</table>
</body>
Implement the process of searching and retrieving entities by ID in the repository.
findById
is a method to retrieve a MyData instance with an ID number as an argument.
MyDataRepository.java
@Repository
public interface MyDataRepository extends JpaRepository<MyData, Long> {
public MyData findById(Long name):
}
In the edit method called at the time of GET access, the entity is acquired by findById
with the ID sent in the query as an argument, and the get
is used to specify the name formModel
and ʻaddObject. .. The update method is called when the form is submitted to
/ edit. When saving an entity based on the sent form data, update it with the same
saveAndFlush` as when saving new data. The difference between new save and update is whether the ID is specified for the argument entity.
SampleController.java
@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
public ModelAndView edit(@ModelAttribute MyData mydata, @PathVariable int id, ModelAndView mav) {
mav.setViewName("edit");
mav.addObject("title","edit mydata.");
MyData data = repository.findById((long)id);
mav.addObject("formModel",data.get());
return mav;
}
@RequestMapping(value = "/edit", method = RequestMethod.POST)
@Transactional(readOnly=false)
public ModelAndView update(@ModelAttribute MyData mydata, ModelAndView mav) {
repository.saveAndFlush(mydata);
return new ModelAndView("redirect:/");
}
There is a delete
method for GET access to / delete
and a remove
method for handling POST access.
In the delete
method, the entity with the specified ID is searched by findById
and displayed. After that, the entity sent by the remove method sent by POST is deleted. Don't forget to annotate the method with @Transactional
because delete
also involves changing the DB.
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
public ModelAndView delete(@PathVariable int id, ModelAndView mav) {
mav.setViewName("delete");
mav.addObject("title", "delete mydata.");
MyData data = repository.findById((long)id);
mav.addObject("formModel", data);
return mav;
}
@RequestMapping(value = "/delete", method = RequestMethod.POST)
@Transactional(readOnly = false)
public ModelAndView remove(@RequestParam long id, ModelAndView mav){
repository.delete(id);
return new ModelAndView("redirect:/");
}
JpaRepository has a built-in automatic code generation function using a dictionary. JpaRepository generates and executes query text by JPQL.
findById(argument)
↓
"find" "by" "id"argument
↓
"select *from table where id=argument"Execution of the query
And It is used when searching for an element that matches both the values of two items. Prepare two arguments as the value of each item.
findByIdAndName
↓
from MyDataEntity where id = ?1 and name = ?2
Or It is used when searching for an element that matches either of the values of two items. Prepare two arguments as the value of each item.
findByIdOrName
↓
from MyDataEntity where id = ?1 or name = ?2
Between It is used when passing two arguments as values and searching for a value between them. Searches for elements within a certain range of specified items.
findByAgeBetween
↓
from MyDataEntity where age between ?1 and ?2
LessThan Search for numerical items that are smaller than the value specified in the argument.
findByAgeLessThan
↓
from MyDataEntity where age < ?1
GreaterThan Search for a numeric item that is larger than the value specified in the argument.
findByAgeGreaterThan
↓
from MyDataEntity where age > ?1
IsNull Searches for the value of the specified item to be null.
findCommentIsNull
↓
from MyDataEntity where comment is null
IsNotNull, NotNull
Searches for the value of the specified item that is not null. Understand even Not Null
findByCommentNotNull,
findByCommentIsNotNull
↓
from MyDataEntity where comment not null
Like Perform a LIKE search of the text. Ambiguous search for a value from a specified item. You can use it with a wildcard attached to the value passed to the argument.
findByNameLike
↓
from MyDataEntity where name like ?1
NotLike Ambiguous search for things that do not include the search string. You can use it with a wildcard attached to the value passed to the argument.
findByNameNotLike
↓
from MyDataEntity where name not like ?1
OrderBy
Specify the order. It is added after the normal search method name. You can specify ascending or descending order by adding ʻAsc or
Desc` after the item name.
findByNameOrderByAgeAsc
↓
from MyDataEntity where name = ?1 order by age Asc
Not Searches for the specified item that is not equal to the value of the argument.
findByNameNot
↓
from MyDataEntity where name <> ?1
In Search if the value of the specified item matches any of the values provided in the argument collection
findByNameIn(Collection<String> name)
↓
from MyDataEntity where name in ?1
NotIn Finds the value of the specified item that does not match any of the values provided in the argument collection
findByNameNotIn(Collection<String> name)
↓
from MyDataEntity where name not in ?1
@Repository
public interface MyDataRepository extends JpaRepository<MyData, Long> {
// "%" + str + "%"You need to specify a wildcard like
public List<MyData> findById(Long name);
public List<MyData> findByNameLike(String name);
public List<MyData> findByIdIsNotNullOrderByIdDesc();
public List<MyData> findByAgeGreaterThan(Integer age);
public List<MyData> findByAgeBetween(Integer age1, Integer age2);
}
find [By ○○] [Other search conditions] [OrderBy etc.]
** All simple searches are auto-generated methods. Only complicated search processing is defined and used in DAO **
By preparing validation in the model in advance, rules are set for each input item, and it is possible to check whether the input value is violated. @ Validated This will check the value of the entity for variation. By adding this, each value of the entity will be checked automatically.
@ BindingResult
The result of the validation check is received by the next argument, BindingResult
.
The hasErrors
method can be used to check if an error has occurred. If it is true
, there is an error, and if it is false
, there is no error. ..
if(!result.hasErrors()) {...}
The variable expression is set in $ {# fields.detailedErrors ()}
in th: each is an object that summarizes the results of validation check of each field of the entity in # fields
, anddetailedErrors The
method returns information about the error that occurred as a collective list. In th: each
, the error objects are taken out from the list and set in the variable ʻerror`.
<li th:each="error : ${#fields.detailedErrors}" class="err" th:text="${error.message}">
ʻError has a property called
message, which gives you the message of the error that occurred. It is output in the form of
th: text =" $ {error.message} "`.
【template】
<form method="post" action="/" th:object="${formModel}">
<ul>
<li th:each="error : ${#fields.detailedErrors()}"
class="err" th:text="${error.message}" />
</ul>
<tr><td><label for="name">name</label></td>
<td><input type="text" name="name"
th:field="*{name}" /></td></tr>
<tr><td><label for="age">age</label></td>
<td><input type="text" name="age"
th:field="*{age}" /></td></tr>
<tr><td><label for="mail">Email</label></td>
<td><input type="text" name="mail"
th:field="*{mail}" /></td></tr>
<tr><td><label for="memo">Note</label></td>
<td><textarea name="memo" th:field="*{memo}"
cols="20" rows="5" ></textarea></td></tr>
<tr><td></td><td><input type="submit" /></td></tr>
</form>
【entity】
@Entity
@Table(name = "mydata")
public class MyData {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
@NotNull
private long id;
@Column(length = 50, nullable = false)
@NotEmpty
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;
//…… Accessor omitted ……
}
【controller】 A sample that checks whether the input value violates the validation set in the entity and allows the value to be stored only when the rule is satisfied.
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(
@ModelAttribute("formModel") MyData mydata,
ModelAndView mav) {
mav.setViewName("index");
mav.addObject("msg","this is sample content.");
mav.addObject("formModel",mydata);
Iterable<MyData> list = repository.findAll();
mav.addObject("datalist",list);
return mav;
}
@RequestMapping(value = "/", method = RequestMethod.POST)
@Transactional(readOnly=false)
public ModelAndView form(
@ModelAttribute("formModel") @Validated MyData mydata,
BindingResult result,
ModelAndView mov) {
ModelAndView res = null;
if (!result.hasErrors()){
repository.saveAndFlush(mydata);
res = new ModelAndView("redirect:/");
} else {
mov.setViewName("index");
mov.addObject("msg","sorry, error is occured...");
Iterable<MyData> list = repository.findAll();
mov.addObject("datalist",list);
res = mov;
}
return res;
}
<input type="text" name="name" th:value="*{name}" th:errorclass="err" />
The object's name
property is set to a value asth: value = "* {name}"
. An attribute called th: errorclass
is provided to specify the class name to be applied when an error occurs. As a result, the processing is applied only when an error occurs.
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" th:errorclass="err">
hasErrors
uses th: if
to display a dug only when true
, whether or not an error has occurred in the field specified in the argument.
The error message is displayed using the attribute th: errors
, and in the sample,* {name}
is specified.
【template】
<form method="post" action="/" th:object="${formModel}">
<tr><td><label for="name">name</label></td>
<td><input type="text" name="name"
th:value="*{name}" th:errorclass="err" />
<div th:if="${#fields.hasErrors('name')}"
th:errors="*{name}" th:errorclass="err">
</div></td></tr>
<tr><td><label for="age">age</label></td>
<td><input type="text" name="age"
th:value="*{age}" th:errorclass="err" />
<div th:if="${#fields.hasErrors('age')}"
th:errors="*{age}" th:errorclass="err">
</div></td></tr>
<tr><td><label for="mail">Email</label></td>
<td><input type="text" name="mail"
th:value="*{mail}" th:errorclass="err" />
<div th:if="${#fields.hasErrors('mail')}"
th:errors="*{mail}" th:errorclass="err">
</div></td></tr>
<tr><td><label for="memo">Note</label></td>
<td><textarea name="memo" th:text="*{memo}"
cols="20" rows="5" ></textarea></td></tr>
<tr><td></td><td><input type="submit" /></td></tr>
</form>
Validation annotation | Contents |
---|---|
@ Null | Check that the value is null |
@ NotNull | Do not allow the value to be null |
@ Min(int num) | Specify the minimum value that can be entered in the item for entering a numerical value (integer) |
@ Max(int num) | Specify the maximum value that can be entered in the item for entering a numerical value (integer) |
@ DecimalMin(String num) | BigDecimal,BigInteger,Specifying the minimum value when setting a value with a String value int is also OK |
@ DecimalMax(String num) | BigDecimal,BigInteger,Specifying the maximum value when setting the value with the String value int is also OK |
@ Degits(integer=5, fraction=10) | Limitation on the number of digits in the integer part and the decimal part |
@ Future | Accept only future dates and times from the present |
@ Past | Accept only past roads from the present |
@ Size(min=1, max=10) | Specify the number of stored elements of objects such as arrays and collection classes in addition to String |
@ Pattern(regexp="[a-zA-Z]+") | Check the input by specifying the regular expression pattern |
Validation annotation | Contents |
---|---|
@ NotEmpty | Null and empty string are not allowed |
@ Length(min=5, max=10) | Annotation that specifies the range of string lengths used for Stringt |
@ Range | Used in numeric items to specify the range of minimum and maximum values.@With Min@Max can be put together |
Check if the value entered is an email address | |
@ CreditCardNumber | It is used for items that are entered numerically as numerical values or String values to check whether the credit card number format is used. |
@ EAN | Bar code identification number standard |
How to customize the error message displayed in English
Specify the display message using the value message
when preparing annotations for validation in the entity class
@NotEmpty(message="No white space")
@Email(message="Email address only")
@Min(value=0, message="Zero or above")
@Max(value=200, message="200 or less")
Since maintainability is poor in actual development, messages should be managed by preparing a property file. It can be handled by creating a text file with the name "ValidationMessages.properties" in the resource folder.
For your own validator
Annotation class is
Create an annotation type class that describes the name after @ interface
The validator class is defined by implementing the javax.validation.ConstraintValidator interface. ConstraintValidator
has two methods, ʻinitialize and ʻisValid
, and implement these to describe the necessary processing.
Initialization method Annotation class specified by generic type is passed as an argument Get information about annotations as needed
The part to be validated
The input value (String) and the ConstraintValidator
instance are passed as arguments.
Check the value here and return the boolean
value depending on whether the validation is correct or not.
[Annotation class]
@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() dedault {};
}
[Validation class]
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()-]*");
}
}
By preparing a repository, DB access is possible with almost no code writing, but there are limits, so consider how to use the DB access function provided by SpringDataJPA from within the Spring Boot application.
DataAccessObject When using DB, when you access the page to display the data, prepare the process to retrieve the data in the request handler of the controller and display it in the view. In other words, prepare the necessary DB access processing for each request handler of the controller.
If you write a process for each request, the controller will become bloated, so separate data access and logic. Therefore, prepare an object called DAO (Data Access Object) that provides a means to access the DB. Call these from the controller and perform the necessary processing.
ʻBy preparing a field for storing the EntityManager` class, it provides the functions required to use the entity.
Query
is an object that has a function equivalent to a single query sentence for querying data in SQL.
The query by JPQL is the Query
instance
When createQuery
of EntityManager is called by specifying a query sentence by JPQL as an argument, a Query instance for executing the query is generated.
What is the sample from MyData
? A JPQL query sentence equivalent to select * from mydata
.
Query query = entityManager.createQuery("from MyData");
By the getResultList
method of the created Query, the execution result can be obtained as an instance in the query.
List<MyData> list = query.getResultList();
@ SuppressWarnings
In the sample, the annotation attached to the list
variable suppresses the compile-time warning. By using ʻunchecked` as an argument, it is checked whether the return value of the method is obtained as the value of the type of the storage destination variable. It has nothing to do with the behavior itself, ** to prevent warnings at build time **
@SuppressWarnings("unchecked")
[DAO interface]
public interface MyDataDao <T> extends Serializable {
public List<T> getAll();
}
[DAO implementation class]
@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;
}
}
Create a controller that uses entities in DAO.
@ PersistenceContext
@PersistenceContext
gets the Bean of ʻEntityManager and sets it in the field. In Spring Boot, ʻEntityManager
is automatically registered as a bean, so assign this to the controller field with @PersistenceContext
.
Bean binding using @PersistenceContext
can only be placed up to" ** 1 instance per class ** "
@PersistenceContext
EntityManager entityManager;
The list is fetched from the entity via DAO and the list object is passed to the template side at the same time as setting the template name of the view. If you summarize the access process to the entity in DAO, you can easily call the access process on the controller side.
Iterable<MyData> list = dao.getAll();
mav.addObject("datalist", list);
public class SampleController {
//Repository
@Autowired
MyDataRepository repository;
//entity
@PersistenceContext
EntityManager entityManager;
// DAO
MyDataDaoImpl dao;
@PostConstruct
public void init() {
dao = new MyDataDaoImpl(entityManager);
MyData md = new MyData():
md.setHoge("fuga");
md.setNumber(123);
repository.saveAndFlush(md);
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index() {
mav.setViewName("index");
mav.addObject("msg", "This is a sample of MyData.");
Iterable<MyData> list = dao.getAll();
mav.addObject("datalist", list);
return mav;
}
}
[DAO interface]
public interface MyDataDao<T> extends Serializable {
public List<T> getAll();
//Finds and returns an entity with an ID as an argument
public T findById(long id);
//Find and return an entity by name
public List<T> findByName(String name);
}
[DAO implementation class]
" from MyData where id = "
-> JPQL query (get entity with id as argument)
getSingleResult
retrieves and returns only one entity obtained from Query
For things like ** only one entity is searched ** (such as ID), it is better to return the obtained entity as it is rather than returning it as a List.
@Override
public MyData findById(long id) {
return (MyData)entityManager.createQuery("from MyData where id = "
+ id).getSingleResult();
}
" from MyData where name = "
-> JPQL query (get entity with name as argument)
getResultList
returns a List, so findByName
has List <MyData>
as the return type.
@SuppressWarnings("unchecked")
@Override
public List<MyData> findByName(String name) {
return (List<MyData>)entityManager.createQuery("from MyData where name = "
+ name).getResultList();
}
JPQL is a simple language that allows JPA to generate SQL queries and operate the DB by executing query statements similar to SQL queries.
HttpServletRequest
We have used @RequestParam
as an argument when receiving and processing the submitted value, such as a method that receives a form with POST
, but you can also use HttpServletRequest
as an argument.
Actually, the parameter of @RequestParam
is the one that calls getParameter
of HttpServletRequest
and automatically performs the operation to receive the parameter and sets the result as an argument.
HttpServletResponse
can also be specified as an argument
@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 + "Search results");
mav.addObject("value",param);
List<MyData> list = dao.find(param);
mav.addObject("datalist", list);
}
return mav;
}
What is written in the JPQL query statement in the form of : fstr
of the query" " from MyData where id =: fstr "
"is treated as a ** variable for parameters **.
Subsequent setParameter
of the Query
instance sets the value of the second argument to the variable of the first argument and assigns the value to the previous : fstr
to create a query.
[DAO interface]
public List<T> find(String fstr);
[DAO implementation class]
@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;
}
You can set many parameters in one query by connecting setParameter
(returning Query instance with parameters set) in a method chain.
String qstr = "from MyData where id = :fid or name like :fname or mail like :fmail";
Long fid = 0L;
Query query =
entityManager.createQuery(qstr).setParameter("fid", fid)
.setParameter("fname", "%" + fstr + "%")
.setParameter("fmail", fstr + "@%");
Some parameter variables to be embedded in the query are set by numbering. Set the parameter embedding by specifying a number after "**? **" like ? 1
.
String qstr = "from MyData where id = ?1 or name like ?2 or mail like ?3";
Long fid = 0L;
Query query = entityManager.createQuery(qstr).setParameter(1, fid)
.setParameter(2, "%" + fstr + "%")
.setParameter(3, fstr + "@%");
Named queries can be created using @NamedQuery
. Prepend to the entity class declaration
Name and set the query string
@Entity
@NamedQuery(
name = "findWithName",
query = "from MyData where name like :fname"
)
@Table(name = "mydata")
public class MyData {
...
}
Use @NamedQueries
to combine multiple queries
You can add @NamedQuery
as many times as you like, separated by commas.
@NamedQueries (
@NamedQuery(
name="findWithName",
query="from MyData where name like :fname"
)
)
createNamedQuery
gets a query with that name by specifying the name of the query annotation in the argument, and creates aQuery
instance.
Put the query in the entity class, detached from the DAO code by using a named query
-> When dealing with multiple entities, it is easy to understand by putting queries with the same function in each with the same name
Query query = entityManager
.createQuery("findWithName")
.setParameter("fname", "%" + fstr + "%");
Since it is nonsense to rewrite the entity every time the search function is expanded, I want to prepare a query on the side that actually executes DB access.
Queries can be declared with @Query
in the repository interface
@Query
will use the specified query when calling the method that describes the annotation.
@Repository
public interface MyDataRepository extends JpaRepository<MyData, Long> {
@Query("SELECT d FROM MyData d ORDER BY d.name")
List<MyData> findAllOrderByName();
}
Try calling the repository to execute the query from the controller There is no internal difference because the place to prepare the query is DAO or repository
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
mav.addObject("title","Find Page");
mav.addObject("msg","This is a sample of MyData.");
Iterable<MyData> list = repository.findAllOrderByName(); //dao.getAll();
mav.addObject("datalist", list);
return mav;
}
If you want to do a detailed search, you will need to include the value for the search condition in the query. In such a case, parameters can be prepared in the query text to be set in the query annotation.
@NamedQuery (
name = "findByAge",
query = "from MyData where age > :min and age < :max"
)
Prepare @NamedQuery
in this way before the entity class and call it from DAO by passing the value to the parameter embedded in the query.
Try to actually call findByAge
from DAO
To create a Query
instance, use createNamedQuery
as an argument and specify the name of the query.
Then use setParameter
to pass the value to the parameter of the query text prepared in @ NamedQuery
.
Then search for the entity with getResultList
public List<MyData> findByAge(int min, int max);
@suppresswarning
@Override
public List<MyData> findByAge(int min, int max) {
return (List<MyData>) entityManager
.createNamedQuery("findByAge")
.setParameter("min", min)
.setParameter("max", max)
.getResultList();
}
When using @Query
, the method of embedding a variable in the query text and specifying it with the method argument is the same.
However, use @ Param
to specify which variable is associated with which parameter
Below is a sample when the findByAge
added to @ NamedQuery
is printed in the repository.
[Repository interface]
@Query("from MyData where age > :min and age < :max")
public List<MyData> findByAge(@Param("min") int min, @Param("max") int max);
Whether to have the query in the entity itself or to prepare it in the repository depends on the design of the application.
There is also a function to access DB by method chain without using a language like JPQL-> Criteria API Criteria API uses a combination of three classes
class | What you can do |
---|---|
CriteriaBuilder | Manage query generation |
CriteriaQuery | Query execution |
Root | You can narrow down the entities from here by the root of the searched entity |
① Acquisition of Criteria Builder
Prepare an instance of CriteriaBuilder
and call ʻEntityManager # getCriteriaBuilder`
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
② Creation of Criteria Query Do not take a query statement as an argument Specify the class property of a specific entity as an argument to access it
CriteriaQuery <Entity> query = builder.createQuery(Entity.class);
③ Acquisition of Root Get Root with CriteriaQuery from method Specify the Class property of the entity to be searched in the argument
Root <Entity> root = query.from(Entity.class);
④ Execution of CriteriaQuery method
Calling a method to narrow down entities with CriteriaQuery
Call in the method chain as needed
query.select(root);
⑤ Create Query to get the result
Finally, generate a Query with createQuery
and get the result List
with getResultList
.
Specifying QriteriaQuery
as the argument of createQuery
is different from the search process by normal Query.
List<Entity> list = (List<MyData>)entityManager
.createQuery(query)
.getResultList();
return list;
Below is a sample to get all entities by getAll
method
Specify MyData.class as an argument of from
method to get Root
You can get a Root instance that holds all MyData as information
After getting Root, to get all MyData, call with CriteriaQuery # select
as an argument and specify MyData.class.
After that, if you createQuery
this CriteriaQuery
and getResult
, you can get all Mydata.
@Override
public List<MyData> getAll() {
List<MyData> list = null;
CriteriaBuilder builder =
entityManager.getCriteriaBuilder();
CriteriaQuery<MyData> query =
builder.createQuery(MyData.class);
Root<MyData> root = query.from(MyData.class);
query.select(root);
list = (List<MyData>)entityManager
.createQuery(query)
.getResultList();
return list;
}
Only the entities whose argument text and name value match are searched.
After acquiring the Root instance, perform the process to narrow down the entities to be fetched.
After select (root)
, use the method chain to narrow down the entities using the where
method with Expression (which handles the evaluation of various expressions) as an argument.
Check if they are equal by Expression and Object specified in the argument to ʻequalmethod, and return the result as an instance of
Predicateclass. Since
Predicate has the role of expressing the conditions and expressions specified in the method as objects, for example, in the case of ʻequal
, Predicate
indicating the conditions that are the same as those specified in the argument is prepared, and the entities that match the conditions are narrowed down.
// where(Expression<boolen>)
// equal(Expression, Object)
query.select(root)
.where(builder.equal(root.get("name"), fstr));
Method | Format | Description |
---|---|---|
equal | equal(Expression> x, Expression> y) | Make a predicate to verify that the arguments are equal. |
gt | gt(Expression<? extends java.lang.Number> x, Expression<? extends java.lang.Number> y) | Make a predicate to verify if the first argument is greater than the second argument. |
greaterThan | greaterThan(Expression<? extends Y> x, Expression<? extends Y> y) | Make a predicate to verify if the first argument is greater than the second argument. |
ge | ge(Expression<? extends java.lang.Number> x, Expression<? extends java.lang.Number> y) | Make a predicate to verify if the first argument is greater than or equal to the second argument. |
greaterThanOrEqualTo | greaterThanOrEqualTo(Expression<? extends Y> x, Expression<? extends Y> y) | Make a predicate to verify if the first argument is greater than or equal to the second argument. |
lt | lt(Expression<? extends java.lang.Number> x, Expression<? extends java.lang.Number> y) | Make a predicate to verify if the first argument is less than the second argument. |
lessThan | Predicate | lessThan(Expression<? extends Y> x, Y y) |
le | le(Expression<? extends java.lang.Number> x, java.lang.Number y) | Make a predicate to verify if the first argument is less than or equal to the second argument. |
lessThanOrEqualTo | lessThanOrEqualTo(Expression<? extends Y> x, Y y) | Make a predicate to verify if the first argument is less than or equal to the second argument. |
between | between(Expression<? extends Y> v, Expression<? extends Y> x, Expression<? extends Y> y) | Make a predicate to verify that the first argument is a value between the second and third arguments. |
isNull | isNull(Expression<?> x) | Make a predicate to check if the expression is null. |
isNotNull | isNotNull(Expression<?> x) | Make a predicate that checks if the expression is not null. |
isEmpty | isEmpty(Expression |
Make a predicate to verify if the collection is empty. |
isNotEmpty | isNotEmpty(Expression |
Make a predicate to verify that the collection is not empty. |
like | like(Expression<java.lang.String> x, Expression<java.lang.String> pattern) | Create a predicate that verifies that the expression satisfies the given pattern. |
and | and(Expression<java.lang.Boolean> x, Expression<java.lang.Boolean> y) | Creates the logical product of a given Boolean expression. |
or | or(Expression<java.lang.Boolean> x, Expression<java.lang.Boolean> y) | Creates the OR of the given Boolean expressions. |
not | not(Expression<java.lang.Boolean> restriction) | Make a denial of the given limitation. |
The retrieved result uses the ʻorderBy method to get the entity as
List The argument ʻExpression
uses CriteriaBuilder # get
to specify the Path that indicates the property of the entity.
The argument of ʻorderBy` in the sample sets Predicate to sort the element of name in ascending order.
query.select(root).orderBy(builder.asc(root.get("name")));
The entity get method of Query is [getSingleResult](adds a search method to ### DAO) that returns only one entity and [getResultList](gets the result from ### Query) that returns all entities as List. is there When actually using the DB, you can also specify the position and number to get it.
Specify an integer value as an argument The acquisition position starts with 0 in the list.
query.setFirstResult(int pos);
Specify the number to acquire Only the maximum number that can be obtained is set, so if there are not enough entities, only some will be retrieved.
@Override
public List<MyData> getAll() {
int offset = 1; //Specifying the extraction position
int limit = 2; //Specify the number to be taken out
List<MyData> list = null;
CriteriaBuilder builder =
entityManager.getCriteriaBuilder();
CriteriaQuery<MyData> query =
builder.createQuery(MyData.class);
Root<MyData> root =
query.from(MyData.class);
query.select(root);
list = (List<MyData>)entityManager
.createQuery(query)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
return list;
}
In a DB where multiple tables work in relation to each other, entities are linked and processed by a function called "association". -> Have another entity as a property of the class
@ OneToOne Indicates a one-to-one correspondence between two entities @ OneToMany Multiple of the other entity correspond to one entity @ ManyToOne For multiple entities, only one of the other entities corresponds @ ManyToMany Relationships in which multiple other entities correspond to multiple entities
The sample MsgData
allows one member to post any number of messages, so
Associated with MyData
in @ManyToOne
[Associating entity]
@Entity
@Table(name = "msgdata")
@Getter
@Setter
public class MsgData {
@Id
@Column
@NotNull
private long id;
@Column
private String title;
@Column(nullable = false)
@NotEmpty
private String message;
@ManyToOne
private MyData mydata;
//Below, the constructor and accessor are omitted.
}
[Associated entity] Add msgdatas property to associate with MsgData
@Entity
@Table(name = "mydata")
@Getter
@Setter
public class MyData {
@OneToMany(cascade = CascadeType.ALL)
@Column(nullable = true)
private List<MsgData> msgdatas;
}
In the repository save
, when saving, if there is a field associated with another entity, it will get an instance based on the sent information and set it automatically.
Therefore, it is not necessary for the programmer to implement the registration process of related records in consideration of the linkage of entities.
repository.saveAndFlush(msgdata);
In business logic, the part that is componentized so that it can be accessed from the application is generally called "** service layer **", and unlike DAO that uses a controller or model, it is a class that can be called and used from anywhere.
What can be freely called from both the controller and business logic (model) is ** service **
Spring Framework can be used at any time simply by registering this service as a bean and writing annotations.
○ Application layer
·controller
Request handler
·model
Business logic
entity
○ Domain layer
·service
・ Repository
@ Service Annotation for registering this class as a service, and the service class puts this annotation before the class name.
@ PersisitenceContext
It is for automatically assigning the Bean of ʻEntityManager, and it is also possible to prepare and use ʻEntityManager
in the service instead of on the controller class side.
@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;
}
}
DB access using service bean from DAO
The created service is used as a bean by associating it with the controller with @Autowired
.
The service class in which @Service
is described is beanized in the application and it is assigned to the field by @ Autowired
.
@Controller
public class SampleController {
@Autowired
MyDataRepository repository;
//Associate a service bean with a field
@Autowired
private MyDataService service;
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
mav.addObject("title","Find Page");
mav.addObject("msg","This is a sample of MyData.");
//Get all entities
List<MyData> list = service.getAll();
mav.addObject("datalist", list);
return mav;
}
// "/find"GET request handler omitted
@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 + "Search results");
mav.addObject("value", param);
//Search for entities
List<MyData> list = service.find(param);
mav.addObject("datalist", list);
}
return mav;
}
//The constructor, accessor, etc. are omitted below.
}
"** Service " in Web application development generally means " Web program that can be accessed from the outside and receive necessary information **". There are JSON format and XML format REST services as services that can be used from the outside.
@RestController
public class MyDataRestController {
@Autowired
private MyDataService service;
@RequestMapping("/rest")
public List<MyData> restAll() {
return service.getAll();
}
@RequestMapping("/rest/{num}")
public MyData restBy(@PathVariable int num) {
return service.get(num);
}
}
When handling with XML in RestController, prepare a library "Jackson DataFormat XML" that analyzes and processes the data format.
@ XmlRootElement
Indicates that it is the root element in XML data. Request handler restAll The only difference is that it returns an object with
or restBy
and is normally converted to JSON format, and with @XmlRootElement
it is converted to XML format.
If you are using Maven
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
** Components ** are beans that are automatically generated in your application, and bean instances can be used by binding with @Autowired
.
A service is a type of component-> a component of the service layer is called a ** service **
@ Component Annotation makes the class to which it is attached recognized by the application as a component. Class instance is now registered as a bean
If @Autowired
is attached to the constructor, the constructor with this @ Autowired
will create the instance when the instance of that class is registered as a bean.
If the constructor with @ Autowired
is not prepared in the component class, an error will occur when the application is executed and the startup will fail, so be sure to prepare it.
The instance argument of MySampleBean (ApplicationArguments args)
is an object that manages the argument passed when the application is executed, that is, when SpringApplication.run
is executed in the class with @ SpringBootApplication
specified.
Prepare this instance as an argument when using the argument passed at runtime
In the sample method, use ʻApplicationArgments # getNonOptionArgs to extract the application runtime argument as
List`.
@Component
public class MySampleBean {
private int max = 10;
@Autowired
public MySampleBean(ApplicationArguments args) {
List<String> files = args.getNonOptionArgs();
try {
max = Integer.parseInt(files.get(0));
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
public int count() {
counter++;
counter = counter > max ? 0 : counter;
return counter;
}
}
Add a request handler to the controller class to use the component
If you associate it with a field with @ Autowired
, it will be automatically bound, so you can execute bean.count
in the request handler.
Components can be freely used from anywhere by associating them with fields with @ Autowired
.
If there is a general-purpose function to be called from several request handlers, it is good to summarize it for the time being
@Autowired
MySampleBean bean;
@RequestMapping("/count")
public int count() {
return bean.count();
}
Also, @ Controller
is a component that acts as a controller, @ Repository
is a type of component that has a special role for data access, and @ Component
has such a special role. Can be attached to something that is not
In Spring, you can bind a bean prepared as standard using annotations such as @Autowired
, or create your own ** component ** or ** service ** and use it as a bean.
Bean can be used in various places such as controller by using ** configuration class **
Actually, due to Spring Boot's idea of ** from configuration file to annotation **, the method of ** creating a configuration file ** is not recommended.
If you add @Configuration
even to a general class that does not inherit a special class, this class will be instantiated as a configuration class when the application is started, and the beans etc. described there will be registered in the application.
[Configuration class with beans registered in the field]
@Configuration
public class MyBootAppConfig {
@Bean
MyDataBean myDataBean(){
return new MyDataBean();
}
}
If you prepare a method that returns an instance of the class with @Bean
in the configuration class, it will be registered as a bean.
[Original Bean class]
public class MyDataBean {
@Autowired
MyDataRepository repository;
public String getTableTagById(Long id){
Optional<MyData> opt = repository.findById(id);
MyData data = opt.get();
String result = "<tr><td>" + data.getName()
+ "</td><td>" + data.getMail() +
"</td><td>" + data.getAge() +
"</td><td>" + data.getMemo() +
"</td></tr>";
return result;
}
}
With the bean ready in your application, @Autowired
will automatically bind that instance.
[Controller associated with the field to use the bean]
@Autowired
MyDataBean myDataBean;
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView indexById(@PathVariable long id,
ModelAndView mav) {
mav.setViewName("pickup");
mav.addObject("title","Pickup Page");
String table = "<table>"
+ myDataBean.getTableTagById(id)
+ "</table>";
mav.addObject("msg","pickup data id = " + id);
mav.addObject("data",table);
return mav;
}
Spring Framework provides ** Page ** class for page division, and by using this, pagination, which is a function to handle data for each page, can be realized.
By specifying parameters, you can specify the number of records on the page and the page number to be displayed (example of 2 data / page, which is the 0th page).
【how to access】
/page?size=2&page=0
The findAll
method, which is a request handler, takes aPagable
as an argument, fetches a Page
instance, and passes it to the template with the name datalist
.
[Controller with request handler that implements pagination]
@RequestMapping(value = "/page", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav, Pageable pageable) {
mav.setViewName("index");
mav.addObject("title","Find Page");
mav.addObject("msg","This is a sample of MyData.");
Page<MyData> list = repository.findAll(pageable); //●
mav.addObject("datalist", list);
return mav;
}
Since Page
is a subclass of List
, the entity passed to the template can be displayed in the table in the same way as List
.
【template】
<table>
<tr><th>ID</th><th>name</th><th>Email</th><th>age</th><th>Note(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>
</table>
Method | return |
---|---|
Pageable#getPageNumber() | Current page number |
Pageable#getPageSize() | Number of records to display on the page |
Pageable#first() | First Pageable |
Pageable#previousOrFirst() | Previous or first Pageable |
Pageable#next() | Next Pageable |
If you take the entities separated by pagination, you need to move to the front and back of the page and prepare functions related to page display, so define it as a Java class using ** utility object ** You can call the internal method from within Thymeleaf to output the result.
To use utility objects, prepare a ʻAttributeTagProcessor class that processes Thymeleaf attributes and a
Dialectclass to combine this class. When
th: text =" name "and Thymeleaf's unique attribute are described, the value
name` is received and processed as the processing of the attribute called text, so prepare the specific processing.
ʻThe constructor and doProcess
are prepared in the class created by inheriting AbstractAttributeTagProcessor`.
The arguments passed to this method are as follows
Argument type | Description |
---|---|
ITemplatecontext | Handle template context. Extract template information such as template engine and setting information from here. |
IProcessableElementTag | Information such as attributes embedded in tags can be retrieved in the class that handles element tags. |
AttributeName | Handles attribute names and prefixes (text that precedes values) |
String | Attribute value |
IElementTagStructureHandler | A class that handles structures such as incorporating attributes into elements |
When Thymeleaf variable expression etc. is described in the value Process the variable expression using the function to evaluate the value ** resolver ** built in the template engine, and then obtain the resulting value. Since it is necessary to perform the actual attribute processing, the sample below is performed with doProcess
.
[Inheritance class of AbstractAttributeTagProcessor]
public class MyPageAttributeTagProcessor extends AbstractAttributeTagProcessor {
private static final String ATTR_NAME = "mypage";
private static final int PRECEDENCE = 10000;
public static int size = 2;
public MyPageAttributeTagProcessor(final String dialectPrefix) {
super(TemplateMode.HTML, dialectPrefix, null,
false, ATTR_NAME, true, PRECEDENCE, true);
}
protected MyPageAttributeTagProcessor(TemplateMode templateMode,
String dialectPrefix, String elementName,
boolean prefixElementName,
String attributeName,
boolean prefixAttributeName,
int precedence,
boolean removeAttribute) {
super(templateMode, dialectPrefix, elementName,
prefixElementName, attributeName, prefixAttributeName,
precedence,removeAttribute);
}
@Override
protected void doProcess(ITemplateContext context,
IProcessableElementTag tag,
AttributeName attrName,
String attrValue,
IElementTagStructureHandler handler) {
final IEngineConfiguration configuration = context.getConfiguration();
final IStandardExpressionParser parser =
StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression =
parser.parseExpression(context, attrValue);
int value = (int)expression.execute(context);
value = value < 0 ? 0 : value;
handler.setAttribute("href", "/page?size=" + size + "&page=" + value);
}
}
ʻAbstractProcessorDialect has a method called
getProcessors that gets a set that summarizes ʻIProcessor
.
ʻIProcessoris a class that processes Thymeleaf attributes, etc. The sample creates a
Set instance, incorporates the ʻI Processor
, and performs a return
process.
[Summary as an attribute as a Dialect class]
public class MyDialect extends AbstractProcessorDialect {
private static final String DIALECT_NAME = "My Dialect";
public MyDialect() {
super(DIALECT_NAME, "my", StandardDialect.PROCESSOR_PRECEDENCE);
}
protected MyDialect(String name, String prefix,
int processorPrecedence) {
super(name, prefix, processorPrecedence);
}
@Override
public Set<IProcessor> getProcessors(String dialectPrefix) {
final Set<IProcessor> processors = new HashSet<IProcessor>();
processors.add(new MyPageAttributeTagProcessor(dialectPrefix));
return processors;
}
}
Register and use Bean
to use ʻAttributeTagProcessor and
Dialect`
① Create template Engine
After creating the SpringTemplateEngine
instance, set the template resolver with setTemplateResolver
.
This sets the template resolver created by templateResolver
If you embed the instance of MyDialect created in the prepared template engine with ʻaddDialect and
return` the template engine, it will be used for rendering.
② Create templateResolver
Create an instance of ClassLoaderTemplateResolver
and set the prefix, set whether to enable caching, set the suffix, set the template mode, etc.
[Structure class]
@Configuration
public class MyBootAppConfig {
...
@Bean
public ClassLoaderTemplateResolver templateResolver() {
ClassLoaderTemplateResolver templateResolver =
new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new MyDialect());
return templateEngine;
}
}
【template】
<div>
<a my:mypage="${datalist.getNumber() - 1}"><<prev</a>
|
<a my:mypage="${datalist.getNumber() + 1}">next>></a>
</div>
The basic usage of NoSQL MongoDB is almost the same as SQL database because Mongo Repository is designed in the same way as Jpa Repository-> ** Write and call repository **
Omitted
-[Introduction to SpringBoot2 Programming](https://www.amazon.co.jp/Spring-Boot-2-Introduction to Programming-Yano Palm Tatsu / dp / 4798053473)
Recommended Posts