findAll();
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/application.yml b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/application.yml
new file mode 100644
index 00000000..6c79a7c5
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/data.sql b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/data.sql
new file mode 100644
index 00000000..e3bcb10d
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/data.sql
@@ -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')
+;
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages.properties b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages.properties
new file mode 100644
index 00000000..372c1b19
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages.properties
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages_en.properties b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages_en.properties
new file mode 100644
index 00000000..b156844e
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages_en.properties
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages_ru.properties b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages_ru.properties
new file mode 100644
index 00000000..a9739884
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/messages_ru.properties
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/schema.sql b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/schema.sql
new file mode 100644
index 00000000..0482e3d4
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/schema.sql
@@ -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)
+);
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/static/listmark.png b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/static/listmark.png
new file mode 100644
index 00000000..f8eb391b
Binary files /dev/null and b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/static/listmark.png differ
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/customError.html b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/customError.html
new file mode 100644
index 00000000..b54ef571
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/customError.html
@@ -0,0 +1,34 @@
+
+
+
+
+ List of all persons
+
+
+
+
+
+ Error text
+ Error text
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/edit.html b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/edit.html
new file mode 100644
index 00000000..ff0abf12
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/edit.html
@@ -0,0 +1,78 @@
+
+
+
+
+ Edit person
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/list.html b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/list.html
new file mode 100644
index 00000000..6acdb815
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/main/resources/templates/list.html
@@ -0,0 +1,76 @@
+
+
+
+
+ List of all persons
+
+
+
+
+
+
+Select language
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+ Hobby |
+ Action |
+
+
+
+
+ | 1 |
+ John Doe |
+ Hobby1, hobby2 |
+
+ Edit
+ |
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-demo/src/test/java/ru/otus/spring/controller/PersonControllerTest.java b/2025-05/spring-16-view/spring-mvc-view-demo/src/test/java/ru/otus/spring/controller/PersonControllerTest.java
new file mode 100644
index 00000000..fcb8dd5e
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-demo/src/test/java/ru/otus/spring/controller/PersonControllerTest.java
@@ -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 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 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));
+ }
+}
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/.gitignore b/2025-05/spring-16-view/spring-mvc-view-exercise/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/.gitignore
@@ -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/
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/pom.xml b/2025-05/spring-16-view/spring-mvc-view-exercise/pom.xml
new file mode 100644
index 00000000..e3b6db2d
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-mvc-view-exercise
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.3
+
+
+
+
+ 17
+ 17
+ 2.2.220
+ 2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+ com.h2database
+ h2
+ runtime
+ ${h2.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/Main.java b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..09283e91
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/Main.java
@@ -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");
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/controller/NotFoundException.java b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/controller/NotFoundException.java
new file mode 100644
index 00000000..41b48826
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/controller/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.otus.spring.controller;
+
+class NotFoundException extends RuntimeException{
+
+ NotFoundException() {
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/controller/PersonController.java b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/controller/PersonController.java
new file mode 100644
index 00000000..5a345bbb
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/controller/PersonController.java
@@ -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 persons = repository.findAll();
+ model.addAttribute("persons", persons);
+ return "list";
+ }
+
+ @GetMapping("/edit")
+ public String editPage(@RequestParam("id") long id, Model model) {
+ return null;
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/domain/Person.java b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..e40fd3f9
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/domain/Person.java
@@ -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;
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4fb88650
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java
@@ -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 {
+
+ List findAll();
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/application.yml b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/application.yml
new file mode 100644
index 00000000..c1f5d333
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/data.sql b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/data.sql
new file mode 100644
index 00000000..7fa18c96
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/data.sql
@@ -0,0 +1,2 @@
+insert into person (id, name) values (1, 'Pushkin');
+insert into person (id, name) values (2, 'Lermontov');
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/schema.sql b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/schema.sql
new file mode 100644
index 00000000..f36202f2
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/schema.sql
@@ -0,0 +1,4 @@
+create table person (
+ id integer generated by default as identity,
+ name varchar(255), primary key (id)
+);
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/templates/edit.html b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/templates/edit.html
new file mode 100644
index 00000000..87d8c8f1
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/templates/edit.html
@@ -0,0 +1,49 @@
+
+
+
+
+ Edit person
+
+
+
+
+
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/templates/list.html b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/templates/list.html
new file mode 100644
index 00000000..a5b52a8d
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-exercise/src/main/resources/templates/list.html
@@ -0,0 +1,49 @@
+
+
+
+
+ List of all persons
+
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+ Action |
+
+
+
+
+ | 1 |
+ John Doe |
+
+ Edit
+ |
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/.gitignore b/2025-05/spring-16-view/spring-mvc-view-solution1/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/.gitignore
@@ -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/
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/pom.xml b/2025-05/spring-16-view/spring-mvc-view-solution1/pom.xml
new file mode 100644
index 00000000..3f0ee517
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-mvc-view-solution1
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.3
+
+
+
+
+ 17
+ 17
+ 2.2.220
+ 2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+ com.h2database
+ h2
+ runtime
+ ${h2.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/Main.java b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..c94edb10
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/Main.java
@@ -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);
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/controller/NotFoundException.java b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/controller/NotFoundException.java
new file mode 100644
index 00000000..41b48826
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/controller/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.otus.spring.controller;
+
+class NotFoundException extends RuntimeException{
+
+ NotFoundException() {
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/controller/PersonController.java b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/controller/PersonController.java
new file mode 100644
index 00000000..2953c703
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/controller/PersonController.java
@@ -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 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";
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/domain/Person.java b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..e40fd3f9
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/domain/Person.java
@@ -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;
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4fb88650
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/java/ru/otus/spring/repostory/PersonRepository.java
@@ -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 {
+
+ List findAll();
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/application.yml b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/application.yml
new file mode 100644
index 00000000..c1f5d333
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/data.sql b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/data.sql
new file mode 100644
index 00000000..7fa18c96
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/data.sql
@@ -0,0 +1,2 @@
+insert into person (id, name) values (1, 'Pushkin');
+insert into person (id, name) values (2, 'Lermontov');
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/schema.sql b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/schema.sql
new file mode 100644
index 00000000..f36202f2
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/schema.sql
@@ -0,0 +1,4 @@
+create table person (
+ id integer generated by default as identity,
+ name varchar(255), primary key (id)
+);
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/templates/edit.html b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/templates/edit.html
new file mode 100644
index 00000000..7b3f5d6a
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/templates/edit.html
@@ -0,0 +1,48 @@
+
+
+
+
+ Edit person
+
+
+
+
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/templates/list.html b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/templates/list.html
new file mode 100644
index 00000000..0d59a759
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution1/src/main/resources/templates/list.html
@@ -0,0 +1,50 @@
+
+
+
+
+ List of all persons
+
+
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+ Action |
+
+
+
+
+ | 1 |
+ John Doe |
+
+ Edit
+ |
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/.gitignore b/2025-05/spring-16-view/spring-mvc-view-solution2/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/.gitignore
@@ -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/
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/pom.xml b/2025-05/spring-16-view/spring-mvc-view-solution2/pom.xml
new file mode 100644
index 00000000..73d31d93
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-mvc-view-solution2
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.3
+
+
+
+
+ 17
+ 17
+ 2.2.220
+ 2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+ com.h2database
+ h2
+ runtime
+ ${h2.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/Main.java b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..c94edb10
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/Main.java
@@ -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);
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/controller/NotFoundException.java b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/controller/NotFoundException.java
new file mode 100644
index 00000000..41b48826
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/controller/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.otus.spring.controller;
+
+class NotFoundException extends RuntimeException{
+
+ NotFoundException() {
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/controller/PersonController.java b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/controller/PersonController.java
new file mode 100644
index 00000000..ad22dbe8
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/controller/PersonController.java
@@ -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 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";
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/domain/Person.java b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..e40fd3f9
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/domain/Person.java
@@ -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;
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4fb88650
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/java/ru/otus/spring/repostory/PersonRepository.java
@@ -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 {
+
+ List findAll();
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/application.yml b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/application.yml
new file mode 100644
index 00000000..c1f5d333
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/data.sql b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/data.sql
new file mode 100644
index 00000000..7fa18c96
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/data.sql
@@ -0,0 +1,2 @@
+insert into person (id, name) values (1, 'Pushkin');
+insert into person (id, name) values (2, 'Lermontov');
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/schema.sql b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/schema.sql
new file mode 100644
index 00000000..f36202f2
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/schema.sql
@@ -0,0 +1,4 @@
+create table person (
+ id integer generated by default as identity,
+ name varchar(255), primary key (id)
+);
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/templates/edit.html b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/templates/edit.html
new file mode 100644
index 00000000..ef643cff
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/templates/edit.html
@@ -0,0 +1,48 @@
+
+
+
+
+ Edit person
+
+
+
+
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/templates/list.html b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/templates/list.html
new file mode 100644
index 00000000..a5b52a8d
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution2/src/main/resources/templates/list.html
@@ -0,0 +1,49 @@
+
+
+
+
+ List of all persons
+
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+ Action |
+
+
+
+
+ | 1 |
+ John Doe |
+
+ Edit
+ |
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/.gitignore b/2025-05/spring-16-view/spring-mvc-view-solution3/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/.gitignore
@@ -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/
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/pom.xml b/2025-05/spring-16-view/spring-mvc-view-solution3/pom.xml
new file mode 100644
index 00000000..a4a0b346
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-mvc-view-solution3
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.3
+
+
+
+
+ 17
+ 17
+ 2.2.220
+ 2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+ com.h2database
+ h2
+ runtime
+ ${h2.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/Main.java b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..418ecb9b
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/Main.java
@@ -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);
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/controller/NotFoundException.java b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/controller/NotFoundException.java
new file mode 100644
index 00000000..41b48826
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/controller/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.otus.spring.controller;
+
+class NotFoundException extends RuntimeException{
+
+ NotFoundException() {
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/controller/PersonController.java b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/controller/PersonController.java
new file mode 100644
index 00000000..ad22dbe8
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/controller/PersonController.java
@@ -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 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";
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/domain/Person.java b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..e40fd3f9
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/domain/Person.java
@@ -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;
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4fb88650
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/java/ru/otus/spring/repostory/PersonRepository.java
@@ -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 {
+
+ List findAll();
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/application.yml b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/application.yml
new file mode 100644
index 00000000..c1f5d333
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/data.sql b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/data.sql
new file mode 100644
index 00000000..7fa18c96
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/data.sql
@@ -0,0 +1,2 @@
+insert into person (id, name) values (1, 'Pushkin');
+insert into person (id, name) values (2, 'Lermontov');
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/schema.sql b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/schema.sql
new file mode 100644
index 00000000..f36202f2
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/schema.sql
@@ -0,0 +1,4 @@
+create table person (
+ id integer generated by default as identity,
+ name varchar(255), primary key (id)
+);
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/static/listmark.png b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/static/listmark.png
new file mode 100644
index 00000000..f8eb391b
Binary files /dev/null and b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/static/listmark.png differ
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/templates/edit.html b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/templates/edit.html
new file mode 100644
index 00000000..ef643cff
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/templates/edit.html
@@ -0,0 +1,48 @@
+
+
+
+
+ Edit person
+
+
+
+
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/templates/list.html b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/templates/list.html
new file mode 100644
index 00000000..e7983e80
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution3/src/main/resources/templates/list.html
@@ -0,0 +1,49 @@
+
+
+
+
+ List of all persons
+
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+ Action |
+
+
+
+
+ | 1 |
+ John Doe |
+
+ Edit
+ |
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/.gitignore b/2025-05/spring-16-view/spring-mvc-view-solution4/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/.gitignore
@@ -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/
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/pom.xml b/2025-05/spring-16-view/spring-mvc-view-solution4/pom.xml
new file mode 100644
index 00000000..43ff305a
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-mvc-view-solution4
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.3
+
+
+
+
+ 17
+ 17
+ 2.2.220
+ 2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+ com.h2database
+ h2
+ runtime
+ ${h2.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/Main.java b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..0335e618
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/Main.java
@@ -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);
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/GlobalExceptionHandler.java b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/GlobalExceptionHandler.java
new file mode 100644
index 00000000..f700c5e6
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/GlobalExceptionHandler.java
@@ -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");
+ }
+
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/NotFoundException.java b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/NotFoundException.java
new file mode 100644
index 00000000..35b57190
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.otus.spring.controller;
+
+public class NotFoundException extends RuntimeException{
+
+ NotFoundException() {
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/PersonController.java b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/PersonController.java
new file mode 100644
index 00000000..971bf323
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/controller/PersonController.java
@@ -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 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:/";
+ }
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/domain/Person.java b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..e40fd3f9
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/domain/Person.java
@@ -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;
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4fb88650
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/java/ru/otus/spring/repostory/PersonRepository.java
@@ -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 {
+
+ List findAll();
+}
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/application.yml b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/application.yml
new file mode 100644
index 00000000..c1f5d333
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/data.sql b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/data.sql
new file mode 100644
index 00000000..7fa18c96
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/data.sql
@@ -0,0 +1,2 @@
+insert into person (id, name) values (1, 'Pushkin');
+insert into person (id, name) values (2, 'Lermontov');
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/schema.sql b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/schema.sql
new file mode 100644
index 00000000..f36202f2
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/schema.sql
@@ -0,0 +1,4 @@
+create table person (
+ id integer generated by default as identity,
+ name varchar(255), primary key (id)
+);
\ No newline at end of file
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/customError.html b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/customError.html
new file mode 100644
index 00000000..a4550575
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/customError.html
@@ -0,0 +1,34 @@
+
+
+
+
+ List of all persons
+
+
+
+
+
+ Error:
+ Error text
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/edit.html b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/edit.html
new file mode 100644
index 00000000..b5165f40
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/edit.html
@@ -0,0 +1,49 @@
+
+
+
+
+ Edit person
+
+
+
+
+
+
+
+
+
diff --git a/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/list.html b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/list.html
new file mode 100644
index 00000000..e93f2235
--- /dev/null
+++ b/2025-05/spring-16-view/spring-mvc-view-solution4/src/main/resources/templates/list.html
@@ -0,0 +1,49 @@
+
+
+
+
+ List of all persons
+
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+ Action |
+
+
+
+
+ | 1 |
+ John Doe |
+
+ Edit
+ |
+
+
+
+
+