diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/.gitignore b/2023-01/spring-09-jdbc/jdbc-demo-exercise/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/pom.xml b/2023-01/spring-09-jdbc/jdbc-demo-exercise/pom.xml
new file mode 100644
index 00000000..36acd2ec
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+
+ 3.0.2
+
+
+
+ ru.otus
+ jdbc-demo-exercise
+ 1.0-SNAPSHOT
+
+
+
+
+ 17
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 2.1.212
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/Main.java b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..8916eb04
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,20 @@
+package ru.otus.spring;
+
+import org.h2.tools.Console;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import ru.otus.spring.dao.PersonDao;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+
+ ApplicationContext context = SpringApplication.run(Main.class);
+
+ PersonDao dao = context.getBean(PersonDao.class);
+
+ Console.main(args);
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..9fa01df3
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+public interface PersonDao {
+ int count();
+
+ void insert(Person person);
+
+ Person getById(long id);
+
+ List getAll();
+
+ void deleteById(long id);
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..836b737f
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,42 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ return 0;
+ }
+
+ @Override
+ public void insert(Person person) {
+
+ }
+
+ @Override
+ public Person getById(long id) {
+ return null;
+ }
+
+ @Override
+ public List getAll() {
+ return null;
+ }
+
+ @Override
+ public void deleteById(long id) {
+
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/domain/Person.java b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.domain;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Data
+public class Person {
+ private final long id;
+ private final String name;
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/application.yml
new file mode 100644
index 00000000..8f40ea4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/application.yml
@@ -0,0 +1,16 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #schema: schema.sql
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ schema-locations: schema.sql
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/schema.sql b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/main/resources/schema.sql
@@ -0,0 +1,2 @@
+DROP TABLE IF EXISTS PERSONS;
+CREATE TABLE PERSONS(ID BIGINT PRIMARY KEY, NAME VARCHAR(255));
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
new file mode 100644
index 00000000..874b7904
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
@@ -0,0 +1,93 @@
+package ru.otus.spring.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.dao.EmptyResultDataAccessException;
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.*;
+
+@DisplayName("Dao для работы с пёрсонами должно")
+@JdbcTest
+@Import(PersonDaoJdbc.class)
+class PersonDaoJdbcTest {
+
+ private static final int EXPECTED_PERSONS_COUNT = 1;
+ private static final int EXISTING_PERSON_ID = 1;
+ private static final String EXISTING_PERSON_NAME = "Ivan";
+
+ @Autowired
+ private PersonDaoJdbc personDao;
+
+ @DisplayName("возвращать ожидаемое количество пёрсонов в БД")
+ @Test
+ void shouldReturnExpectedPersonCount() {
+ int actualPersonsCount = personDao.count();
+ assertThat(actualPersonsCount).isEqualTo(EXPECTED_PERSONS_COUNT);
+ }
+
+ @DisplayName("добавлять пёрсона в БД")
+ @Test
+ void shouldInsertPerson() {
+ int countBeforeInsert = personDao.count();
+ assertThat(countBeforeInsert).isEqualTo(EXPECTED_PERSONS_COUNT);
+
+ Person expectedPerson = new Person(2, "Igor");
+ personDao.insert(expectedPerson);
+
+ // Ошибка! Сейчас так проверяем т.к. больше нет других способов,
+ // когда появится getById, будем использовать его
+ int countAfterInsert = personDao.count();
+ assertThat(countAfterInsert).isEqualTo(countBeforeInsert + 1);
+/*
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+*/
+ }
+
+ @DisplayName("возвращать ожидаемого пёрсона по его id")
+ @Test
+ void shouldReturnExpectedPersonById() {
+ Person expectedPerson = new Person(EXISTING_PERSON_ID, EXISTING_PERSON_NAME);
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+ }
+
+ @DisplayName("удалять заданного пёрсона по его id")
+ @Test
+ void shouldCorrectDeletePersonById() {
+ // Ошибка! Сейчас так проверяем т.к. больше нет других способов,
+ // когда появится getById, тест будет выглядеть, как закомментированный блок ниже
+ int countBeforeDelete = personDao.count();
+ assertThat(countBeforeDelete).isEqualTo(EXPECTED_PERSONS_COUNT);
+
+ personDao.deleteById(EXISTING_PERSON_ID);
+
+ int countAfterDelete = personDao.count();
+ assertThat(countAfterDelete).isEqualTo(countBeforeDelete - 1);
+
+ /*
+ assertThatCode(() -> personDao.getById(EXISTING_PERSON_ID))
+ .doesNotThrowAnyException();
+
+ personDao.deleteById(EXISTING_PERSON_ID);
+
+ assertThatThrownBy(() -> personDao.getById(EXISTING_PERSON_ID))
+ .isInstanceOf(EmptyResultDataAccessException.class);
+ */
+ }
+
+ @DisplayName("возвращать ожидаемый список пёрсонов")
+ @Test
+ void shouldReturnExpectedPersonsList() {
+ Person expectedPerson = new Person(EXISTING_PERSON_ID, EXISTING_PERSON_NAME);
+ List actualPersonList = personDao.getAll();
+ assertThat(actualPersonList)
+ .containsExactlyInAnyOrder(expectedPerson);
+ }
+}
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/resources/application.yml
new file mode 100644
index 00000000..f7de231a
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ #schema-locations: schema.sql
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/resources/data.sql
new file mode 100644
index 00000000..32d79cd1
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-exercise/src/test/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'Ivan');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/.gitignore b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/pom.xml b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/pom.xml
new file mode 100644
index 00000000..d240349d
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-3
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+
+ 3.0.2
+
+
+
+
+
+ 17
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 2.1.212
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/Main.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..6c08834b
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,23 @@
+package ru.otus.spring;
+
+import org.h2.tools.Console;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+
+ ApplicationContext context = SpringApplication.run(Main.class);
+
+ PersonDao dao = context.getBean(PersonDao.class);
+
+ System.out.println("All count " + dao.count());
+
+ Console.main(args);
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDao.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..f2c30f7c
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,18 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+public interface PersonDao {
+
+ int count();
+
+ void insert(Person person);
+
+ Person getById(long id);
+
+ List getAll();
+
+ void deleteById(long id);
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..87974d75
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,45 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ Integer count = jdbc.queryForObject("select count(*) from persons", Integer.class);
+ return count == null? 0: count;
+ }
+
+ @Override
+ public void insert(Person person) {
+
+ }
+
+ @Override
+ public Person getById(long id) {
+ return null;
+ }
+
+ @Override
+ public List getAll() {
+ return null;
+ }
+
+ @Override
+ public void deleteById(long id) {
+
+ }
+
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/domain/Person.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.domain;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Data
+public class Person {
+ private final long id;
+ private final String name;
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/application.yml
new file mode 100644
index 00000000..8f40ea4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/application.yml
@@ -0,0 +1,16 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #schema: schema.sql
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ schema-locations: schema.sql
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/schema.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/main/resources/schema.sql
@@ -0,0 +1,2 @@
+DROP TABLE IF EXISTS PERSONS;
+CREATE TABLE PERSONS(ID BIGINT PRIMARY KEY, NAME VARCHAR(255));
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
new file mode 100644
index 00000000..31426b0a
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
@@ -0,0 +1,27 @@
+package ru.otus.spring.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
+import org.springframework.context.annotation.Import;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@DisplayName("Dao для работы с пёрсонами должно")
+@JdbcTest
+@Import(PersonDaoJdbc.class)
+class PersonDaoJdbcTest {
+
+ private static final int EXPECTED_PERSONS_COUNT = 1;
+
+ @Autowired
+ private PersonDaoJdbc personDao;
+
+ @DisplayName("возвращать ожидаемое количество пёрсонов в БД")
+ @Test
+ void shouldReturnExpectedPersonCount() {
+ int actualPersonsCount = personDao.count();
+ assertThat(actualPersonsCount).isEqualTo(EXPECTED_PERSONS_COUNT);
+ }
+}
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/resources/application.yml
new file mode 100644
index 00000000..f7de231a
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ #schema-locations: schema.sql
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/resources/data.sql
new file mode 100644
index 00000000..32d79cd1
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-3/src/test/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'Ivan');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/.gitignore b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/pom.xml b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/pom.xml
new file mode 100644
index 00000000..2415ad1c
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-4
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+
+ 3.0.2
+
+
+
+
+
+ 17
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 2.1.212
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/Main.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..2bc8e898
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,27 @@
+package ru.otus.spring;
+
+import org.h2.tools.Console;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+
+ ApplicationContext context = SpringApplication.run(Main.class);
+
+ PersonDao dao = context.getBean(PersonDao.class);
+
+ System.out.println("All count " + dao.count());
+
+ dao.insert(new Person(2, "ivan"));
+
+ System.out.println("All count " + dao.count());
+
+ Console.main(args);
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDao.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..f2c30f7c
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,18 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+public interface PersonDao {
+
+ int count();
+
+ void insert(Person person);
+
+ Person getById(long id);
+
+ List getAll();
+
+ void deleteById(long id);
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..eb2dabbb
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,46 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ Integer count = jdbc.queryForObject("select count(*) from persons", Integer.class);
+ return count == null? 0: count;
+ }
+
+ @Override
+ public void insert(Person person) {
+ jdbc.update("insert into persons (id, name) values (?, ?)", person.getId(), person.getName());
+ }
+
+ @Override
+ public Person getById(long id) {
+ return null;
+ }
+
+ @Override
+ public List getAll() {
+ return null;
+ }
+
+ @Override
+ public void deleteById(long id) {
+ jdbc.update("delete from persons where id = ?", id);
+ }
+
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/domain/Person.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.domain;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Data
+public class Person {
+ private final long id;
+ private final String name;
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/application.yml
new file mode 100644
index 00000000..8f40ea4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/application.yml
@@ -0,0 +1,16 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #schema: schema.sql
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ schema-locations: schema.sql
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/schema.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/main/resources/schema.sql
@@ -0,0 +1,2 @@
+DROP TABLE IF EXISTS PERSONS;
+CREATE TABLE PERSONS(ID BIGINT PRIMARY KEY, NAME VARCHAR(255));
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
new file mode 100644
index 00000000..505a1bb0
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
@@ -0,0 +1,73 @@
+package ru.otus.spring.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
+import org.springframework.context.annotation.Import;
+import ru.otus.spring.domain.Person;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@DisplayName("Dao для работы с пёрсонами должно")
+@JdbcTest
+@Import(PersonDaoJdbc.class)
+class PersonDaoJdbcTest {
+
+ private static final int EXPECTED_PERSONS_COUNT = 1;
+ private static final int EXISTING_PERSON_ID = 1;
+ private static final String EXISTING_PERSON_NAME = "Ivan";
+
+ @Autowired
+ private PersonDaoJdbc personDao;
+
+ @DisplayName("возвращать ожидаемое количество пёрсонов в БД")
+ @Test
+ void shouldReturnExpectedPersonCount() {
+ int actualPersonsCount = personDao.count();
+ assertThat(actualPersonsCount).isEqualTo(EXPECTED_PERSONS_COUNT);
+ }
+
+ @DisplayName("добавлять пёрсона в БД")
+ @Test
+ void shouldInsertPerson() {
+ int countBeforeInsert = personDao.count();
+ assertThat(countBeforeInsert).isEqualTo(EXPECTED_PERSONS_COUNT);
+
+ Person expectedPerson = new Person(2, "Igor");
+ personDao.insert(expectedPerson);
+
+ // Ошибка! Сейчас так проверяем т.к. больше нет других способов,
+ // когда появится getById, будем использовать его
+ int countAfterInsert = personDao.count();
+ assertThat(countAfterInsert).isEqualTo(countBeforeInsert + 1);
+/*
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+*/
+ }
+
+ @DisplayName("удалять заданного пёрсона по его id")
+ @Test
+ void shouldCorrectDeletePersonById() {
+ // Ошибка! Сейчас так проверяем т.к. больше нет других способов,
+ // когда появится getById, тест будет выглядеть, как закомментированный блок ниже
+ int countBeforeDelete = personDao.count();
+ assertThat(countBeforeDelete).isEqualTo(EXPECTED_PERSONS_COUNT);
+
+ personDao.deleteById(EXISTING_PERSON_ID);
+
+ int countAfterDelete = personDao.count();
+ assertThat(countAfterDelete).isEqualTo(countBeforeDelete - 1);
+
+ /*
+ assertThatCode(() -> personDao.getById(EXISTING_PERSON_ID))
+ .doesNotThrowAnyException();
+
+ personDao.deleteById(EXISTING_PERSON_ID);
+
+ assertThatThrownBy(() -> personDao.getById(EXISTING_PERSON_ID))
+ .isInstanceOf(EmptyResultDataAccessException.class);
+ */
+ }
+}
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/resources/application.yml
new file mode 100644
index 00000000..f7de231a
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ #schema-locations: schema.sql
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/resources/data.sql
new file mode 100644
index 00000000..32d79cd1
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-4/src/test/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'Ivan');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/.gitignore b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/pom.xml b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/pom.xml
new file mode 100644
index 00000000..288e3289
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-5
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+
+ 3.0.2
+
+
+
+
+
+ 17
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 2.1.212
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/Main.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..4bc2aca8
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,31 @@
+package ru.otus.spring;
+
+import org.h2.tools.Console;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+
+ ApplicationContext context = SpringApplication.run(Main.class);
+
+ PersonDao dao = context.getBean(PersonDao.class);
+
+ System.out.println("All count " + dao.count());
+
+ dao.insert(new Person(2, "ivan"));
+
+ System.out.println("All count " + dao.count());
+
+ Person ivan = dao.getById(2);
+
+ System.out.println("Ivan id: " + ivan.getId() + " name: " + ivan.getName());
+
+ Console.main(args);
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDao.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..9fa01df3
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+public interface PersonDao {
+ int count();
+
+ void insert(Person person);
+
+ Person getById(long id);
+
+ List getAll();
+
+ void deleteById(long id);
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..425ce000
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,58 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ Integer count = jdbc.queryForObject("select count(*) from persons", Integer.class);
+ return count == null? 0: count;
+ }
+
+ @Override
+ public void insert(Person person) {
+ jdbc.update("insert into persons (id, name) values (?, ?)", person.getId(), person.getName());
+ }
+
+ @Override
+ public Person getById(long id) {
+ return jdbc.queryForObject("select id, name from persons where id = ?", new PersonMapper(), id);
+ }
+
+ @Override
+ public List getAll() {
+ return jdbc.query("select id, name from persons", new PersonMapper());
+ }
+
+ @Override
+ public void deleteById(long id) {
+ jdbc.update("delete from persons where id = ?", id);
+ }
+
+
+ private static class PersonMapper implements RowMapper {
+
+ @Override
+ public Person mapRow(ResultSet resultSet, int i) throws SQLException {
+ long id = resultSet.getLong("id");
+ String name = resultSet.getString("name");
+ return new Person(id, name);
+ }
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/domain/Person.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.domain;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Data
+public class Person {
+ private final long id;
+ private final String name;
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/application.yml
new file mode 100644
index 00000000..8f40ea4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/application.yml
@@ -0,0 +1,16 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #schema: schema.sql
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ schema-locations: schema.sql
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/schema.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/main/resources/schema.sql
@@ -0,0 +1,2 @@
+DROP TABLE IF EXISTS PERSONS;
+CREATE TABLE PERSONS(ID BIGINT PRIMARY KEY, NAME VARCHAR(255));
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
new file mode 100644
index 00000000..3f76df0e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
@@ -0,0 +1,71 @@
+package ru.otus.spring.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.dao.EmptyResultDataAccessException;
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.*;
+
+@DisplayName("Dao для работы с пёрсонами должно")
+@JdbcTest
+@Import(PersonDaoJdbc.class)
+class PersonDaoJdbcTest {
+
+ private static final int EXPECTED_PERSONS_COUNT = 1;
+ private static final int EXISTING_PERSON_ID = 1;
+ private static final String EXISTING_PERSON_NAME = "Ivan";
+
+ @Autowired
+ private PersonDaoJdbc personDao;
+
+ @DisplayName("возвращать ожидаемое количество пёрсонов в БД")
+ @Test
+ void shouldReturnExpectedPersonCount() {
+ int actualPersonsCount = personDao.count();
+ assertThat(actualPersonsCount).isEqualTo(EXPECTED_PERSONS_COUNT);
+ }
+
+ @DisplayName("добавлять пёрсона в БД")
+ @Test
+ void shouldInsertPerson() {
+ Person expectedPerson = new Person(2, "Igor");
+ personDao.insert(expectedPerson);
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+ }
+
+ @DisplayName("возвращать ожидаемого пёрсона по его id")
+ @Test
+ void shouldReturnExpectedPersonById() {
+ Person expectedPerson = new Person(EXISTING_PERSON_ID, EXISTING_PERSON_NAME);
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+ }
+
+ @DisplayName("удалять заданного пёрсона по его id")
+ @Test
+ void shouldCorrectDeletePersonById() {
+ assertThatCode(() -> personDao.getById(EXISTING_PERSON_ID))
+ .doesNotThrowAnyException();
+
+ personDao.deleteById(EXISTING_PERSON_ID);
+
+ assertThatThrownBy(() -> personDao.getById(EXISTING_PERSON_ID))
+ .isInstanceOf(EmptyResultDataAccessException.class);
+ }
+
+ @DisplayName("возвращать ожидаемый список пёрсонов")
+ @Test
+ void shouldReturnExpectedPersonsList() {
+ Person expectedPerson = new Person(EXISTING_PERSON_ID, EXISTING_PERSON_NAME);
+ List actualPersonList = personDao.getAll();
+ assertThat(actualPersonList)
+ .containsExactlyInAnyOrder(expectedPerson);
+ }
+}
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/resources/application.yml
new file mode 100644
index 00000000..f7de231a
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ #schema-locations: schema.sql
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/resources/data.sql
new file mode 100644
index 00000000..32d79cd1
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-5/src/test/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'Ivan');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/.gitignore b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/pom.xml b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/pom.xml
new file mode 100644
index 00000000..923ba287
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-final
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+
+ 3.0.2
+
+
+
+
+
+ 17
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 2.1.212
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/Main.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..05636811
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,33 @@
+package ru.otus.spring;
+
+import org.h2.tools.Console;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+
+ ApplicationContext context = SpringApplication.run(Main.class);
+
+ PersonDao dao = context.getBean(PersonDao.class);
+
+ System.out.println("All count " + dao.count());
+
+ dao.insert(new Person(2, "ivan"));
+
+ System.out.println("All count " + dao.count());
+
+ Person ivan = dao.getById(2);
+
+ System.out.println("Ivan id: " + ivan.getId() + " name: " + ivan.getName());
+
+ System.out.println(dao.getAll());
+
+ Console.main(args);
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDao.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..f2c30f7c
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,18 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+public interface PersonDao {
+
+ int count();
+
+ void insert(Person person);
+
+ Person getById(long id);
+
+ List getAll();
+
+ void deleteById(long id);
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..e703d4f6
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,71 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+ private final NamedParameterJdbcOperations namedParameterJdbcOperations;
+
+ public PersonDaoJdbc(NamedParameterJdbcOperations namedParameterJdbcOperations)
+ {
+ // Это просто оставили, чтобы не переписывать код
+ // В идеале всё должно быть на NamedParameterJdbcOperations
+ this.jdbc = namedParameterJdbcOperations.getJdbcOperations();
+ this.namedParameterJdbcOperations = namedParameterJdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ Integer count = jdbc.queryForObject("select count(*) from persons", Integer.class);
+ return count == null? 0: count;
+ }
+
+ @Override
+ public void insert(Person person) {
+ namedParameterJdbcOperations.update("insert into persons (id, name) values (:id, :name)",
+ Map.of("id", person.getId(), "name", person.getName()));
+ }
+
+ @Override
+ public Person getById(long id) {
+ Map params = Collections.singletonMap("id", id);
+ return namedParameterJdbcOperations.queryForObject(
+ "select id, name from persons where id = :id", params, new PersonMapper()
+ );
+ }
+
+ @Override
+ public List getAll() {
+ return jdbc.query("select id, name from persons", new PersonMapper());
+ }
+
+ @Override
+ public void deleteById(long id) {
+ Map params = Collections.singletonMap("id", id);
+ namedParameterJdbcOperations.update(
+ "delete from persons where id = :id", params
+ );
+ }
+
+ private static class PersonMapper implements RowMapper {
+
+ @Override
+ public Person mapRow(ResultSet resultSet, int i) throws SQLException {
+ long id = resultSet.getLong("id");
+ String name = resultSet.getString("name");
+ return new Person(id, name);
+ }
+ }
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/domain/Person.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.domain;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Data
+public class Person {
+ private final long id;
+ private final String name;
+}
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/application.yml
new file mode 100644
index 00000000..50b6c632
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/application.yml
@@ -0,0 +1,17 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #schema: schema.sql
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ schema-locations: schema.sql
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
+
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/schema.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/main/resources/schema.sql
@@ -0,0 +1,2 @@
+DROP TABLE IF EXISTS PERSONS;
+CREATE TABLE PERSONS(ID BIGINT PRIMARY KEY, NAME VARCHAR(255));
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
new file mode 100644
index 00000000..87e83148
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
@@ -0,0 +1,90 @@
+package ru.otus.spring.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.test.annotation.Commit;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.transaction.AfterTransaction;
+import org.springframework.test.context.transaction.BeforeTransaction;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.*;
+
+@DisplayName("Dao для работы с пёрсонами должно")
+@JdbcTest
+@Import(PersonDaoJdbc.class)
+//@Transactional(propagation = Propagation.NOT_SUPPORTED)
+class PersonDaoJdbcTest {
+
+ private static final int EXPECTED_PERSONS_COUNT = 1;
+ private static final int EXISTING_PERSON_ID = 1;
+ private static final String EXISTING_PERSON_NAME = "Ivan";
+
+ @Autowired
+ private PersonDaoJdbc personDao;
+
+ @BeforeTransaction
+ void beforeTransaction(){
+ System.out.println("beforeTransaction");
+ }
+
+ @AfterTransaction
+ void afterTransaction(){
+ System.out.println("afterTransaction");
+ }
+
+ @DisplayName("возвращать ожидаемое количество пёрсонов в БД")
+ @Test
+ void shouldReturnExpectedPersonCount() {
+ int actualPersonsCount = personDao.count();
+ assertThat(actualPersonsCount).isEqualTo(EXPECTED_PERSONS_COUNT);
+ }
+
+ //@Rollback(value = false)
+ //@Commit
+ @DisplayName("добавлять пёрсона в БД")
+ @Test
+ void shouldInsertPerson() {
+ Person expectedPerson = new Person(2, "Igor");
+ personDao.insert(expectedPerson);
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+ }
+
+ @DisplayName("возвращать ожидаемого пёрсона по его id")
+ @Test
+ void shouldReturnExpectedPersonById() {
+ Person expectedPerson = new Person(EXISTING_PERSON_ID, EXISTING_PERSON_NAME);
+ Person actualPerson = personDao.getById(expectedPerson.getId());
+ assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
+ }
+
+ @DisplayName("удалять заданного пёрсона по его id")
+ @Test
+ void shouldCorrectDeletePersonById() {
+ assertThatCode(() -> personDao.getById(EXISTING_PERSON_ID))
+ .doesNotThrowAnyException();
+
+ personDao.deleteById(EXISTING_PERSON_ID);
+
+ assertThatThrownBy(() -> personDao.getById(EXISTING_PERSON_ID))
+ .isInstanceOf(EmptyResultDataAccessException.class);
+ }
+
+ @DisplayName("возвращать ожидаемый список пёрсонов")
+ @Test
+ void shouldReturnExpectedPersonsList() {
+ Person expectedPerson = new Person(EXISTING_PERSON_ID, EXISTING_PERSON_NAME);
+ List actualPersonList = personDao.getAll();
+ assertThat(actualPersonList)
+ .containsExactlyInAnyOrder(expectedPerson);
+ }
+}
\ No newline at end of file
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/resources/application.yml b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/resources/application.yml
new file mode 100644
index 00000000..f7de231a
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ #initialization-mode: always
+ #data: data.sql
+ sql:
+ init:
+ mode: always
+ data-locations: data.sql
+ #schema-locations: schema.sql
diff --git a/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/resources/data.sql b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/resources/data.sql
new file mode 100644
index 00000000..32d79cd1
--- /dev/null
+++ b/2023-01/spring-09-jdbc/jdbc-demo-solution-final/src/test/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'Ivan');
diff --git a/2023-01/spring-09-jdbc/pom.xml b/2023-01/spring-09-jdbc/pom.xml
new file mode 100644
index 00000000..1c941227
--- /dev/null
+++ b/2023-01/spring-09-jdbc/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-class-work
+ 1.0
+
+ pom
+
+
+ jdbc-demo-exercise
+ jdbc-demo-solution-3
+ jdbc-demo-solution-4
+ jdbc-demo-solution-5
+ jdbc-demo-solution-final
+
+