diff --git a/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-exercise/pom.xml b/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-exercise/pom.xml
index c0201a2c..0a7c8dcc 100644
--- a/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-exercise/pom.xml
+++ b/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-exercise/pom.xml
@@ -15,8 +15,8 @@
- 14
- 14
+ 11
+ 11
diff --git a/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-solution/pom.xml b/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-solution/pom.xml
index 436a2a0a..e47993b8 100644
--- a/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-solution/pom.xml
+++ b/2021-02/spring-16/spring-mvc-view-classwork/spring-mvc-view-solution/pom.xml
@@ -15,8 +15,8 @@
- 14
- 14
+ 11
+ 11
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/.gitignore b/2021-02/spring-17-jquery/spring-17-boot-and-react/.gitignore
new file mode 100644
index 00000000..b11e399f
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/.gitignore
@@ -0,0 +1,30 @@
+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/
+
+
+node/
+/node_modules
+/output
+package-lock.json
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/package.json b/2021-02/spring-17-jquery/spring-17-boot-and-react/package.json
new file mode 100644
index 00000000..f7dc8478
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "client",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "dev": "webpack-dev-server --config webpack.dev.config.js",
+ "build": "webpack"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "react": "^16.2.0",
+ "react-dom": "^16.2.0"
+ },
+ "devDependencies": {
+ "babel-core": "^6.26.0",
+ "babel-loader": "^7.1.2",
+ "babel-preset-env": "^1.6.1",
+ "babel-preset-react": "^6.24.1",
+ "html-webpack-plugin": "^3.2.0",
+ "webpack": "^3.10.0",
+ "webpack-cli": "^3.2.3",
+ "webpack-dev-server": "^2.9.7"
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/pom.xml b/2021-02/spring-17-jquery/spring-17-boot-and-react/pom.xml
new file mode 100644
index 00000000..6256e3f1
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-17-boot-and-react
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.4.RELEASE
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ com.h2database
+ h2
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ 1.6
+
+
+ install node and npm
+
+ install-node-and-npm
+
+
+ v10.15.1
+ 6.4.1
+
+
+
+ npm install
+
+ npm
+
+ generate-resources
+
+ install
+
+
+
+ npm run build
+
+ npm
+
+ generate-resources
+
+ run build
+
+
+
+
+
+
+
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..d33c8908
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,27 @@
+package ru.otus.spring;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.repostory.PersonRepository;
+
+import javax.annotation.PostConstruct;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class);
+ }
+
+ @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
+ @Autowired
+ private PersonRepository repository;
+
+ @PostConstruct
+ public void init() {
+ repository.save(new Person("Pushkin"));
+ repository.save(new Person("Lermontov"));
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/domain/Person.java b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..374c55ac
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,37 @@
+package ru.otus.spring.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Person {
+
+ @Id
+ @GeneratedValue
+ private int id;
+ private String name;
+
+ public Person() {
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4b20e5b7
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/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/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/PersonController.java
new file mode 100644
index 00000000..8073a615
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/PersonController.java
@@ -0,0 +1,30 @@
+package ru.otus.spring.rest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.repostory.PersonRepository;
+import ru.otus.spring.rest.dto.PersonDto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+public class PersonController {
+
+ private final PersonRepository repository;
+
+ @Autowired
+ public PersonController(PersonRepository repository) {
+ this.repository = repository;
+ }
+
+ @GetMapping("/api/persons")
+ public List getAllPersons() {
+ return repository.findAll().stream().map(PersonDto::toDto)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/dto/PersonDto.java b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/dto/PersonDto.java
new file mode 100644
index 00000000..03918447
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/dto/PersonDto.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Russian Post
+ *
+ * This source code is Russian Post Confidential Proprietary.
+ * This software is protected by copyright. All rights and titles are reserved.
+ * You shall not use, copy, distribute, modify, decompile, disassemble or reverse engineer the software.
+ * Otherwise this violation would be treated by law and would be subject to legal prosecution.
+ * Legal use of the software provides receipt of a license from the right name only.
+ */
+package ru.otus.spring.rest.dto;
+
+import ru.otus.spring.domain.Person;
+
+/**
+ * DTO that represents Account
+ */
+@SuppressWarnings("all")
+public class PersonDto {
+
+ private int id = -1;
+ private String name;
+
+ public PersonDto() {
+ }
+
+ public PersonDto(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public static PersonDto toDto(Person person) {
+ return new PersonDto(person.getId(), person.getName());
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/components/App.js b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/components/App.js
new file mode 100644
index 00000000..79f94811
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/components/App.js
@@ -0,0 +1,45 @@
+import React from 'react'
+
+const Header = (props) => (
+ {props.title}
+);
+
+export default class App extends React.Component {
+
+ constructor() {
+ super();
+ this.state = {persons: []};
+ }
+
+ componentDidMount() {
+ fetch('/api/persons')
+ .then(response => response.json())
+ .then(persons => this.setState({persons}));
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ | ID |
+ Name |
+
+
+
+ {
+ this.state.persons.map((person, i) => (
+
+ | {person.id} |
+ {person.name} |
+
+ ))
+ }
+
+
+
+ )
+ }
+};
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.html b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.html
new file mode 100644
index 00000000..86fc7182
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.html
@@ -0,0 +1,15 @@
+
+
+
+ Minimal React Boilerplate
+
+
+
+
+
+
+
+
+
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.js b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.js
new file mode 100644
index 00000000..52a1c724
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.js
@@ -0,0 +1,9 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+import App from './components/App'
+
+ReactDOM.render(
+ ,
+ document.getElementById('root')
+)
\ No newline at end of file
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.config.js b/2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.config.js
new file mode 100644
index 00000000..654887cc
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.config.js
@@ -0,0 +1,47 @@
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const path = require('path');
+const webpack = require('webpack');
+
+module.exports = {
+ entry: './src/ui/index.js',
+ output: {
+ path: path.resolve(__dirname, 'target/classes/public/'),
+ filename: 'bundle.min.js',
+ libraryTarget: 'umd'
+ },
+
+ module: {
+ loaders: [
+ {
+ test: /\.js$/,
+ exclude: /(node_modules|bower_components|build)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: ['env', 'react']
+ }
+ }
+ }
+ ]
+ },
+
+ plugins: [
+ new webpack.DefinePlugin({
+ "process.env": {
+ NODE_ENV: JSON.stringify("production")
+ }
+ }),
+ new webpack.optimize.UglifyJsPlugin({
+ compress: {
+ warnings: false,
+ },
+ output: {
+ comments: false,
+ },
+ }),
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ template: 'src/ui/index.html'
+ })
+ ]
+}
diff --git a/2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.dev.config.js b/2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.dev.config.js
new file mode 100644
index 00000000..97817ef7
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.dev.config.js
@@ -0,0 +1,46 @@
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const path = require('path');
+
+module.exports = {
+ entry: './src/ui/index.js',
+ devtool: 'inline-source-map',
+ output: {
+ path: path.resolve(__dirname),
+ filename: 'bundle.js',
+ libraryTarget: 'umd'
+ },
+
+ devServer: {
+ contentBase: path.resolve(__dirname) + '/src/ui',
+ compress: true,
+ port: 9000,
+ host: 'localhost',
+ open: true,
+ before: (app) => {
+ app.get('/api/persons', (req, res) => res.send([
+ {id: '1', name: 'Привяу'}
+ ]));
+ }
+ },
+
+ module: {
+ loaders: [
+ {
+ test: /\.js$/,
+ exclude: /(node_modules|bower_components|build)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: ['env', 'react']
+ }
+ }
+ }
+ ]
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ filename: 'index.html',
+ template: 'src/ui/index.html'
+ })
+ ]
+}
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/.gitignore b/2021-02/spring-17-jquery/spring-17-jquery/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/.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/2021-02/spring-17-jquery/spring-17-jquery/pom.xml b/2021-02/spring-17-jquery/spring-17-jquery/pom.xml
new file mode 100644
index 00000000..cb4de97d
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/pom.xml
@@ -0,0 +1,60 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-17-jquery
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.4.RELEASE
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.webjars
+ jquery
+ 3.3.1
+
+
+
+
+ com.h2database
+ h2
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..d33c8908
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,27 @@
+package ru.otus.spring;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.repostory.PersonRepository;
+
+import javax.annotation.PostConstruct;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class);
+ }
+
+ @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
+ @Autowired
+ private PersonRepository repository;
+
+ @PostConstruct
+ public void init() {
+ repository.save(new Person("Pushkin"));
+ repository.save(new Person("Lermontov"));
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/domain/Person.java b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..374c55ac
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,37 @@
+package ru.otus.spring.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Person {
+
+ @Id
+ @GeneratedValue
+ private int id;
+ private String name;
+
+ public Person() {
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/page/PersonPagesController.java b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/page/PersonPagesController.java
new file mode 100644
index 00000000..0f3a2e76
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/page/PersonPagesController.java
@@ -0,0 +1,15 @@
+package ru.otus.spring.page;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Controller
+public class PersonPagesController {
+
+ @GetMapping("/")
+ public String listPage(Model model) {
+ model.addAttribute("keywords", "list users in Omsk, omsk, list users, list users free");
+ return "list";
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4b20e5b7
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/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/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/PersonController.java
new file mode 100644
index 00000000..8073a615
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/PersonController.java
@@ -0,0 +1,30 @@
+package ru.otus.spring.rest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.repostory.PersonRepository;
+import ru.otus.spring.rest.dto.PersonDto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+public class PersonController {
+
+ private final PersonRepository repository;
+
+ @Autowired
+ public PersonController(PersonRepository repository) {
+ this.repository = repository;
+ }
+
+ @GetMapping("/api/persons")
+ public List getAllPersons() {
+ return repository.findAll().stream().map(PersonDto::toDto)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/dto/PersonDto.java b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/dto/PersonDto.java
new file mode 100644
index 00000000..03918447
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/dto/PersonDto.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Russian Post
+ *
+ * This source code is Russian Post Confidential Proprietary.
+ * This software is protected by copyright. All rights and titles are reserved.
+ * You shall not use, copy, distribute, modify, decompile, disassemble or reverse engineer the software.
+ * Otherwise this violation would be treated by law and would be subject to legal prosecution.
+ * Legal use of the software provides receipt of a license from the right name only.
+ */
+package ru.otus.spring.rest.dto;
+
+import ru.otus.spring.domain.Person;
+
+/**
+ * DTO that represents Account
+ */
+@SuppressWarnings("all")
+public class PersonDto {
+
+ private int id = -1;
+ private String name;
+
+ public PersonDto() {
+ }
+
+ public PersonDto(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public static PersonDto toDto(Person person) {
+ return new PersonDto(person.getId(), person.getName());
+ }
+}
diff --git a/2021-02/spring-17-jquery/spring-17-jquery/src/main/resources/templates/list.html b/2021-02/spring-17-jquery/spring-17-jquery/src/main/resources/templates/list.html
new file mode 100644
index 00000000..a782b158
--- /dev/null
+++ b/2021-02/spring-17-jquery/spring-17-jquery/src/main/resources/templates/list.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+ List of all persons
+
+
+
+
+Persons:
+
+
+
+
+ | ID |
+ Name |
+
+
+
+
+
+
+
+
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/.gitignore b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/pom.xml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/pom.xml
new file mode 100644
index 00000000..c8c2505b
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-exercise
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.4
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 1.4.200
+
+
+ org.projectlombok
+ lombok
+ 1.18.18
+ provided
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..8916eb04
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..4ee1a3b3
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,4 @@
+package ru.otus.spring.dao;
+
+public interface PersonDao {
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..a6b41b84
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,15 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.stereotype.Repository;
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/application.yml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/application.yml
new file mode 100644
index 00000000..24f942fb
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/data.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/schema.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-exercise/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/.gitignore b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/pom.xml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/pom.xml
new file mode 100644
index 00000000..6ae65ed4
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-3
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.4
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 1.4.200
+
+
+ org.projectlombok
+ lombok
+ 1.18.18
+ provided
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..6c08834b
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDao.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..9f5581fd
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,6 @@
+package ru.otus.spring.dao;
+
+public interface PersonDao {
+
+ int count();
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..f069a7a2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,23 @@
+package ru.otus.spring.dao;
+
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ return jdbc.queryForObject("select count(*) from persons", Integer.class);
+ }
+
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/application.yml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/application.yml
new file mode 100644
index 00000000..24f942fb
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/data.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/schema.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-3/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/.gitignore b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/pom.xml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/pom.xml
new file mode 100644
index 00000000..09235f12
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-4
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.4
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 1.4.200
+
+
+ org.projectlombok
+ lombok
+ 1.18.18
+ provided
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..2bc8e898
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDao.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..73109a4a
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,12 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+import java.util.List;
+
+public interface PersonDao {
+
+ int count();
+
+ void insert(Person person);
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..35b648f2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,33 @@
+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;
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ return jdbc.queryForObject("select count(*) from persons", Integer.class);
+ }
+
+ @Override
+ public void insert(Person person) {
+ jdbc.update("insert into persons (id, `name`) values (?, ?)", person.getId(), person.getName());
+ }
+
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/application.yml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/application.yml
new file mode 100644
index 00000000..24f942fb
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/data.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/schema.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-4/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/.gitignore b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/pom.xml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/pom.xml
new file mode 100644
index 00000000..156e0899
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-5
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.4
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 1.4.200
+
+
+ org.projectlombok
+ lombok
+ 1.18.18
+ provided
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..4bc2aca8
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDao.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..e2995f92
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,16 @@
+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();
+}
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..9685df76
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
@@ -0,0 +1,56 @@
+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;
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+public class PersonDaoJdbc implements PersonDao {
+
+ private final JdbcOperations jdbc;
+
+ public PersonDaoJdbc(JdbcOperations jdbcOperations)
+ {
+ this.jdbc = jdbcOperations;
+ }
+
+ @Override
+ public int count() {
+ return jdbc.queryForObject("select count(*) from persons", Integer.class);
+ }
+
+ @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 * from persons where id = ?", new Object[] {id}, new PersonMapper());
+ }
+
+ @Override
+ public List getAll() {
+ return jdbc.query("select * from persons", new PersonMapper());
+ }
+
+
+ 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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/application.yml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/application.yml
new file mode 100644
index 00000000..24f942fb
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/data.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/schema.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-5/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/.gitignore b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/pom.xml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/pom.xml
new file mode 100644
index 00000000..94dc7a32
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+
+ ru.otus
+ jdbc-demo-solution-final
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.4
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.h2database
+ h2
+ 1.4.200
+
+
+ org.projectlombok
+ lombok
+ 1.18.18
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..05636811
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDao.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..f2c30f7c
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/dao/PersonDaoJdbc.java
new file mode 100644
index 00000000..f70dffc7
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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;
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@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() {
+ return jdbc.queryForObject("select count(*) from persons", Integer.class);
+ }
+
+ @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 * from persons where id = :id", params, new PersonMapper()
+ );
+ }
+
+ @Override
+ public List getAll() {
+ return jdbc.query("select * 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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..a78f21e5
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/application.yml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/application.yml
new file mode 100644
index 00000000..24f942fb
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+
+ h2:
+ console:
+ path: /h2-console
+ settings:
+ web-allow-others: true
\ No newline at end of file
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/data.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/data.sql
new file mode 100644
index 00000000..240a5b4e
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'masha');
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/schema.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/main/resources/schema.sql
new file mode 100644
index 00000000..87aadc44
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/java/ru/otus/spring/dao/PersonDaoJdbcTest.java
new file mode 100644
index 00000000..4ad937d2
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/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 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)
+ .usingFieldByFieldElementComparator()
+ .containsExactlyInAnyOrder(expectedPerson);
+ }
+
+}
\ No newline at end of file
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/resources/application.yml b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/resources/application.yml
new file mode 100644
index 00000000..92d26750
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/resources/application.yml
@@ -0,0 +1,5 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ initialization-mode: always
+ data: data.sql
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/resources/data.sql b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/resources/data.sql
new file mode 100644
index 00000000..32d79cd1
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/jdbc-demo-solution-final/src/test/resources/data.sql
@@ -0,0 +1 @@
+insert into persons (id, `name`) values (1, 'Ivan');
diff --git a/2021-03/spring-09-jdbc/jdbc-class-work/pom.xml b/2021-03/spring-09-jdbc/jdbc-class-work/pom.xml
new file mode 100644
index 00000000..1c941227
--- /dev/null
+++ b/2021-03/spring-09-jdbc/jdbc-class-work/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
+
+
diff --git a/2021-03/spring-10-orm/demo-projects/.gitignore b/2021-03/spring-10-orm/demo-projects/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/.gitignore b/2021-03/spring-10-orm/demo-projects/mybatis-demo/.gitignore
new file mode 100644
index 00000000..153c9335
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/.gitignore
@@ -0,0 +1,29 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/
+
+### VS Code ###
+.vscode/
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/README.md b/2021-03/spring-10-orm/demo-projects/mybatis-demo/README.md
new file mode 100644
index 00000000..44635273
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/README.md
@@ -0,0 +1,2 @@
+# mybatis-demo
+Пример работы с БД через MyBatis
\ No newline at end of file
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/pom.xml b/2021-03/spring-10-orm/demo-projects/mybatis-demo/pom.xml
new file mode 100644
index 00000000..642579ed
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/pom.xml
@@ -0,0 +1,61 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.5
+
+
+ ru.otus.example
+ mybatis-demo
+ 0.0.1-SNAPSHOT
+ mybatis-demo
+ MyBatis demo
+
+
+ 11
+ 11
+ 11
+ 2.1.4
+
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ ${mybatis.version}
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/MyBatisDemoApplication.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/MyBatisDemoApplication.java
new file mode 100644
index 00000000..ab5f7513
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/MyBatisDemoApplication.java
@@ -0,0 +1,14 @@
+package ru.otus.example.mybatisdemo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+@SpringBootApplication
+public class MyBatisDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(MyBatisDemoApplication.class, args);
+ }
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Avatar.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Avatar.java
new file mode 100644
index 00000000..5c8f4728
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Avatar.java
@@ -0,0 +1,13 @@
+package ru.otus.example.mybatisdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Avatar {
+ private long id;
+ private String photoUrl;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Course.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Course.java
new file mode 100644
index 00000000..6aa8cff7
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Course.java
@@ -0,0 +1,13 @@
+package ru.otus.example.mybatisdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Course {
+ private long id;
+ private String name;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/EMail.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/EMail.java
new file mode 100644
index 00000000..8fa43ebd
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/EMail.java
@@ -0,0 +1,13 @@
+package ru.otus.example.mybatisdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EMail {
+ private long id;
+ private String email;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/OtusStudent.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/OtusStudent.java
new file mode 100644
index 00000000..afb324fb
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/OtusStudent.java
@@ -0,0 +1,18 @@
+package ru.otus.example.mybatisdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OtusStudent {
+ private long id;
+ private String name;
+ private Avatar avatar;
+ private List emails;
+ private List courses;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/AvatarRepository.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/AvatarRepository.java
new file mode 100644
index 00000000..634d1aba
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/AvatarRepository.java
@@ -0,0 +1,18 @@
+package ru.otus.example.mybatisdemo.repositories;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Result;
+import org.apache.ibatis.annotations.Results;
+import org.apache.ibatis.annotations.Select;
+import ru.otus.example.mybatisdemo.models.Avatar;
+
+@Mapper
+public interface AvatarRepository {
+ @Select("select * from avatars where id = #{id}")
+ @Results(value = {
+ @Result(property = "id", column = "id"),
+ @Result(property = "photoUrl", column = "photo_url")
+ })
+ Avatar getAvatarById(long id);
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/CourseRepository.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/CourseRepository.java
new file mode 100644
index 00000000..20271981
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/CourseRepository.java
@@ -0,0 +1,17 @@
+package ru.otus.example.mybatisdemo.repositories;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import ru.otus.example.mybatisdemo.models.Course;
+
+import java.util.List;
+
+@Mapper
+public interface CourseRepository {
+
+ @Select("select * " +
+ "from student_courses sc left join courses c on sc.course_id = c.id " +
+ "where sc.student_id = #{studentId}")
+ List getCoursesByStudentId(long studentId);
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/EmailRepository.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/EmailRepository.java
new file mode 100644
index 00000000..989b681d
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/EmailRepository.java
@@ -0,0 +1,14 @@
+package ru.otus.example.mybatisdemo.repositories;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import ru.otus.example.mybatisdemo.models.EMail;
+
+import java.util.List;
+
+@Mapper
+public interface EmailRepository {
+
+ @Select("select * from emails where student_id = #{studentId}")
+ List getEmailsByStudentId(long studentId);
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepository.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepository.java
new file mode 100644
index 00000000..82a8260b
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepository.java
@@ -0,0 +1,42 @@
+package ru.otus.example.mybatisdemo.repositories;
+
+import org.apache.ibatis.annotations.*;
+import org.apache.ibatis.mapping.FetchType;
+import ru.otus.example.mybatisdemo.models.Avatar;
+import ru.otus.example.mybatisdemo.models.OtusStudent;
+
+import java.util.List;
+
+@Mapper
+public interface OtusStudentRepository {
+
+ @Select("select * from otus_students")
+ @Results(id = "studentAllMap", value = {
+ @Result(property = "id", column = "id"),
+ @Result(property = "name", column = "name"),
+ @Result(property = "avatar", column = "avatar_id", javaType = Avatar.class,
+ one = @One(select = "ru.otus.example.mybatisdemo.repositories.AvatarRepository.getAvatarById", fetchType = FetchType.EAGER)),
+ @Result(property = "emails", column = "id", javaType = List.class,
+ many = @Many(select = "ru.otus.example.mybatisdemo.repositories.EmailRepository.getEmailsByStudentId", fetchType = FetchType.EAGER)),
+ @Result(property = "courses", column = "id", javaType = List.class,
+ many = @Many(select = "ru.otus.example.mybatisdemo.repositories.CourseRepository.getCoursesByStudentId", fetchType = FetchType.EAGER))
+ })
+ List findAllWithAllInfo();
+
+ @Select("select * from otus_students where id = #{id}")
+ @ResultMap("studentAllMap")
+ OtusStudent findById(long id);
+
+ @Select("select count(*) as students_count from otus_students")
+ long getStudentsCount();
+
+ @Insert("insert into otus_students(name, avatar_id) values (#{name}, #{avatar.id})")
+ void insert(OtusStudent student);
+
+ @Update("update otus_students set name = #{name} where id = #{id}")
+ void updateName(OtusStudent student);
+
+ @Delete("delete from otus_students where id = #{id}")
+ void deleteById(long id);
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/resources/schema.sql b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/resources/schema.sql
new file mode 100644
index 00000000..43a684bb
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/resources/schema.sql
@@ -0,0 +1,31 @@
+create table avatars(
+ id bigserial,
+ photo_url varchar(8000),
+ primary key (id)
+);
+
+create table courses(
+ id bigserial,
+ name varchar(255),
+ primary key (id)
+);
+
+create table otus_students(
+ id bigserial,
+ name varchar(255),
+ avatar_id bigint references avatars (id),
+ primary key (id)
+);
+
+create table emails(
+ id bigserial,
+ student_id bigint references otus_students(id) on delete cascade,
+ email varchar(255),
+ primary key (id)
+);
+
+create table student_courses(
+ student_id bigint references otus_students(id) on delete cascade,
+ course_id bigint references courses(id),
+ primary key (student_id, course_id)
+);
\ No newline at end of file
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepositoryTest.java b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepositoryTest.java
new file mode 100644
index 00000000..d9a24bce
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepositoryTest.java
@@ -0,0 +1,110 @@
+package ru.otus.example.mybatisdemo.repositories;
+
+import lombok.val;
+import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.example.mybatisdemo.models.Avatar;
+import ru.otus.example.mybatisdemo.models.OtusStudent;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@DisplayName("Репозиторий на основе MyBatis для работы со студентами ")
+@SpringBootTest
+@Transactional
+public class OtusStudentRepositoryTest {
+
+ private static final String FIELD_ID = "id";
+ private static final String FIELD_PHOTO_URL = "photoUrl";
+ private static final String FIELD_NAME = "name";
+
+ private static final long FIRST_STUDENT_ID = 1L;
+ private static final long FIRST_AVATAR_ID = 1L;
+ private static final String FIRST_STUDENT_NAME = "student_01";
+ private static final String FIRST_AVATAR_URL = "photoUrl_01";
+ private static final String STUDENT_NEW_NAME = "Висусуалий";
+
+ private static final int EXPECTED_NUMBER_OF_STUDENTS = 10;
+ private static final long INSERTED_STUDENT_ID = 11L;
+ private static final int EXPECTED_EMAILS_COUNT = 2;
+ private static final int EXPECTED_COURSES_COUNT = 3;
+
+ @Autowired
+ private OtusStudentRepository studentRepositoryMyBatis;
+
+ @DisplayName("должен загружать список всех студентов с полной информацией о них")
+ @Test
+ void shouldReturnCorrectStudentsListWithAllInfo() {
+ val students = studentRepositoryMyBatis.findAllWithAllInfo();
+ assertThat(students).isNotNull().hasSize(EXPECTED_NUMBER_OF_STUDENTS)
+ .allMatch(s -> !s.getName().equals(""))
+ .allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
+ .allMatch(s -> s.getAvatar() != null)
+ .allMatch(s -> s.getEmails() != null && s.getEmails().size() > 0);
+ }
+
+ @DisplayName("должен загружать число студентов в БД")
+ @Test
+ void shouldReturnCorrectStudentsCount() {
+ long studentsCount = studentRepositoryMyBatis.getStudentsCount();
+ assertThat(studentsCount).isEqualTo(EXPECTED_NUMBER_OF_STUDENTS);
+ }
+
+ @DisplayName(" должен загружать информацию о нужном студенте")
+ @Test
+ void shouldFindExpectedStudentById(){
+ val actualStudent = studentRepositoryMyBatis.findById(FIRST_STUDENT_ID);
+
+ assertThat(actualStudent).isNotNull();
+ assertThat(actualStudent.getName()).isEqualTo(FIRST_STUDENT_NAME);
+ assertThat(actualStudent.getAvatar()).isNotNull()
+ .hasFieldOrPropertyWithValue(FIELD_ID, FIRST_STUDENT_ID)
+ .hasFieldOrPropertyWithValue(FIELD_PHOTO_URL, FIRST_AVATAR_URL);
+ assertThat(actualStudent.getEmails()).isNotNull().hasSize(EXPECTED_EMAILS_COUNT);
+ assertThat(actualStudent.getCourses()).isNotNull().hasSize(EXPECTED_COURSES_COUNT);
+ }
+
+ @DisplayName(" должен сохранить, а потом загрузить информацию о нужном студенте")
+ @Test
+ void shouldSaveAndLoadCorrectStudent() {
+ val expectedStudent = new OtusStudent(0, STUDENT_NEW_NAME,
+ new Avatar(FIRST_AVATAR_ID, FIRST_AVATAR_URL), List.of(), List.of());
+ studentRepositoryMyBatis.insert(expectedStudent);
+ val actualStudent = studentRepositoryMyBatis.findById(INSERTED_STUDENT_ID);
+
+ assertThat(actualStudent)
+ .isNotNull()
+ .usingRecursiveComparison(
+ RecursiveComparisonConfiguration.builder()
+ .withIgnoredFields(FIELD_ID).build())
+ .isEqualTo(expectedStudent);
+ }
+
+
+ @DisplayName(" должен обновлять имя студента в БД")
+ @Test
+ void shouldUpdateStudentName() {
+ val student = studentRepositoryMyBatis.findById(FIRST_STUDENT_ID);
+ student.setName(STUDENT_NEW_NAME);
+ studentRepositoryMyBatis.updateName(student);
+ val actualStudent = studentRepositoryMyBatis.findById(FIRST_STUDENT_ID);
+
+ assertThat(actualStudent).isNotNull().hasFieldOrPropertyWithValue(FIELD_NAME, student.getName());
+ }
+
+ @DisplayName("должен удалять студента из БД по id")
+ @Test
+ void shouldDeleteStudentFromDbById() {
+ val studentsCountBefore = studentRepositoryMyBatis.getStudentsCount();
+ studentRepositoryMyBatis.deleteById(FIRST_STUDENT_ID);
+ val studentsCountAfter = studentRepositoryMyBatis.getStudentsCount();
+
+ assertThat(studentsCountBefore - studentsCountAfter).isEqualTo(1);
+ }
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/application.yml b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/application.yml
new file mode 100644
index 00000000..dc237b00
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/application.yml
@@ -0,0 +1,8 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ initialization-mode: always
+
+logging:
+ level:
+ ru.otus.example.mybatisdemo.repositories: TRACE
\ No newline at end of file
diff --git a/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/data.sql b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/data.sql
new file mode 100644
index 00000000..a8db6b85
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/data.sql
@@ -0,0 +1,29 @@
+insert into avatars(photo_url)
+values ('photoUrl_01'), ('photoUrl_02'), ('photoUrl_03'), ('photoUrl_04'), ('photoUrl_05'),
+ ('photoUrl_06'), ('photoUrl_07'), ('photoUrl_08'), ('photoUrl_09'), ('photoUrl_10');
+
+insert into courses(name)
+values ('course_name_01'), ('course_name_02'), ('course_name_03'), ('course_name_04'), ('course_name_05'),
+ ('course_name_06'), ('course_name_07'), ('course_name_08'), ('course_name_09'), ('course_name_10'), ('not_used_11');
+
+insert into otus_students(name, avatar_id)
+values ('student_01', 1), ('student_02', 2), ('student_03', 3), ('student_04', 4), ('student_05', 5),
+ ('student_06', 6), ('student_07', 7), ('student_08', 8), ('student_09', 9), ('student_10', 10);
+
+
+insert into emails(email, student_id)
+values ('email_01', 1), ('email_02', 1), ('email_03', 2), ('email_04', 2), ('email_05', 3), ('email_06', 4),
+ ('email_07', 5), ('email_08', 6), ('email_09', 7), ('email_10', 8), ('email_11', 9), ('email_12', 10);
+
+
+insert into student_courses(student_id, course_id)
+values (1, 1), (1, 2), (1, 3),
+ (2, 2), (2, 4), (2, 5),
+ (3, 3), (3, 6), (3, 7),
+ (4, 4), (4, 8), (4, 9),
+ (5, 5), (5, 10), (5, 1),
+ (6, 6), (6, 2), (6, 3),
+ (7, 7), (7, 4), (7, 5),
+ (8, 8), (8, 6), (8, 7),
+ (9, 9), (9, 8), (9, 10),
+ (10, 10), (10, 1), (10, 2);
diff --git a/2021-03/spring-10-orm/demo-projects/pom.xml b/2021-03/spring-10-orm/demo-projects/pom.xml
new file mode 100644
index 00000000..711fadf0
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ ru.otus
+ demo-projects
+ 1.0
+
+ pom
+
+
+ spring-jdbc-demo
+ mybatis-demo
+
+
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/.gitignore b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/.gitignore
new file mode 100644
index 00000000..153c9335
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/.gitignore
@@ -0,0 +1,29 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/
+
+### VS Code ###
+.vscode/
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/README.md b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/README.md
new file mode 100644
index 00000000..462216c3
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/README.md
@@ -0,0 +1,2 @@
+# spring-jdbc-demo
+Пример работы с БД через jdbc
\ No newline at end of file
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/pom.xml b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/pom.xml
new file mode 100644
index 00000000..450b4982
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/pom.xml
@@ -0,0 +1,57 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.5
+
+
+ ru.otus.example
+ spring-jdbc-demo
+ 0.0.1-SNAPSHOT
+ spring-jdbc-demo
+ Spring jdbc demo
+
+
+ 11
+ 11
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jdbc
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/SpringJdbcDemoApplication.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/SpringJdbcDemoApplication.java
new file mode 100644
index 00000000..28ebfec4
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/SpringJdbcDemoApplication.java
@@ -0,0 +1,13 @@
+package ru.otus.example.springjdbcdemo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringJdbcDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringJdbcDemoApplication.class, args);
+ }
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Avatar.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Avatar.java
new file mode 100644
index 00000000..a1963ea4
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Avatar.java
@@ -0,0 +1,13 @@
+package ru.otus.example.springjdbcdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Avatar {
+ private long id;
+ private String photoUrl;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Course.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Course.java
new file mode 100644
index 00000000..07b6e2c2
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Course.java
@@ -0,0 +1,13 @@
+package ru.otus.example.springjdbcdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Course {
+ private long id;
+ private String name;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/EMail.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/EMail.java
new file mode 100644
index 00000000..16985ad5
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/EMail.java
@@ -0,0 +1,13 @@
+package ru.otus.example.springjdbcdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EMail {
+ private long id;
+ private String email;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/OtusStudent.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/OtusStudent.java
new file mode 100644
index 00000000..5ae9167c
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/OtusStudent.java
@@ -0,0 +1,18 @@
+package ru.otus.example.springjdbcdemo.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OtusStudent {
+ private long id;
+ private String name;
+ private Avatar avatar;
+ private List emails;
+ private List courses;
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbc.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbc.java
new file mode 100644
index 00000000..716ab880
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbc.java
@@ -0,0 +1,9 @@
+package ru.otus.example.springjdbcdemo.repositories;
+
+import ru.otus.example.springjdbcdemo.models.Course;
+
+import java.util.List;
+
+public interface CourseRepositoryJdbc {
+ List findAllUsed();
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbcImpl.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbcImpl.java
new file mode 100644
index 00000000..53915e14
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbcImpl.java
@@ -0,0 +1,36 @@
+package ru.otus.example.springjdbcdemo.repositories;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+import ru.otus.example.springjdbcdemo.models.Course;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+@Repository
+@RequiredArgsConstructor
+public class CourseRepositoryJdbcImpl implements CourseRepositoryJdbc {
+
+ @Autowired
+ private final JdbcOperations op;
+
+ @Override
+ public List findAllUsed() {
+ return op.query("select c.id, c.name " +
+ "from courses c inner join student_courses sc on c.id = sc.course_id " +
+ "group by c.id, c.name " +
+ "order by c.name", new CourseRowMapper());
+ }
+
+ private static class CourseRowMapper implements RowMapper {
+ @Override
+ public Course mapRow(ResultSet rs, int i) throws SQLException {
+ return new Course(rs.getLong(1), rs.getString(2));
+ }
+ }
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbc.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbc.java
new file mode 100644
index 00000000..0761373d
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbc.java
@@ -0,0 +1,9 @@
+package ru.otus.example.springjdbcdemo.repositories;
+
+import ru.otus.example.springjdbcdemo.models.OtusStudent;
+
+import java.util.List;
+
+public interface OtusStudentRepositoryJdbc {
+ List findAllWithAllInfo();
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImpl.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImpl.java
new file mode 100644
index 00000000..571b4f21
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImpl.java
@@ -0,0 +1,52 @@
+package ru.otus.example.springjdbcdemo.repositories;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.stereotype.Repository;
+import ru.otus.example.springjdbcdemo.models.Course;
+import ru.otus.example.springjdbcdemo.models.OtusStudent;
+import ru.otus.example.springjdbcdemo.repositories.ext.OtusStudentResultSetExtractor;
+import ru.otus.example.springjdbcdemo.repositories.ext.StudentCourseRelation;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Repository
+@RequiredArgsConstructor
+public class OtusStudentRepositoryJdbcImpl implements OtusStudentRepositoryJdbc {
+
+ private final CourseRepositoryJdbc courseRepository;
+ private final JdbcOperations op;
+
+ @Override
+ public List findAllWithAllInfo() {
+ List courses = courseRepository.findAllUsed();
+ List relations = getAllRelations();
+ Map students =
+ op.query("select os.id, os.name, a.id avatar_id, a.photo_url, e.id email_id, e.email " +
+ "from (otus_students os left join avatars a on " +
+ "os.avatar_id = a.id) left join emails e on os.id = e.student_id",
+ new OtusStudentResultSetExtractor());
+
+ mergeStudentsInfo(students, courses, relations);
+ return new ArrayList<>(Objects.requireNonNull(students).values());
+ }
+
+ private List getAllRelations() {
+ return op.query("select student_id, course_id from student_courses sc order by student_id, course_id",
+ (rs, i) -> new StudentCourseRelation(rs.getLong(1), rs.getLong(2)));
+ }
+
+ private void mergeStudentsInfo(Map students, List courses,
+ List relations) {
+ Map coursesMap = courses.stream().collect(Collectors.toMap(Course::getId, Function.identity()));
+ relations.forEach(r -> {
+ if (students.containsKey(r.getStudentId()) && coursesMap.containsKey(r.getCourseId())) {
+ students.get(r.getStudentId()).getCourses().add(coursesMap.get(r.getCourseId()));
+ }
+ });
+ }
+
+
+}
diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/OtusStudentResultSetExtractor.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/OtusStudentResultSetExtractor.java
new file mode 100644
index 00000000..778a46b7
--- /dev/null
+++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/OtusStudentResultSetExtractor.java
@@ -0,0 +1,37 @@
+package ru.otus.example.springjdbcdemo.repositories.ext;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.ResultSetExtractor;
+import ru.otus.example.springjdbcdemo.models.Avatar;
+import ru.otus.example.springjdbcdemo.models.EMail;
+import ru.otus.example.springjdbcdemo.models.OtusStudent;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class OtusStudentResultSetExtractor implements
+ ResultSetExtractor