mirror of
https://github.com/OtusTeam/Spring.git
synced 2026-05-30 10:50:42 +00:00
2025-05 spring-16-view
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>spring-mvc-view-exercise</module>
|
||||
<module>spring-mvc-view-demo</module>
|
||||
<module>spring-mvc-view-solution1</module>
|
||||
<module>spring-mvc-view-solution2</module>
|
||||
<module>spring-mvc-view-solution3</module>
|
||||
<module>spring-mvc-view-solution4</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view-demo</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<h2.version>2.2.220</h2.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
<!--<hibernate.validator.version>7.0.2.Final</hibernate.validator.version>-->
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.otus.spring;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class);
|
||||
System.out.printf("Чтобы перейти на страницу сайта открывай: %n%s%n",
|
||||
"http://localhost:8080");
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package ru.otus.spring.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@Configuration
|
||||
public class LocalizationConfig implements WebMvcConfigurer {
|
||||
|
||||
@Bean(name = "localeResolver")
|
||||
public LocaleResolver localeResolver() {
|
||||
var resolver = new CookieLocaleResolver("locale");
|
||||
resolver.setDefaultLocale(new Locale("en"));
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocaleChangeInterceptor localeChangeInterceptor() {
|
||||
var localeChangeInterceptor = new LocaleChangeInterceptor();
|
||||
localeChangeInterceptor.setParamName("lang");
|
||||
return localeChangeInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(localeChangeInterceptor());
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private final MessageSource messageSource;
|
||||
|
||||
@ExceptionHandler(NotFoundException.class)
|
||||
public ModelAndView handeNotFoundException(NotFoundException ex) {
|
||||
String errorText = messageSource.getMessage("person-not-found-error", null,
|
||||
LocaleContextHolder.getLocale());
|
||||
return new ModelAndView("customError", "errorText", errorText);
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
public class NotFoundException extends RuntimeException{
|
||||
|
||||
NotFoundException() {
|
||||
super("Person not found");
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.otus.spring.dto.PersonDto;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class PersonController {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String listPage(Model model) {
|
||||
List<PersonDto> persons = repository.findAll().stream()
|
||||
.map(PersonDto::fromDomainObject).toList();
|
||||
model.addAttribute("persons", persons);
|
||||
return "list";
|
||||
}
|
||||
|
||||
@GetMapping("/edit")
|
||||
public String editPage(@RequestParam("id") long id, Model model) {
|
||||
PersonDto person = repository.findById(id)
|
||||
.map(PersonDto::fromDomainObject)
|
||||
.orElseThrow(NotFoundException::new);
|
||||
model.addAttribute("person", person);
|
||||
return "edit";
|
||||
}
|
||||
|
||||
@PostMapping("/edit")
|
||||
public String savePerson(@Valid @ModelAttribute("person") PersonDto person,
|
||||
BindingResult bindingResult,
|
||||
@RequestParam(value = "hobby", defaultValue = "") List<String> hobby) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return "edit";
|
||||
}
|
||||
|
||||
log.debug("Hobby from plain RequestParam: {}", String.join(", ", hobby));
|
||||
log.debug("Hobby from DTO: {}", person.hobbyAsString());
|
||||
|
||||
repository.save(person.toDomainObject());
|
||||
return "redirect:/";
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package ru.otus.spring.domain;
|
||||
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.FetchType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
private List<String> hobby;
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package ru.otus.spring.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.util.CollectionUtils.isEmpty;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PersonDto {
|
||||
|
||||
private long id;
|
||||
|
||||
@NotBlank(message = "{name-field-should-not-be-blank}")
|
||||
@Size(min = 2, max = 10, message = "{name-field-should-has-expected-size}")
|
||||
private String name;
|
||||
private List<String> hobby;
|
||||
|
||||
public String hobbyAsString() {
|
||||
if (isEmpty(hobby)){
|
||||
return "";
|
||||
}
|
||||
return String.join(", ", hobby);
|
||||
}
|
||||
|
||||
public Person toDomainObject(){
|
||||
return new Person(id, name, hobby);
|
||||
}
|
||||
|
||||
public static PersonDto fromDomainObject(Person person) {
|
||||
return new PersonDto(person.getId(), person.getName(), person.getHobby());
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.spring.repostory;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findAll();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
spring:
|
||||
messages:
|
||||
encoding: UTF-8
|
||||
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
|
||||
jpa:
|
||||
open-in-view: false
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
ru.otus.spring.controller: DEBUG
|
||||
@@ -0,0 +1,10 @@
|
||||
insert into person (id, name) values (1, 'Pushkin');
|
||||
insert into person (id, name) values (2, 'Lermontov');
|
||||
|
||||
insert into person_hobby (person_id, hobby)
|
||||
values
|
||||
(1, 'Fishing'),
|
||||
(1, 'Poetry'),
|
||||
(2, 'Traveling'),
|
||||
(2, 'Poetry')
|
||||
;
|
||||
@@ -0,0 +1,16 @@
|
||||
lang-switcher-header=Select language
|
||||
en-lang-switch-button-caption=Language - EN
|
||||
ru-lang-switch-button-caption=Language - RU
|
||||
persons-table-header=Persons:
|
||||
persons-table-column-action=Action
|
||||
person-field-id=ID
|
||||
person-field-name=Name
|
||||
person-field-hobby=Hobby
|
||||
edit-button-caption=Edit
|
||||
person-form-header=Person Info:
|
||||
save-button-caption=Save
|
||||
cancel-button-caption=Cancel
|
||||
name-field-should-not-be-blank=Name field should not be blank
|
||||
name-field-should-has-expected-size=Name field should be between 2 and 10 characters
|
||||
error-text-header=Error
|
||||
person-not-found-error=Person not found
|
||||
@@ -0,0 +1,16 @@
|
||||
lang-switcher-header=Select language
|
||||
en-lang-switch-button-caption=Language - EN
|
||||
ru-lang-switch-button-caption=Language - RU
|
||||
persons-table-header=Persons:
|
||||
persons-table-column-action=Action
|
||||
person-field-column-id=ID
|
||||
person-field-name=Name
|
||||
person-field-hobby=Hobby
|
||||
edit-button-caption=Edit
|
||||
person-form-header=Person Info:
|
||||
save-button-caption=Save
|
||||
cancel-button-caption=Cancel
|
||||
name-field-should-not-be-blank=Name field should not be blank
|
||||
name-field-should-has-expected-size=Name field should be between 2 and 10 characters
|
||||
error-text-header=Error
|
||||
person-not-found-error=Person not found
|
||||
@@ -0,0 +1,16 @@
|
||||
lang-switcher-header=\u0412\u044B\u0431\u043E\u0440 \u044F\u0437\u044B\u043A\u0430
|
||||
en-lang-switch-button-caption=\u042F\u0437\u044B\u043A - EN
|
||||
ru-lang-switch-button-caption=\u042F\u0437\u044B\u043A - RU
|
||||
persons-table-header=\u041F\u0451\u0440\u0441\u043E\u043D\u044B:
|
||||
persons-table-column-action=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435
|
||||
person-field-id=\u0410\u0439\u0414\u0438
|
||||
person-field-name=\u0418\u043C\u044F
|
||||
person-field-hobby=\u0425\u043E\u0431\u0431\u0438
|
||||
edit-button-caption=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C
|
||||
person-form-header=\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u043F\u0451\u0440\u0441\u043E\u043D\u0435:
|
||||
save-button-caption=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C
|
||||
cancel-button-caption=\u041E\u0442\u043C\u0435\u043D\u0430
|
||||
name-field-should-not-be-blank=\u0418\u043C\u044F \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u044B\u043C
|
||||
name-field-should-has-expected-size=\u0414\u043B\u0438\u043D\u0430 \u0438\u043C\u0435\u043D\u0438 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043E\u0442 2 \u0434\u043E 10 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432
|
||||
error-text-header=\u041E\u0448\u0438\u0431\u043A\u0430
|
||||
person-not-found-error=\u041F\u0451\u0440\u0441\u043E\u043D \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D
|
||||
@@ -0,0 +1,11 @@
|
||||
create table person (
|
||||
id integer generated by default as identity,
|
||||
name varchar(255),
|
||||
hobby varchar(500),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table person_hobby (
|
||||
person_id integer references person(id),
|
||||
hobby varchar(255)
|
||||
);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 236 B |
+34
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
background-image: url("../static/listmark.png");
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style type="text/css" th:inline="text">
|
||||
[[h3]] {
|
||||
background-image: url([[@{/listmark.png}]]);
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3 th:text="#{error-text-header}">Error text</h3>
|
||||
<span th:text="${errorText}">Error text</span>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Edit person</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
background-image: url("../static/listmark.png");
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.errors {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style type="text/css" th:inline="text">
|
||||
[[h3]] {
|
||||
background-image: url([[@{/listmark.png}]]);
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Person edition -->
|
||||
<form id="edit-form" action="edit.html" th:action="@{/edit(id=${person.id})}" th:method="post" th:object="${person}">
|
||||
<h3 th:text = "#{person-form-header}">Person Info:</h3>
|
||||
|
||||
<div class="row">
|
||||
<label for="id-input" th:text="#{person-field-id} + ':'">ID:</label>
|
||||
<input id="id-input" type="text" readonly="readonly" name="id" th:value="*{id}" value="1"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-name-input" th:text="#{person-field-name} + ':'">Name:</label>
|
||||
<input id="person-name-input" name="name" type="text" th:value="*{name}" value="John Doe"/>
|
||||
<div class="errors" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Wrong person name error</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-hobby-select" th:text="#{person-field-hobby} + ':'">Hobby:</label>
|
||||
<select id="person-hobby-select" name="hobby" multiple>
|
||||
<option value="Fishing" th:selected="*{hobby.contains('Fishing')}">Fishing</option>
|
||||
<option value="Poetry" th:selected="*{hobby.contains('Poetry')}">Poetry</option>
|
||||
<option value="Traveling" th:selected="*{hobby.contains('Traveling')}">Traveling</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit" th:text="#{save-button-caption}">Save</button>
|
||||
<a href="list.html" th:href="@{/}"><button type="button" th:text="#{cancel-button-caption}">Cancel</button></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.persons {
|
||||
border: 1px solid steelblue;
|
||||
width: 300px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.persons tr td, th {
|
||||
padding: 5px;
|
||||
border: 1px solid steelblue;
|
||||
}
|
||||
|
||||
.persons td:last-child, td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
background-image: url("../static/listmark.png");
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style type="text/css" th:inline="text">
|
||||
[[h3]] {
|
||||
background-image: url([[@{/listmark.png}]]);
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3 th:text="#{lang-switcher-header}">Select language</h3>
|
||||
<div>
|
||||
<div><a href="#" th:href="@{/(lang=en)}" th:text="#{en-lang-switch-button-caption}">Language - EN</a></div>
|
||||
<div><a href="#" th:href="@{/(lang=ru)}" th:text="#{ru-lang-switch-button-caption}">Language - RU</a></div>
|
||||
</div>
|
||||
|
||||
<h3 th:text="#{persons-table-header}">Persons:</h3>
|
||||
|
||||
<table class="persons">
|
||||
<thead>
|
||||
<tr>
|
||||
<th th:text="#{person-field-id}">ID</th>
|
||||
<th th:text="#{person-field-name}">Name</th>
|
||||
<th th:text="#{person-field-hobby}">Hobby</th>
|
||||
<th th:text="#{persons-table-column-action}">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="person : ${persons}">
|
||||
<td th:text="${person.id}">1</td>
|
||||
<td th:text="${person.name}">John Doe</td>
|
||||
<td th:text="${person.hobbyAsString()}">Hobby1, hobby2</td>
|
||||
<td>
|
||||
<a th:href="@{/edit(id=${person.id})}" href="edit.html" th:text="#{edit-button-caption}">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import ru.otus.spring.domain.Person;
|
||||
import ru.otus.spring.dto.PersonDto;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
||||
|
||||
@WebMvcTest(PersonController.class)
|
||||
class PersonControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mvc;
|
||||
|
||||
@MockitoBean
|
||||
private PersonRepository personRepository;
|
||||
|
||||
private List<Person> persons = List.of(new Person(1L, "Vasya", List.of()),
|
||||
new Person(2L, "Dima", List.of()));
|
||||
|
||||
@Test
|
||||
void shouldRenderListPageWithCorrectViewAndModelAttributes() throws Exception {
|
||||
when(personRepository.findAll()).thenReturn(persons);
|
||||
List<PersonDto> expectedPersons = persons.stream()
|
||||
.map(PersonDto::fromDomainObject).toList();
|
||||
mvc.perform(get("/"))
|
||||
.andExpect(view().name("list"))
|
||||
.andExpect(model().attribute("persons", expectedPersons));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderEditPageWithCorrectViewAndModelAttributes() throws Exception {
|
||||
when(personRepository.findById(1L)).thenReturn(Optional.of(persons.get(0)));
|
||||
PersonDto expectedPerson = PersonDto.fromDomainObject(persons.get(0));
|
||||
mvc.perform(get("/edit").param("id", "1"))
|
||||
.andExpect(view().name("edit"))
|
||||
.andExpect(model().attribute("person", expectedPerson));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderErrorPageWhenPersonNotFound() throws Exception {
|
||||
when(personRepository.findById(1L)).thenThrow(new NotFoundException());
|
||||
mvc.perform(get("/edit").param("id", "1"))
|
||||
.andExpect(view().name("customError"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSavePersonAndRedirectToContextPath() throws Exception {
|
||||
when(personRepository.findById(1L)).thenReturn(Optional.of(persons.get(0)));
|
||||
mvc.perform(post("/edit").param("id", "3").param("name", "Olya"))
|
||||
.andExpect(view().name("redirect:/"));
|
||||
verify(personRepository, times(1)).save(any(Person.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view-exercise</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<h2.version>2.2.220</h2.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package ru.otus.spring;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class);
|
||||
System.out.printf("Чтобы проверить себя открывай: %n%s%n%s%n",
|
||||
"http://localhost:8080", "http://localhost:8080/edit?id=1");
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
class NotFoundException extends RuntimeException{
|
||||
|
||||
NotFoundException() {
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.otus.spring.domain.Person;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class PersonController {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String listPage(Model model) {
|
||||
List<Person> persons = repository.findAll();
|
||||
model.addAttribute("persons", persons);
|
||||
return "list";
|
||||
}
|
||||
|
||||
@GetMapping("/edit")
|
||||
public String editPage(@RequestParam("id") long id, Model model) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ru.otus.spring.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.spring.repostory;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findAll();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
|
||||
jpa:
|
||||
open-in-view: false
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into person (id, name) values (1, 'Pushkin');
|
||||
insert into person (id, name) values (2, 'Lermontov');
|
||||
@@ -0,0 +1,4 @@
|
||||
create table person (
|
||||
id integer generated by default as identity,
|
||||
name varchar(255), primary key (id)
|
||||
);
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Edit person</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Person edition -->
|
||||
<form id="edit-form" action="edit.html">
|
||||
<h3>Person Info:</h3>
|
||||
|
||||
<div class="row">
|
||||
<label for="id-input">ID:</label>
|
||||
<input id="id-input" type="text" readonly="readonly" value="1"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-name-input">Name:</label>
|
||||
<input id="person-name-input" name="name" type="text" value="John Doe"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit">Save</button>
|
||||
<a href="list.html"><button type="button">Cancel</button></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.persons {
|
||||
border: 1px solid steelblue;
|
||||
width: 300px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.persons tr td, th {
|
||||
padding: 5px;
|
||||
border: 1px solid steelblue;
|
||||
}
|
||||
|
||||
.persons td:last-child, td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Persons:</h3>
|
||||
|
||||
<table class="persons">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>John Doe</td>
|
||||
<td>
|
||||
<a href="edit.html">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view-solution1</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<h2.version>2.2.220</h2.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.spring;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
//http://localhost:8080/edit?id=1
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
class NotFoundException extends RuntimeException{
|
||||
|
||||
NotFoundException() {
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.otus.spring.domain.Person;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class PersonController {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String listPage(Model model) {
|
||||
List<Person> persons = repository.findAll();
|
||||
model.addAttribute("persons", persons);
|
||||
return "list";
|
||||
}
|
||||
|
||||
@GetMapping("/edit")
|
||||
public String editPage(@RequestParam("id") long id, Model model) {
|
||||
Person person = repository.findById(id)
|
||||
.orElseThrow(NotFoundException::new);
|
||||
model.addAttribute("person", person);
|
||||
return "edit";
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ru.otus.spring.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.spring.repostory;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findAll();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
|
||||
jpa:
|
||||
open-in-view: false
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into person (id, name) values (1, 'Pushkin');
|
||||
insert into person (id, name) values (2, 'Lermontov');
|
||||
@@ -0,0 +1,4 @@
|
||||
create table person (
|
||||
id integer generated by default as identity,
|
||||
name varchar(255), primary key (id)
|
||||
);
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Edit person</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Person edition -->
|
||||
<form id="edit-form" action="edit.html">
|
||||
<h3>Person Info:</h3>
|
||||
|
||||
<div class="row">
|
||||
<label for="id-input">ID:</label>
|
||||
<input id="id-input" type="text" readonly="readonly" value="1" th:value="${person.id}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-name-input">Name:</label>
|
||||
<input id="person-name-input" name="name" type="text" value="John Doe" th:value="${person.name}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit">Save</button>
|
||||
<a href="list.html"><button type="button">Cancel</button></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.persons {
|
||||
border: 1px solid steelblue;
|
||||
width: 300px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.persons tr td, th {
|
||||
padding: 5px;
|
||||
border: 1px solid steelblue;
|
||||
}
|
||||
|
||||
.persons td:last-child, td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h3>Persons:</h3>
|
||||
|
||||
<table class="persons">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>John Doe</td>
|
||||
<td>
|
||||
<a href="edit.html">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view-solution2</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<h2.version>2.2.220</h2.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.spring;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
//http://localhost:8080/edit?id=1
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
class NotFoundException extends RuntimeException{
|
||||
|
||||
NotFoundException() {
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.otus.spring.domain.Person;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class PersonController {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String listPage(Model model) {
|
||||
List<Person> persons = repository.findAll();
|
||||
model.addAttribute("persons", persons);
|
||||
return "list";
|
||||
}
|
||||
|
||||
@GetMapping("/edit")
|
||||
public String editPage(@RequestParam("id") long id, Model model) {
|
||||
Person person = repository.findById(id).orElseThrow(NotFoundException::new);
|
||||
model.addAttribute("person", person);
|
||||
return "edit";
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ru.otus.spring.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.spring.repostory;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findAll();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
|
||||
jpa:
|
||||
open-in-view: false
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into person (id, name) values (1, 'Pushkin');
|
||||
insert into person (id, name) values (2, 'Lermontov');
|
||||
@@ -0,0 +1,4 @@
|
||||
create table person (
|
||||
id integer generated by default as identity,
|
||||
name varchar(255), primary key (id)
|
||||
);
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Edit person</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Person edition -->
|
||||
<form id="edit-form" action="edit.html" th:object="${person}">
|
||||
<h3>Person Info:</h3>
|
||||
|
||||
<div class="row">
|
||||
<label for="id-input">ID:</label>
|
||||
<input id="id-input" type="text" readonly="readonly" value="1" th:value="*{id}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-name-input">Name:</label>
|
||||
<input id="person-name-input" name="name" type="text" value="John Doe" th:value="*{name}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit">Save</button>
|
||||
<a href="list.html"><button type="button">Cancel</button></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.persons {
|
||||
border: 1px solid steelblue;
|
||||
width: 300px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.persons tr td, th {
|
||||
padding: 5px;
|
||||
border: 1px solid steelblue;
|
||||
}
|
||||
|
||||
.persons td:last-child, td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Persons:</h3>
|
||||
|
||||
<table class="persons">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>John Doe</td>
|
||||
<td>
|
||||
<a href="edit.html">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view-solution3</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<h2.version>2.2.220</h2.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package ru.otus.spring;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
//http://localhost:8080
|
||||
//http://localhost:8080/edit?id=1
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
class NotFoundException extends RuntimeException{
|
||||
|
||||
NotFoundException() {
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.otus.spring.domain.Person;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class PersonController {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String listPage(Model model) {
|
||||
List<Person> persons = repository.findAll();
|
||||
model.addAttribute("persons", persons);
|
||||
return "list";
|
||||
}
|
||||
|
||||
@GetMapping("/edit")
|
||||
public String editPage(@RequestParam("id") long id, Model model) {
|
||||
Person person = repository.findById(id).orElseThrow(NotFoundException::new);
|
||||
model.addAttribute("person", person);
|
||||
return "edit";
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ru.otus.spring.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.spring.repostory;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findAll();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
|
||||
jpa:
|
||||
open-in-view: false
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into person (id, name) values (1, 'Pushkin');
|
||||
insert into person (id, name) values (2, 'Lermontov');
|
||||
@@ -0,0 +1,4 @@
|
||||
create table person (
|
||||
id integer generated by default as identity,
|
||||
name varchar(255), primary key (id)
|
||||
);
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 236 B |
+48
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Edit person</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Person edition -->
|
||||
<form id="edit-form" action="edit.html" th:object="${person}">
|
||||
<h3>Person Info:</h3>
|
||||
|
||||
<div class="row">
|
||||
<label for="id-input">ID:</label>
|
||||
<input id="id-input" type="text" readonly="readonly" value="1" th:value="*{id}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-name-input">Name:</label>
|
||||
<input id="person-name-input" name="name" type="text" value="John Doe" th:value="*{name}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit">Save</button>
|
||||
<a href="list.html"><button type="button">Cancel</button></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.persons {
|
||||
border: 1px solid steelblue;
|
||||
width: 300px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.persons tr td, th {
|
||||
padding: 5px;
|
||||
border: 1px solid steelblue;
|
||||
}
|
||||
|
||||
.persons td:last-child, td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Persons:</h3>
|
||||
|
||||
<table class="persons">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="person : ${persons}" th:object = "${person}">
|
||||
<td th:text="*{id}">1</td>
|
||||
<td th:text="*{name}">John Doe</td>
|
||||
<td>
|
||||
<a href="edit.html">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-mvc-view-solution4</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<h2.version>2.2.220</h2.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package ru.otus.spring;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
//http://localhost:8080
|
||||
//http://localhost:8080/edit?id=1
|
||||
//http://localhost:8080/edit?id=111
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(NotFoundException.class)
|
||||
public ModelAndView handeNotFoundException(NotFoundException ex) {
|
||||
return new ModelAndView("customError",
|
||||
"errorText", "Person not found");
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
public class NotFoundException extends RuntimeException{
|
||||
|
||||
NotFoundException() {
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package ru.otus.spring.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.otus.spring.domain.Person;
|
||||
import ru.otus.spring.repostory.PersonRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class PersonController {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String listPage(Model model) {
|
||||
List<Person> persons = repository.findAll();
|
||||
model.addAttribute("persons", persons);
|
||||
return "list";
|
||||
}
|
||||
|
||||
@GetMapping("/edit")
|
||||
public String editPage(@RequestParam("id") long id, Model model) {
|
||||
Person person = repository.findById(id).orElseThrow(NotFoundException::new);
|
||||
model.addAttribute("person", person);
|
||||
return "edit";
|
||||
}
|
||||
|
||||
@PostMapping("/edit")
|
||||
public String savePerson(Person person) {
|
||||
repository.save(person);
|
||||
return "redirect:/";
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ru.otus.spring.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.spring.repostory;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import ru.otus.spring.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findAll();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
|
||||
|
||||
jpa:
|
||||
open-in-view: false
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into person (id, name) values (1, 'Pushkin');
|
||||
insert into person (id, name) values (2, 'Lermontov');
|
||||
@@ -0,0 +1,4 @@
|
||||
create table person (
|
||||
id integer generated by default as identity,
|
||||
name varchar(255), primary key (id)
|
||||
);
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
background-image: url("../static/listmark.png");
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style type="text/css" th:inline="text">
|
||||
[[h3]] {
|
||||
background-image: url([[@{/listmark.png}]]);
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Error:</h3>
|
||||
<span th:text="${errorText}">Error text</span>
|
||||
</body>
|
||||
</html>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Edit person</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Person edition -->
|
||||
<form id="edit-form" action="edit.html" th:method="post" th:action="@{/edit(id=${person.id})}"
|
||||
th:object="${person}">
|
||||
<h3>Person Info:</h3>
|
||||
|
||||
<div class="row">
|
||||
<label for="id-input">ID:</label>
|
||||
<input id="id-input" type="text" readonly="readonly" value="1" th:value="*{id}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="person-name-input">Name:</label>
|
||||
<input id="person-name-input" name="name" type="text" value="John Doe" th:value="*{name}"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit">Save</button>
|
||||
<a href="list.html" th:href="@{/}"><button type="button">Cancel</button></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>List of all persons</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.persons {
|
||||
border: 1px solid steelblue;
|
||||
width: 300px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.persons tr td, th {
|
||||
padding: 5px;
|
||||
border: 1px solid steelblue;
|
||||
}
|
||||
|
||||
.persons td:last-child, td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Persons:</h3>
|
||||
|
||||
<table class="persons">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="person : ${persons}" th:object="${person}">
|
||||
<td th:text="*{id}">1</td>
|
||||
<td th:text="*{name}">John Doe</td>
|
||||
<td>
|
||||
<a href="edit.html" th:href="@{/edit(id=*{id})}">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user