From 93fa89deea636c6f3e7218f0d4e200577072acaf Mon Sep 17 00:00:00 2001 From: Yuriy Dvorzhetskiy Date: Sat, 24 Apr 2021 13:38:57 +0600 Subject: [PATCH 01/25] 2021-02 - 17 --- .../spring-17-boot-and-react/.gitignore | 30 +++++++ .../spring-17-boot-and-react/package.json | 26 ++++++ .../spring-17-boot-and-react/pom.xml | 88 +++++++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 27 ++++++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++++ .../spring/repostory/PersonRepository.java | 11 +++ .../ru/otus/spring/rest/PersonController.java | 30 +++++++ .../ru/otus/spring/rest/dto/PersonDto.java | 50 +++++++++++ .../src/ui/components/App.js | 45 ++++++++++ .../src/ui/index.html | 15 ++++ .../spring-17-boot-and-react/src/ui/index.js | 9 ++ .../webpack.config.js | 47 ++++++++++ .../webpack.dev.config.js | 46 ++++++++++ .../spring-17-jquery/.gitignore | 24 +++++ .../spring-17-jquery/spring-17-jquery/pom.xml | 60 +++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 27 ++++++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++++ .../spring/page/PersonPagesController.java | 15 ++++ .../spring/repostory/PersonRepository.java | 11 +++ .../ru/otus/spring/rest/PersonController.java | 30 +++++++ .../ru/otus/spring/rest/dto/PersonDto.java | 50 +++++++++++ .../src/main/resources/templates/list.html | 47 ++++++++++ 22 files changed, 762 insertions(+) create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/.gitignore create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/package.json create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/pom.xml create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/main/java/ru/otus/spring/rest/dto/PersonDto.java create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/components/App.js create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.html create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/src/ui/index.js create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.config.js create mode 100644 2021-02/spring-17-jquery/spring-17-boot-and-react/webpack.dev.config.js create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/.gitignore create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/pom.xml create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/page/PersonPagesController.java create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/java/ru/otus/spring/rest/dto/PersonDto.java create mode 100644 2021-02/spring-17-jquery/spring-17-jquery/src/main/resources/templates/list.html 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 ( + +
+ + + + + + + + + { + this.state.persons.map((person, i) => ( + + + + + )) + } + +
IDName
{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:

+ + + + + + + + + + +
IDName
+ + + From 1e016a7f5aa39187d1554e09137c58a1723f20d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Sun, 25 Apr 2021 20:38:29 +0400 Subject: [PATCH 02/25] 2021-03 spring-10-orm added --- .../spring-10-orm/demo-projects/.gitignore | 4 + .../demo-projects/mybatis-demo/.gitignore | 29 +++++ .../demo-projects/mybatis-demo/README.md | 2 + .../demo-projects/mybatis-demo/pom.xml | 61 ++++++++++ .../mybatisdemo/MyBatisDemoApplication.java | 14 +++ .../example/mybatisdemo/models/Avatar.java | 13 +++ .../example/mybatisdemo/models/Course.java | 13 +++ .../example/mybatisdemo/models/EMail.java | 13 +++ .../mybatisdemo/models/OtusStudent.java | 18 +++ .../repositories/AvatarRepository.java | 18 +++ .../repositories/CourseRepository.java | 17 +++ .../repositories/EmailRepository.java | 14 +++ .../repositories/OtusStudentRepository.java | 42 +++++++ .../src/main/resources/schema.sql | 31 +++++ .../OtusStudentRepositoryTest.java | 110 ++++++++++++++++++ .../src/test/resources/application.yml | 8 ++ .../mybatis-demo/src/test/resources/data.sql | 29 +++++ 2021-03/spring-10-orm/demo-projects/pom.xml | 17 +++ .../demo-projects/spring-jdbc-demo/.gitignore | 29 +++++ .../demo-projects/spring-jdbc-demo/README.md | 2 + .../demo-projects/spring-jdbc-demo/pom.xml | 57 +++++++++ .../SpringJdbcDemoApplication.java | 13 +++ .../example/springjdbcdemo/models/Avatar.java | 13 +++ .../example/springjdbcdemo/models/Course.java | 13 +++ .../example/springjdbcdemo/models/EMail.java | 13 +++ .../springjdbcdemo/models/OtusStudent.java | 18 +++ .../repositories/CourseRepositoryJdbc.java | 9 ++ .../CourseRepositoryJdbcImpl.java | 36 ++++++ .../OtusStudentRepositoryJdbc.java | 9 ++ .../OtusStudentRepositoryJdbcImpl.java | 52 +++++++++ .../ext/OtusStudentResultSetExtractor.java | 37 ++++++ .../ext/StudentCourseRelation.java | 11 ++ .../src/main/resources/schema.sql | 31 +++++ .../OtusStudentRepositoryJdbcImplTest.java | 35 ++++++ .../src/test/resources/application.yml | 4 + .../src/test/resources/data.sql | 29 +++++ .../orm-class-work/orm-exercise/.gitignore | 29 +++++ .../orm-class-work/orm-exercise/pom.xml | 57 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 15 +++ .../otus/example/ormdemo/models/Course.java | 15 +++ .../ru/otus/example/ormdemo/models/EMail.java | 15 +++ .../example/ormdemo/models/OtusStudent.java | 18 +++ .../src/main/resources/application.yml | 20 ++++ .../orm-class-work/orm-solution-01/.gitignore | 29 +++++ .../orm-class-work/orm-solution-01/pom.xml | 57 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 17 +++ .../otus/example/ormdemo/models/Course.java | 17 +++ .../ru/otus/example/ormdemo/models/EMail.java | 17 +++ .../example/ormdemo/models/OtusStudent.java | 20 ++++ .../src/main/resources/application.yml | 20 ++++ .../orm-class-work/orm-solution-02/.gitignore | 29 +++++ .../orm-class-work/orm-solution-02/pom.xml | 56 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 20 ++++ .../otus/example/ormdemo/models/Course.java | 20 ++++ .../ru/otus/example/ormdemo/models/EMail.java | 21 ++++ .../example/ormdemo/models/OtusStudent.java | 24 ++++ .../src/main/resources/application.yml | 20 ++++ .../orm-class-work/orm-solution-03/.gitignore | 29 +++++ .../orm-class-work/orm-solution-03/pom.xml | 57 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 21 ++++ .../otus/example/ormdemo/models/Course.java | 21 ++++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++++ .../example/ormdemo/models/OtusStudent.java | 25 ++++ .../src/main/resources/application.yml | 20 ++++ .../orm-class-work/orm-solution-04/.gitignore | 29 +++++ .../orm-class-work/orm-solution-04/pom.xml | 58 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 21 ++++ .../otus/example/ormdemo/models/Course.java | 21 ++++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++++ .../example/ormdemo/models/OtusStudent.java | 30 +++++ .../src/main/resources/application.yml | 20 ++++ .../orm-class-work/orm-solution-05/.gitignore | 29 +++++ .../orm-class-work/orm-solution-05/pom.xml | 57 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 21 ++++ .../otus/example/ormdemo/models/Course.java | 21 ++++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++++ .../example/ormdemo/models/OtusStudent.java | 34 ++++++ .../src/main/resources/application.yml | 20 ++++ .../orm-solution-final/.gitignore | 29 +++++ .../orm-class-work/orm-solution-final/pom.xml | 57 +++++++++ .../example/ormdemo/OrmDemoApplication.java | 17 +++ .../otus/example/ormdemo/models/Avatar.java | 21 ++++ .../otus/example/ormdemo/models/Course.java | 21 ++++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++++ .../example/ormdemo/models/OtusStudent.java | 39 +++++++ .../src/main/resources/application.yml | 20 ++++ 2021-03/spring-10-orm/orm-class-work/pom.xml | 22 ++++ 93 files changed, 2350 insertions(+) create mode 100644 2021-03/spring-10-orm/demo-projects/.gitignore create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/.gitignore create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/README.md create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/pom.xml create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/MyBatisDemoApplication.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/AvatarRepository.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/CourseRepository.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/EmailRepository.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepository.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/main/resources/schema.sql create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/java/ru/otus/example/mybatisdemo/repositories/OtusStudentRepositoryTest.java create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/application.yml create mode 100644 2021-03/spring-10-orm/demo-projects/mybatis-demo/src/test/resources/data.sql create mode 100644 2021-03/spring-10-orm/demo-projects/pom.xml create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/.gitignore create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/README.md create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/pom.xml create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/SpringJdbcDemoApplication.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbc.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/CourseRepositoryJdbcImpl.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbc.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImpl.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/OtusStudentResultSetExtractor.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/StudentCourseRelation.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/resources/schema.sql create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImplTest.java create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/application.yml create mode 100644 2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/data.sql create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/.gitignore create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/pom.xml create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/resources/application.yml create mode 100644 2021-03/spring-10-orm/orm-class-work/pom.xml 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> { + @Override + public Map extractData(ResultSet rs) throws SQLException, + DataAccessException { + + Map students = new HashMap<>(); + while (rs.next()) { + long id = rs.getLong("id"); + OtusStudent student = students.get(id); + if (student == null) { + student = new OtusStudent(id, rs.getString("name"), + new Avatar(rs.getLong("avatar_id"), rs.getString("photo_url")), + new ArrayList<>(), new ArrayList<>()); + students.put(student.getId(), student); + } + + student.getEmails().add(new EMail(rs.getLong("email_id"), + rs.getString("email"))); + } + return students; + } +} diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/StudentCourseRelation.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/StudentCourseRelation.java new file mode 100644 index 00000000..408c97d2 --- /dev/null +++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/java/ru/otus/example/springjdbcdemo/repositories/ext/StudentCourseRelation.java @@ -0,0 +1,11 @@ +package ru.otus.example.springjdbcdemo.repositories.ext; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +public class StudentCourseRelation { + private final long studentId; + private final long courseId; +} diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/resources/schema.sql b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-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/spring-jdbc-demo/src/test/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImplTest.java b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImplTest.java new file mode 100644 index 00000000..dab99810 --- /dev/null +++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/java/ru/otus/example/springjdbcdemo/repositories/OtusStudentRepositoryJdbcImplTest.java @@ -0,0 +1,35 @@ +package ru.otus.example.springjdbcdemo.repositories; + +import lombok.val; +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("Репозиторий на основе Jdbc для работы со студентами ") +@JdbcTest +@Import({OtusStudentRepositoryJdbcImpl.class, CourseRepositoryJdbcImpl.class}) +class OtusStudentRepositoryJdbcImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + + @Autowired + private OtusStudentRepositoryJdbcImpl repositoryJdbc; + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + val students = repositoryJdbc.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); + students.forEach(System.out::println); + + } +} \ No newline at end of file diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/application.yml b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/application.yml new file mode 100644 index 00000000..e1e538a4 --- /dev/null +++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/application.yml @@ -0,0 +1,4 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always \ No newline at end of file diff --git a/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/data.sql b/2021-03/spring-10-orm/demo-projects/spring-jdbc-demo/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-10-orm/demo-projects/spring-jdbc-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/orm-class-work/orm-exercise/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-exercise/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/.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/orm-class-work/orm-exercise/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-exercise/pom.xml new file mode 100644 index 00000000..ebcfd330 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-exercise + orm-exercise + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..52c8b747 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,15 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Avatar { + private long id; + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..adbde731 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,15 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Course { + private long id; + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..d7ed5610 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,15 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EMail { + private long id; + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..49d7bcba --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +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/orm-class-work/orm-exercise/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-exercise/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-01/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/.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/orm-class-work/orm-solution-01/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/pom.xml new file mode 100644 index 00000000..d0fd85c7 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-solution-01 + orm-solution-01 + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..d9cca2ca --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Avatar { + @Id + private long id; + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..a02a15f8 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Course { + @Id + private long id; + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..e71e8ac9 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class EMail { + @Id + private long id; + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..ec9dfaa5 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,20 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + private long id; + private String name; + + //private Avatar avatar; + //private List emails; + //private List courses; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-01/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-02/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/.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/orm-class-work/orm-solution-02/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/pom.xml new file mode 100644 index 00000000..7e629c29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-solution-02 + orm-solution-02 + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..d60a4f32 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,20 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + private long id; + + @Column(name = "photo_url") + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..8a677513 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,20 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + private long id; + + @Column(name = "name") + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..8f15fd87 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + private long id; + + @Column(name = "email") + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..a509d959 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,24 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name") + private String name; + + //private Avatar avatar; + //private List emails; + //private List courses; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-02/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-03/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/.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/orm-class-work/orm-solution-03/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/pom.xml new file mode 100644 index 00000000..cfdce293 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-solution-03 + orm-solution-03 + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..6d7bc172 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url") + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..8fb4b707 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name") + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..9c999a97 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email") + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..31d00f58 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,25 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Позволяет указать стратегию генерации id + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name") + private String name; + + //private Avatar avatar; + //private List emails; + //private List courses; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-03/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-04/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/.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/orm-class-work/orm-solution-04/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/pom.xml new file mode 100644 index 00000000..9a35c02a --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-solution-04 + orm-solution-04 + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..6d7bc172 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url") + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..8fb4b707 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name") + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..9c999a97 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email") + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..44eef5ed --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,30 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + //private List emails; + //private List courses; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-04/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-05/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/.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/orm-class-work/orm-solution-05/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/pom.xml new file mode 100644 index 00000000..4c4c4d6b --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-solution-05 + orm-solution-05 + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..6d7bc172 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url") + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..8fb4b707 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name") + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..9c999a97 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email") + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..c14e85a5 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,34 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + //private List courses; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-05/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-final/.gitignore b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/.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/orm-class-work/orm-solution-final/pom.xml b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/pom.xml new file mode 100644 index 00000000..f564d3fa --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + ru.otus.example + orm-solution-final + orm-solution-final + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.h2database + h2 + 1.4.200 + + + + 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/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..cbf71df2 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,17 @@ +package ru.otus.example.ormdemo; + +import org.h2.tools.Console; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.sql.SQLException; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) throws SQLException { + SpringApplication.run(OrmDemoApplication.class, args); + Console.main(args); + } + +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..6d7bc172 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url") + private String photoUrl; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..8fb4b707 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name") + private String name; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..9c999a97 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email") + private String email; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..0ec1e721 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,39 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY /*, cascade = CascadeType.ALL*/) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/resources/application.yml b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/resources/application.yml new file mode 100644 index 00000000..80d05f29 --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/orm-solution-final/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-10-orm/orm-class-work/pom.xml b/2021-03/spring-10-orm/orm-class-work/pom.xml new file mode 100644 index 00000000..3226fdcc --- /dev/null +++ b/2021-03/spring-10-orm/orm-class-work/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + ru.otus + orm-class-work + 1.0 + + pom + + + orm-exercise + orm-solution-01 + orm-solution-02 + orm-solution-03 + orm-solution-04 + orm-solution-05 + orm-solution-final + + From 60a32cdc1bfd9c6762150f08597469373bd77027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Tue, 27 Apr 2021 20:01:00 +0400 Subject: [PATCH 03/25] examples updated --- examples/spring-mail-integration-demo/pom.xml | 2 +- .../user-activity-emitter-microservice/pom.xml | 2 +- .../user-activity-processor-microservice/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/spring-mail-integration-demo/pom.xml b/examples/spring-mail-integration-demo/pom.xml index c5163d8b..613bd25e 100644 --- a/examples/spring-mail-integration-demo/pom.xml +++ b/examples/spring-mail-integration-demo/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.2 + 2.4.5 diff --git a/examples/spring-mail-rabbitmq-demo/user-activity-emitter-microservice/pom.xml b/examples/spring-mail-rabbitmq-demo/user-activity-emitter-microservice/pom.xml index efd8ac65..0cb19192 100644 --- a/examples/spring-mail-rabbitmq-demo/user-activity-emitter-microservice/pom.xml +++ b/examples/spring-mail-rabbitmq-demo/user-activity-emitter-microservice/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.2 + 2.4.5 diff --git a/examples/spring-mail-rabbitmq-demo/user-activity-processor-microservice/pom.xml b/examples/spring-mail-rabbitmq-demo/user-activity-processor-microservice/pom.xml index 7a4665e9..423bacee 100644 --- a/examples/spring-mail-rabbitmq-demo/user-activity-processor-microservice/pom.xml +++ b/examples/spring-mail-rabbitmq-demo/user-activity-processor-microservice/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.2 + 2.4.5 From ba8eb6c5b5ba6b37e73d886d5535e912d3b99043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Wed, 28 Apr 2021 13:33:58 +0400 Subject: [PATCH 04/25] 2021-03 jpql added --- .../jpql-class-work/jpql-exercise/.gitignore | 4 + .../jpql-class-work/jpql-exercise/pom.xml | 55 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 39 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 56 ++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 126 ++++++++ .../src/test/resources/application.yml | 17 ++ .../jpql-exercise/src/test/resources/data.sql | 29 ++ .../jpql-solution-01/.gitignore | 4 + .../jpql-class-work/jpql-solution-01/pom.xml | 55 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 39 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 62 ++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 126 ++++++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../jpql-solution-02/.gitignore | 4 + .../jpql-class-work/jpql-solution-02/pom.xml | 55 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 39 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 67 +++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 126 ++++++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../jpql-solution-03/.gitignore | 4 + .../jpql-class-work/jpql-solution-03/pom.xml | 56 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 39 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 76 +++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 126 ++++++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../jpql-solution-04/.gitignore | 4 + .../jpql-class-work/jpql-solution-04/pom.xml | 55 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 42 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 75 +++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 64 ++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../jpql-solution-05/.gitignore | 4 + .../jpql-class-work/jpql-solution-05/pom.xml | 56 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 42 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 75 +++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 64 ++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../jpql-solution-06/.gitignore | 4 + .../jpql-class-work/jpql-solution-06/pom.xml | 56 ++++ .../example/ormdemo/OrmDemoApplication.java | 13 + .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 48 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 75 +++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 58 ++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../jpql-solution-final/.gitignore | 4 + .../jpql-solution-final/pom.xml | 56 ++++ .../example/ormdemo/OrmDemoApplication.java | 19 ++ .../otus/example/ormdemo/models/Avatar.java | 21 ++ .../otus/example/ormdemo/models/Course.java | 21 ++ .../ru/otus/example/ormdemo/models/EMail.java | 22 ++ .../example/ormdemo/models/OtusStudent.java | 48 +++ .../OtusStudentRepositoryJpa.java | 18 ++ .../OtusStudentRepositoryJpaImpl.java | 75 +++++ .../src/main/resources/application.yml | 15 + .../src/main/resources/schema.sql | 31 ++ .../OtusStudentRepositoryJpaImplTest.java | 58 ++++ .../src/test/resources/application.yml | 17 ++ .../src/test/resources/data.sql | 29 ++ .../spring-11-jpql/jpql-class-work/pom.xml | 23 ++ 2021-03/spring-11-jpql/jpql-demo/.gitignore | 29 ++ 2021-03/spring-11-jpql/jpql-demo/README.md | 6 + 2021-03/spring-11-jpql/jpql-demo/pom.xml | 71 +++++ .../jpql_demo/JpqlDemoApplication.java | 13 + .../example/jpql_demo/dto/CitySalary.java | 15 + .../jpql_demo/dto/EmployeeProjects.java | 16 + .../example/jpql_demo/models/Address.java | 24 ++ .../example/jpql_demo/models/Department.java | 21 ++ .../example/jpql_demo/models/Employee.java | 53 ++++ .../example/jpql_demo/models/Project.java | 21 ++ .../repositories/EmployeeRepository.java | 49 +++ .../repositories/EmployeeRepositoryImpl.java | 241 +++++++++++++++ .../src/main/resources/application.yml | 15 + .../jpql-demo/src/main/resources/jpql.sql | 74 +++++ .../jpql-demo/src/main/resources/schema.sql | 39 +++ .../EmployeeRepositoryImplTest.java | 282 ++++++++++++++++++ .../src/test/resources/application.yml | 16 + .../src/test/resources/test-data.sql | 21 ++ 131 files changed, 4652 insertions(+) create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/data.sql create mode 100644 2021-03/spring-11-jpql/jpql-class-work/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-demo/.gitignore create mode 100644 2021-03/spring-11-jpql/jpql-demo/README.md create mode 100644 2021-03/spring-11-jpql/jpql-demo/pom.xml create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/JpqlDemoApplication.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/CitySalary.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/EmployeeProjects.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Address.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Department.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Employee.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Project.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepository.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImpl.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/resources/jpql.sql create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/main/resources/schema.sql create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/test/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImplTest.java create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/test/resources/application.yml create mode 100644 2021-03/spring-11-jpql/jpql-demo/src/test/resources/test-data.sql diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/pom.xml new file mode 100644 index 00000000..a54d8414 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + jpql-exercise + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..f3822503 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,39 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..5baca1e7 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,56 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + return null; + } + + @Override + public Optional findById(long id) { + return Optional.empty(); + } + + @Override + public List findAll() { + return Collections.emptyList(); + } + + @Override + public List findByName(String name) { + return Collections.emptyList(); + } + + @Override + public void updateNameById(long id, String name) { + + } + + @Override + public void deleteById(long id) { + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/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-11-jpql/jpql-class-work/jpql-exercise/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..4955c874 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,126 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.models.Avatar; +import ru.otus.example.ormdemo.models.Course; +import ru.otus.example.ormdemo.models.EMail; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + private static final String FIRST_STUDENT_NAME = "student_01"; + + private static final int EXPECTED_QUERIES_COUNT = 31; + + private static final String STUDENT_AVATAR_URL = "где-то там"; + private static final String STUDENT_EMAIL = "any@mail.com"; + private static final String COURSE_NAME = "Spring"; + private static final String STUDENT_NAME = "Вася"; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } + + @DisplayName(" должен корректно сохранять всю информацию о студенте") + @Test + void shouldSaveAllStudentInfo() { + val avatar = new Avatar(0, STUDENT_AVATAR_URL); + val email = new EMail(0, STUDENT_EMAIL); + val emails = Collections.singletonList(email); + + val course = new Course(0, COURSE_NAME); + val courses = Collections.singletonList(course); + + + val vasya = new OtusStudent(0, STUDENT_NAME, avatar, emails, courses); + repositoryJpa.save(vasya); + assertThat(vasya.getId()).isGreaterThan(0); + + val actualStudent = em.find(OtusStudent.class, vasya.getId()); + assertThat(actualStudent).isNotNull().matches(s -> !s.getName().equals("")) + .matches(s -> s.getCourses() != null && s.getCourses().size() > 0 && s.getCourses().get(0).getId() > 0) + .matches(s -> s.getAvatar() != null) + .matches(s -> s.getEmails() != null && s.getEmails().size() > 0); + } + + @DisplayName(" должен загружать информацию о нужном студенте по его имени") + @Test + void shouldFindExpectedStudentByName() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + List students = repositoryJpa.findByName(FIRST_STUDENT_NAME); + assertThat(students).containsOnlyOnce(firstStudent); + } + + @DisplayName(" должен изменять имя заданного студента по его id") + @Test + void shouldUpdateStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + String oldName = firstStudent.getName(); + em.detach(firstStudent); + + repositoryJpa.updateNameById(FIRST_STUDENT_ID, STUDENT_NAME); + val updatedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(updatedStudent.getName()).isNotEqualTo(oldName).isEqualTo(STUDENT_NAME); + } + + @DisplayName(" должен удалять заданного студента по его id") + @Test + void shouldDeleteStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(firstStudent).isNotNull(); + em.detach(firstStudent); + + repositoryJpa.deleteById(FIRST_STUDENT_ID); + val deletedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(deletedStudent).isNull(); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-exercise/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-11-jpql/jpql-class-work/jpql-solution-01/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/pom.xml new file mode 100644 index 00000000..0781564e --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + jpql-solution-01 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..f3822503 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,39 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..cef07252 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,62 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + return Collections.emptyList(); + } + + @Override + public List findByName(String name) { + return Collections.emptyList(); + } + + @Override + public void updateNameById(long id, String name) { + + } + + @Override + public void deleteById(long id) { + + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/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-11-jpql/jpql-class-work/jpql-solution-01/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..4955c874 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,126 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.models.Avatar; +import ru.otus.example.ormdemo.models.Course; +import ru.otus.example.ormdemo.models.EMail; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + private static final String FIRST_STUDENT_NAME = "student_01"; + + private static final int EXPECTED_QUERIES_COUNT = 31; + + private static final String STUDENT_AVATAR_URL = "где-то там"; + private static final String STUDENT_EMAIL = "any@mail.com"; + private static final String COURSE_NAME = "Spring"; + private static final String STUDENT_NAME = "Вася"; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } + + @DisplayName(" должен корректно сохранять всю информацию о студенте") + @Test + void shouldSaveAllStudentInfo() { + val avatar = new Avatar(0, STUDENT_AVATAR_URL); + val email = new EMail(0, STUDENT_EMAIL); + val emails = Collections.singletonList(email); + + val course = new Course(0, COURSE_NAME); + val courses = Collections.singletonList(course); + + + val vasya = new OtusStudent(0, STUDENT_NAME, avatar, emails, courses); + repositoryJpa.save(vasya); + assertThat(vasya.getId()).isGreaterThan(0); + + val actualStudent = em.find(OtusStudent.class, vasya.getId()); + assertThat(actualStudent).isNotNull().matches(s -> !s.getName().equals("")) + .matches(s -> s.getCourses() != null && s.getCourses().size() > 0 && s.getCourses().get(0).getId() > 0) + .matches(s -> s.getAvatar() != null) + .matches(s -> s.getEmails() != null && s.getEmails().size() > 0); + } + + @DisplayName(" должен загружать информацию о нужном студенте по его имени") + @Test + void shouldFindExpectedStudentByName() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + List students = repositoryJpa.findByName(FIRST_STUDENT_NAME); + assertThat(students).containsOnlyOnce(firstStudent); + } + + @DisplayName(" должен изменять имя заданного студента по его id") + @Test + void shouldUpdateStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + String oldName = firstStudent.getName(); + em.detach(firstStudent); + + repositoryJpa.updateNameById(FIRST_STUDENT_ID, STUDENT_NAME); + val updatedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(updatedStudent.getName()).isNotEqualTo(oldName).isEqualTo(STUDENT_NAME); + } + + @DisplayName(" должен удалять заданного студента по его id") + @Test + void shouldDeleteStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(firstStudent).isNotNull(); + em.detach(firstStudent); + + repositoryJpa.deleteById(FIRST_STUDENT_ID); + val deletedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(deletedStudent).isNull(); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-01/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-11-jpql/jpql-class-work/jpql-solution-02/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/pom.xml new file mode 100644 index 00000000..526729c5 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + jpql-solution-02 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..f3822503 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,39 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..7212d42f --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,67 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + return em.createQuery("select s from OtusStudent s", OtusStudent.class) + .getResultList(); + } + + @Override + public List findByName(String name) { + TypedQuery query = em.createQuery("select s " + + "from OtusStudent s " + + "where s.name = :name", + OtusStudent.class); + query.setParameter("name", name); + return query.getResultList(); + } + + @Override + public void updateNameById(long id, String name) { + + } + + @Override + public void deleteById(long id) { + + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/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-11-jpql/jpql-class-work/jpql-solution-02/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..4955c874 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,126 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.models.Avatar; +import ru.otus.example.ormdemo.models.Course; +import ru.otus.example.ormdemo.models.EMail; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + private static final String FIRST_STUDENT_NAME = "student_01"; + + private static final int EXPECTED_QUERIES_COUNT = 31; + + private static final String STUDENT_AVATAR_URL = "где-то там"; + private static final String STUDENT_EMAIL = "any@mail.com"; + private static final String COURSE_NAME = "Spring"; + private static final String STUDENT_NAME = "Вася"; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } + + @DisplayName(" должен корректно сохранять всю информацию о студенте") + @Test + void shouldSaveAllStudentInfo() { + val avatar = new Avatar(0, STUDENT_AVATAR_URL); + val email = new EMail(0, STUDENT_EMAIL); + val emails = Collections.singletonList(email); + + val course = new Course(0, COURSE_NAME); + val courses = Collections.singletonList(course); + + + val vasya = new OtusStudent(0, STUDENT_NAME, avatar, emails, courses); + repositoryJpa.save(vasya); + assertThat(vasya.getId()).isGreaterThan(0); + + val actualStudent = em.find(OtusStudent.class, vasya.getId()); + assertThat(actualStudent).isNotNull().matches(s -> !s.getName().equals("")) + .matches(s -> s.getCourses() != null && s.getCourses().size() > 0 && s.getCourses().get(0).getId() > 0) + .matches(s -> s.getAvatar() != null) + .matches(s -> s.getEmails() != null && s.getEmails().size() > 0); + } + + @DisplayName(" должен загружать информацию о нужном студенте по его имени") + @Test + void shouldFindExpectedStudentByName() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + List students = repositoryJpa.findByName(FIRST_STUDENT_NAME); + assertThat(students).containsOnlyOnce(firstStudent); + } + + @DisplayName(" должен изменять имя заданного студента по его id") + @Test + void shouldUpdateStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + String oldName = firstStudent.getName(); + em.detach(firstStudent); + + repositoryJpa.updateNameById(FIRST_STUDENT_ID, STUDENT_NAME); + val updatedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(updatedStudent.getName()).isNotEqualTo(oldName).isEqualTo(STUDENT_NAME); + } + + @DisplayName(" должен удалять заданного студента по его id") + @Test + void shouldDeleteStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(firstStudent).isNotNull(); + em.detach(firstStudent); + + repositoryJpa.deleteById(FIRST_STUDENT_ID); + val deletedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(deletedStudent).isNull(); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-02/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-11-jpql/jpql-class-work/jpql-solution-03/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/pom.xml new file mode 100644 index 00000000..6451fc4d --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + ru.otus + jpql-solution-03 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..f3822503 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,39 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..7aef0520 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,76 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + return em.createQuery("select s from OtusStudent s", OtusStudent.class) + .getResultList(); + } + + @Override + public List findByName(String name) { + TypedQuery query = em.createQuery("select s " + + "from OtusStudent s " + + "where s.name = :name", + OtusStudent.class); + query.setParameter("name", name); + return query.getResultList(); + } + + @Override + public void updateNameById(long id, String name) { + Query query = em.createQuery("update OtusStudent s " + + "set s.name = :name " + + "where s.id = :id"); + query.setParameter("name", name); + query.setParameter("id", id); + query.executeUpdate(); + } + + @Override + public void deleteById(long id) { + Query query = em.createQuery("delete " + + "from OtusStudent s " + + "where s.id = :id"); + query.setParameter("id", id); + query.executeUpdate(); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/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-11-jpql/jpql-class-work/jpql-solution-03/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..4955c874 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,126 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.models.Avatar; +import ru.otus.example.ormdemo.models.Course; +import ru.otus.example.ormdemo.models.EMail; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + private static final String FIRST_STUDENT_NAME = "student_01"; + + private static final int EXPECTED_QUERIES_COUNT = 31; + + private static final String STUDENT_AVATAR_URL = "где-то там"; + private static final String STUDENT_EMAIL = "any@mail.com"; + private static final String COURSE_NAME = "Spring"; + private static final String STUDENT_NAME = "Вася"; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } + + @DisplayName(" должен корректно сохранять всю информацию о студенте") + @Test + void shouldSaveAllStudentInfo() { + val avatar = new Avatar(0, STUDENT_AVATAR_URL); + val email = new EMail(0, STUDENT_EMAIL); + val emails = Collections.singletonList(email); + + val course = new Course(0, COURSE_NAME); + val courses = Collections.singletonList(course); + + + val vasya = new OtusStudent(0, STUDENT_NAME, avatar, emails, courses); + repositoryJpa.save(vasya); + assertThat(vasya.getId()).isGreaterThan(0); + + val actualStudent = em.find(OtusStudent.class, vasya.getId()); + assertThat(actualStudent).isNotNull().matches(s -> !s.getName().equals("")) + .matches(s -> s.getCourses() != null && s.getCourses().size() > 0 && s.getCourses().get(0).getId() > 0) + .matches(s -> s.getAvatar() != null) + .matches(s -> s.getEmails() != null && s.getEmails().size() > 0); + } + + @DisplayName(" должен загружать информацию о нужном студенте по его имени") + @Test + void shouldFindExpectedStudentByName() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + List students = repositoryJpa.findByName(FIRST_STUDENT_NAME); + assertThat(students).containsOnlyOnce(firstStudent); + } + + @DisplayName(" должен изменять имя заданного студента по его id") + @Test + void shouldUpdateStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + String oldName = firstStudent.getName(); + em.detach(firstStudent); + + repositoryJpa.updateNameById(FIRST_STUDENT_ID, STUDENT_NAME); + val updatedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(updatedStudent.getName()).isNotEqualTo(oldName).isEqualTo(STUDENT_NAME); + } + + @DisplayName(" должен удалять заданного студента по его id") + @Test + void shouldDeleteStudentNameById() { + val firstStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(firstStudent).isNotNull(); + em.detach(firstStudent); + + repositoryJpa.deleteById(FIRST_STUDENT_ID); + val deletedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + + assertThat(deletedStudent).isNull(); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-03/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-11-jpql/jpql-class-work/jpql-solution-04/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/pom.xml new file mode 100644 index 00000000..59f37354 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + jpql-solution-04 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..da9bbb0f --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,42 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +// Позволяет указать какие связи родительской сущности загружать в одном с ней запросе +@NamedEntityGraph(name = "otus-student-avatars-entity-graph", + attributeNodes = {@NamedAttributeNode("avatar")}) +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..2dd60dc3 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,75 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.*; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + EntityGraph entityGraph = em.getEntityGraph("otus-student-avatars-entity-graph"); + TypedQuery query = em.createQuery("select s from OtusStudent s", OtusStudent.class); + query.setHint("javax.persistence.fetchgraph", entityGraph); + return query.getResultList(); + } + + @Override + public List findByName(String name) { + TypedQuery query = em.createQuery("select s " + + "from OtusStudent s " + + "where s.name = :name", + OtusStudent.class); + query.setParameter("name", name); + return query.getResultList(); + } + + @Override + public void updateNameById(long id, String name) { + Query query = em.createQuery("update OtusStudent s " + + "set s.name = :name " + + "where s.id = :id"); + query.setParameter("name", name); + query.setParameter("id", id); + query.executeUpdate(); + } + + @Override + public void deleteById(long id) { + Query query = em.createQuery("delete " + + "from OtusStudent s " + + "where s.id = :id"); + query.setParameter("id", id); + query.executeUpdate(); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/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-11-jpql/jpql-class-work/jpql-solution-04/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..145a0529 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,64 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.models.Avatar; +import ru.otus.example.ormdemo.models.Course; +import ru.otus.example.ormdemo.models.EMail; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + + private static final int EXPECTED_QUERIES_COUNT = 21; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-04/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-11-jpql/jpql-class-work/jpql-solution-05/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/pom.xml new file mode 100644 index 00000000..83cbd62c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + ru.otus + jpql-solution-05 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..da9bbb0f --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,42 @@ +package ru.otus.example.ormdemo.models; + +import lombok.*; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +// Позволяет указать какие связи родительской сущности загружать в одном с ней запросе +@NamedEntityGraph(name = "otus-student-avatars-entity-graph", + attributeNodes = {@NamedAttributeNode("avatar")}) +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..d00ad4f0 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,75 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.*; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + EntityGraph entityGraph = em.getEntityGraph("otus-student-avatars-entity-graph"); + TypedQuery query = em.createQuery("select s from OtusStudent s join fetch s.emails", OtusStudent.class); + query.setHint("javax.persistence.fetchgraph", entityGraph); + return query.getResultList(); + } + + @Override + public List findByName(String name) { + TypedQuery query = em.createQuery("select s " + + "from OtusStudent s " + + "where s.name = :name", + OtusStudent.class); + query.setParameter("name", name); + return query.getResultList(); + } + + @Override + public void updateNameById(long id, String name) { + Query query = em.createQuery("update OtusStudent s " + + "set s.name = :name " + + "where s.id = :id"); + query.setParameter("name", name); + query.setParameter("id", id); + query.executeUpdate(); + } + + @Override + public void deleteById(long id) { + Query query = em.createQuery("delete " + + "from OtusStudent s " + + "where s.id = :id"); + query.setParameter("id", id); + query.executeUpdate(); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/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-11-jpql/jpql-class-work/jpql-solution-05/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..41d802d3 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,64 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.models.Avatar; +import ru.otus.example.ormdemo.models.Course; +import ru.otus.example.ormdemo.models.EMail; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + + private static final int EXPECTED_QUERIES_COUNT = 11; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-05/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-11-jpql/jpql-class-work/jpql-solution-06/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/pom.xml new file mode 100644 index 00000000..241b4300 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + ru.otus + jpql-solution-06 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..8d4b413c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..aba63679 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,48 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +// Позволяет указать какие связи родительской сущности загружать в одном с ней запросе +@NamedEntityGraph(name = "otus-student-avatars-entity-graph", + attributeNodes = {@NamedAttributeNode("avatar")}) +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + // Все данные талицы будут загружены в память отдельным запросом и соединены с родительской сущностью + @Fetch(FetchMode.SUBSELECT) + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..d00ad4f0 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,75 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.*; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + EntityGraph entityGraph = em.getEntityGraph("otus-student-avatars-entity-graph"); + TypedQuery query = em.createQuery("select s from OtusStudent s join fetch s.emails", OtusStudent.class); + query.setHint("javax.persistence.fetchgraph", entityGraph); + return query.getResultList(); + } + + @Override + public List findByName(String name) { + TypedQuery query = em.createQuery("select s " + + "from OtusStudent s " + + "where s.name = :name", + OtusStudent.class); + query.setParameter("name", name); + return query.getResultList(); + } + + @Override + public void updateNameById(long id, String name) { + Query query = em.createQuery("update OtusStudent s " + + "set s.name = :name " + + "where s.id = :id"); + query.setParameter("name", name); + query.setParameter("id", id); + query.executeUpdate(); + } + + @Override + public void deleteById(long id) { + Query query = em.createQuery("delete " + + "from OtusStudent s " + + "where s.id = :id"); + query.setParameter("id", id); + query.executeUpdate(); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/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-11-jpql/jpql-class-work/jpql-solution-06/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..bee53f1b --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,58 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + + private static final int EXPECTED_QUERIES_COUNT = 2; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-06/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-11-jpql/jpql-class-work/jpql-solution-final/.gitignore b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/pom.xml new file mode 100644 index 00000000..0429804e --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + ru.otus + jpql-solution-final + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + 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-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java new file mode 100644 index 00000000..eecb923a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/OrmDemoApplication.java @@ -0,0 +1,19 @@ +package ru.otus.example.ormdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import ru.otus.example.ormdemo.models.OtusStudent; +import ru.otus.example.ormdemo.repositories.OtusStudentRepositoryJpa; + +import javax.persistence.EntityManager; +import java.util.Optional; + +@SpringBootApplication +public class OrmDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OrmDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java new file mode 100644 index 00000000..e54749c2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Avatar.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "avatars") +public class Avatar { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "photo_url", nullable = false, unique = true) + private String photoUrl; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java new file mode 100644 index 00000000..cfb3c968 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/Course.java @@ -0,0 +1,21 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "courses") +public class Course { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java new file mode 100644 index 00000000..7e2d6dde --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/EMail.java @@ -0,0 +1,22 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "emails") +public class EMail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java new file mode 100644 index 00000000..ecdbf955 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/models/OtusStudent.java @@ -0,0 +1,48 @@ +package ru.otus.example.ormdemo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +import javax.persistence.*; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity // Указывает, что данный класс является сущностью +@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность +@NamedEntityGraph(name = "otus-student-avatars-entity-graph", + attributeNodes = {@NamedAttributeNode("avatar")}) +public class OtusStudent { + @Id // Позволяет указать какое поле является идентификатором + @GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов + private long id; + + // Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности + @Column(name = "name", nullable = false, unique = true) + private String name; + + // Указывает на связь между таблицами "один к одному" + @OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL) + // Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности + @JoinColumn(name = "avatar_id") + private Avatar avatar; + + // Указывает на связь между таблицами "один ко многим" + @OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "student_id") + private List emails; + + @Fetch(FetchMode.SELECT) + @BatchSize(size = 5) + // Указывает на связь между таблицами "многие ко многим" + @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // Задает таблицу связей между таблицами для хранения родительской и связанной сущностью + @JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"), + inverseJoinColumns = @JoinColumn(name = "course_id")) + private List courses; +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java new file mode 100644 index 00000000..dc773e5a --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpa.java @@ -0,0 +1,18 @@ +package ru.otus.example.ormdemo.repositories; + + +import ru.otus.example.ormdemo.models.OtusStudent; + +import java.util.List; +import java.util.Optional; + +public interface OtusStudentRepositoryJpa { + OtusStudent save(OtusStudent student); + Optional findById(long id); + + List findAll(); + List findByName(String name); + + void updateNameById(long id, String name); + void deleteById(long id); +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java new file mode 100644 index 00000000..d00ad4f0 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImpl.java @@ -0,0 +1,75 @@ +package ru.otus.example.ormdemo.repositories; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.example.ormdemo.models.OtusStudent; + +import javax.persistence.*; +import java.util.List; +import java.util.Optional; + +// @Transactional должна стоять на методе сервиса. +// Причем, если метод не подразумевает изменения данных в БД то категорически желательно +// выставить у аннотации параметр readOnly в true. +// Но это только упражнение и транзакции мы пока не проходили. +// Поэтому, для упрощения, пока вешаем над классом репозитория +@Transactional +@Repository +public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa { + + @PersistenceContext + private EntityManager em; + + @Override + public OtusStudent save(OtusStudent student) { + if (student.getId() <= 0) { + em.persist(student); + return student; + } else { + return em.merge(student); + } + } + + @Override + public Optional findById(long id) { + return Optional.ofNullable(em.find(OtusStudent.class, id)); + } + + @Override + public List findAll() { + EntityGraph entityGraph = em.getEntityGraph("otus-student-avatars-entity-graph"); + TypedQuery query = em.createQuery("select s from OtusStudent s join fetch s.emails", OtusStudent.class); + query.setHint("javax.persistence.fetchgraph", entityGraph); + return query.getResultList(); + } + + @Override + public List findByName(String name) { + TypedQuery query = em.createQuery("select s " + + "from OtusStudent s " + + "where s.name = :name", + OtusStudent.class); + query.setParameter("name", name); + return query.getResultList(); + } + + @Override + public void updateNameById(long id, String name) { + Query query = em.createQuery("update OtusStudent s " + + "set s.name = :name " + + "where s.id = :id"); + query.setParameter("name", name); + query.setParameter("id", id); + query.executeUpdate(); + } + + @Override + public void deleteById(long id) { + Query query = em.createQuery("delete " + + "from OtusStudent s " + + "where s.id = :id"); + query.setParameter("id", id); + query.executeUpdate(); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/application.yml new file mode 100644 index 00000000..8d633961 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/main/resources/schema.sql new file mode 100644 index 00000000..43a684bb --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/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-11-jpql/jpql-class-work/jpql-solution-final/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java new file mode 100644 index 00000000..6ff06cbc --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/java/ru/otus/example/ormdemo/repositories/OtusStudentRepositoryJpaImplTest.java @@ -0,0 +1,58 @@ +package ru.otus.example.ormdemo.repositories; + +import lombok.val; +import org.hibernate.SessionFactory; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.ormdemo.models.OtusStudent; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Репозиторий на основе Jpa для работы со студентами ") +@DataJpaTest +@Import(OtusStudentRepositoryJpaImpl.class) +class OtusStudentRepositoryJpaImplTest { + + private static final int EXPECTED_NUMBER_OF_STUDENTS = 10; + private static final long FIRST_STUDENT_ID = 1L; + + private static final int EXPECTED_QUERIES_COUNT = 3; + + @Autowired + private OtusStudentRepositoryJpaImpl repositoryJpa; + + @Autowired + private TestEntityManager em; + + @DisplayName(" должен загружать информацию о нужном студенте по его id") + @Test + void shouldFindExpectedStudentById() { + val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID); + val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID); + assertThat(optionalActualStudent).isPresent().get() + .usingRecursiveComparison().isEqualTo(expectedStudent); + } + + @DisplayName("должен загружать список всех студентов с полной информацией о них") + @Test + void shouldReturnCorrectStudentsListWithAllInfo() { + SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory() + .unwrap(SessionFactory.class); + sessionFactory.getStatistics().setStatisticsEnabled(true); + + + System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------"); + val students = repositoryJpa.findAll(); + 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); + System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n"); + assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT); + } +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/application.yml new file mode 100644 index 00000000..d2b811a4 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + #generate-ddl: true + hibernate: + ddl-auto: none + #ddl-auto: create-drop + + #show-sql: true + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/data.sql b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/src/test/resources/data.sql new file mode 100644 index 00000000..a8db6b85 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/jpql-solution-final/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-11-jpql/jpql-class-work/pom.xml b/2021-03/spring-11-jpql/jpql-class-work/pom.xml new file mode 100644 index 00000000..6f781fad --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-class-work/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + ru.otus + jpql-class-work + 1.0 + + pom + + + jpql-exercise + jpql-solution-01 + jpql-solution-02 + jpql-solution-03 + jpql-solution-04 + jpql-solution-05 + jpql-solution-06 + jpql-solution-final + + diff --git a/2021-03/spring-11-jpql/jpql-demo/.gitignore b/2021-03/spring-11-jpql/jpql-demo/.gitignore new file mode 100644 index 00000000..153c9335 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-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-11-jpql/jpql-demo/README.md b/2021-03/spring-11-jpql/jpql-demo/README.md new file mode 100644 index 00000000..67632929 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/README.md @@ -0,0 +1,6 @@ +## Пример работы с JPQL + +В примере демонстрируется: +* *репозитории на Spring ORM с использованием JPA и JPQL* +* *использование JPQL для написания разного рода запросов (в т.ч. для выборки, агрегации, изменения и удаления данных)* +* *тестирование репозиториев на Spring ORM с использованием @DataJpaTest* diff --git a/2021-03/spring-11-jpql/jpql-demo/pom.xml b/2021-03/spring-11-jpql/jpql-demo/pom.xml new file mode 100644 index 00000000..376d49a7 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + ru.otus.example + jpql-demo + 0.0.1-SNAPSHOT + jpql-demo + Demo project for Spring Boot + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.shell + spring-shell-starter + 2.0.1.RELEASE + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + 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-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/JpqlDemoApplication.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/JpqlDemoApplication.java new file mode 100644 index 00000000..5588b288 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/JpqlDemoApplication.java @@ -0,0 +1,13 @@ +package ru.otus.example.jpql_demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpqlDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(JpqlDemoApplication.class, args); + } + +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/CitySalary.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/CitySalary.java new file mode 100644 index 00000000..29078c2f --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/CitySalary.java @@ -0,0 +1,15 @@ +package ru.otus.example.jpql_demo.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CitySalary { + private String city; + private Double salary; + +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/EmployeeProjects.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/EmployeeProjects.java new file mode 100644 index 00000000..7f73ba5c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/dto/EmployeeProjects.java @@ -0,0 +1,16 @@ +package ru.otus.example.jpql_demo.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.otus.example.jpql_demo.models.Employee; + + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EmployeeProjects { + private Employee employee; + private long projectsCount; + +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Address.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Address.java new file mode 100644 index 00000000..96a03d58 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Address.java @@ -0,0 +1,24 @@ +package ru.otus.example.jpql_demo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; + +import javax.persistence.*; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "addresses") +public class Address { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "city") + private String city; +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Department.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Department.java new file mode 100644 index 00000000..79110620 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Department.java @@ -0,0 +1,21 @@ +package ru.otus.example.jpql_demo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "departments") +public class Department { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Employee.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Employee.java new file mode 100644 index 00000000..0bcd8123 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Employee.java @@ -0,0 +1,53 @@ +package ru.otus.example.jpql_demo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "employees") +public class Employee { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "first_name") + private String firstName; + + @Column(name = "last_name") + private String lastName; + + @Column(name = "salary") + private BigDecimal salary; + + @ManyToOne + @JoinColumn(name = "department_id", referencedColumnName = "id") + private Department department; + + @ManyToOne + @JoinColumn(name = "address_id", referencedColumnName = "id") + private Address address; + + @BatchSize(size = 100) + @ManyToMany + @JoinTable(name = "employees_projects", + joinColumns = @JoinColumn(name = "employee_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "project_id", referencedColumnName = "id")) + private List projects; + + + public Employee(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Project.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Project.java new file mode 100644 index 00000000..f22e4242 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/models/Project.java @@ -0,0 +1,21 @@ +package ru.otus.example.jpql_demo.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "projects") +public class Project { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "name") + private String name; +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepository.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepository.java new file mode 100644 index 00000000..3c00881c --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepository.java @@ -0,0 +1,49 @@ +package ru.otus.example.jpql_demo.repositories; + +import ru.otus.example.jpql_demo.dto.CitySalary; +import ru.otus.example.jpql_demo.dto.EmployeeProjects; +import ru.otus.example.jpql_demo.models.Employee; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +public interface EmployeeRepository { + + List findAll(); + Optional findEmployeeById(long id); + List findAllEmployeesWithSalaryOver100000(); + List findEmployeesFirstNames(); + List findEmployeesFirstAndLastNames(); + long calcEmployeesCount(); + BigDecimal findMaxEmployeeSalary(); + Double calcAvgEmployeeSalary(); + + + List calcAvgSalaryByCities(); + List calcAvgSalaryByCitiesSorted(); + List calcAvgSalaryByCitiesHavingValueOver100000(); + + + List findEmployeesWithGivenProjects(String p1Name, String p2Name); + List findEmployeesProjectsCount(); + + + List findEmployeesWithGivenFirstNames(String name1, String name2); + List findEmployeesWithFirstNamesFromGivenList(List names); + List findEmployeeNameSakes(Employee employee); + + + Employee findEmployeeNameSake(Employee employee); + List findEmployeesWithSalaryLessThanGivenEmployee(Employee employee); + List findEmployeeWithNameMatchingAnyOtherEmployeesNames(); + List findEmployeesWithSalaryLessThanAllEmployees(); + + + int updateEmployeesSalary(BigDecimal oldSalary, BigDecimal newSalary); + int doubleEmployeesSalary(BigDecimal oldSalary); + int deleteEmployeesWithoutDepartment(); + + + +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImpl.java b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImpl.java new file mode 100644 index 00000000..99952ce2 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImpl.java @@ -0,0 +1,241 @@ +package ru.otus.example.jpql_demo.repositories; + +import org.springframework.stereotype.Repository; +import ru.otus.example.jpql_demo.dto.CitySalary; +import ru.otus.example.jpql_demo.dto.EmployeeProjects; +import ru.otus.example.jpql_demo.models.Employee; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +@Repository +public class EmployeeRepositoryImpl implements EmployeeRepository { + + @PersistenceContext + private final EntityManager em; + + public EmployeeRepositoryImpl(EntityManager em) { + this.em = em; + } + + @Override + public List findAll() { + return em.createQuery("select e from Employee e", Employee.class).getResultList(); + } + + @Override + public Optional findEmployeeById(long id) { + TypedQuery query = em.createQuery( + "select e from Employee e where e.id = :id" + , Employee.class); + query.setParameter("id", id); + try { + return Optional.of(query.getSingleResult()); + } catch (NoResultException e) { + return Optional.empty(); + } + } + + @Override + public List findAllEmployeesWithSalaryOver100000() { + return em.createQuery( + "select e from Employee e where e.salary > 100000" + , Employee.class).getResultList(); + } + + @Override + public List findEmployeesFirstNames() { + return em.createQuery( + "select e.firstName from Employee e" + , String.class).getResultList(); + } + + @SuppressWarnings("unchecked") + @Override + public List findEmployeesFirstAndLastNames() { + return em.createQuery( + "select e.firstName, e.lastName from Employee e" + ).getResultList(); + } + + @Override + public long calcEmployeesCount() { + return em.createQuery( + "select count(e) from Employee e" + , Long.class).getSingleResult(); + } + + @Override + public BigDecimal findMaxEmployeeSalary() { + return em.createQuery( + "select max(e.salary) from Employee e" + , BigDecimal.class).getSingleResult(); + } + + @Override + public Double calcAvgEmployeeSalary() { + return em.createQuery( + "select avg(e.salary) from Employee e" + , Double.class).getSingleResult(); + } + + //------------------------------------------------------------------------------------------------------- + + @Override + public List calcAvgSalaryByCities() { + return em.createQuery( + "select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) " + + "from Employee e " + + "group by e.address.city" + , CitySalary.class).getResultList(); + } + + @Override + public List calcAvgSalaryByCitiesSorted() { + return em.createQuery( + "select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) " + + "from Employee e " + + "group by e.address.city " + + "order by avg(e.salary)" + , CitySalary.class).getResultList(); + } + + @Override + public List calcAvgSalaryByCitiesHavingValueOver100000() { + return em.createQuery( + "select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) " + + "from Employee e " + + "group by e.address.city " + + "having avg(e.salary) > 100000" + + "order by avg(e.salary) " + , CitySalary.class).getResultList(); + } + + //------------------------------------------------------------------------------------------------------- + + @Override + public List findEmployeesWithGivenProjects(String p1Name, String p2Name) { + TypedQuery query = em.createQuery( + "select e " + + "from Employee e join e.projects p1 join e.projects p2 " + + "where p1.name = :p1 and p2.name = :p2" + , Employee.class); + query.setParameter("p1", p1Name); + query.setParameter("p2", p2Name); + return query.getResultList(); + } + + @Override + public List findEmployeesProjectsCount() { + return em.createQuery( + "select new ru.otus.example.jpql_demo.dto.EmployeeProjects(e, count(p)) " + + "from Employee e left join e.projects p " + + "group by e " + + "order by count(p) desc " + , EmployeeProjects.class).getResultList(); + } + + //------------------------------------------------------------------------------------------------------- + + @Override + public List findEmployeesWithGivenFirstNames(String name1, String name2) { + TypedQuery query = em.createQuery( + "select e " + + "from Employee e " + + "where e.firstName in (:name1, :name2) " + , Employee.class); + query.setParameter("name1", name1); + query.setParameter("name2", name2); + return query.getResultList(); + } + + @Override + public List findEmployeesWithFirstNamesFromGivenList(List names) { + TypedQuery query = em.createQuery( + "select e " + + "from Employee e " + + "where e.firstName in :names " + , Employee.class); + query.setParameter("names", names); + return query.getResultList(); + } + + @Override + public List findEmployeeNameSakes(Employee employee) { + TypedQuery query = em.createQuery( + "select e " + + "from Employee e " + + "where e.firstName in (select e2.firstName from Employee e2 where e2.lastName = :lastName and e2.id <> :id) " + , Employee.class); + query.setParameter("lastName", employee.getLastName()); + query.setParameter("id", employee.getId()); + return query.getResultList(); + } + + //------------------------------------------------------------------------------------------------------- + + @Override + public Employee findEmployeeNameSake(Employee employee) { + TypedQuery query = em.createQuery( + "select e " + + "from Employee e " + + "where e.firstName = (select e2.firstName from Employee e2 where e2.id = :id) and e.id <> :id " + , Employee.class); + query.setParameter("id", employee.getId()); + return query.getSingleResult(); + } + + @Override + public List findEmployeesWithSalaryLessThanGivenEmployee(Employee employee) { + TypedQuery query = em.createQuery( + "select e " + + "from Employee e " + + "where e.salary < (select e2.salary from Employee e2 where e2.id = :id) " + , Employee.class); + query.setParameter("id", employee.getId()); + return query.getResultList(); + } + + @Override + public List findEmployeeWithNameMatchingAnyOtherEmployeesNames() { + return em.createQuery( + "select e " + + "from Employee e " + + "where e.firstName = any(select e2.firstName from Employee e2 where e2.id <> e.id) " + , Employee.class).getResultList(); + } + + @Override + public List findEmployeesWithSalaryLessThanAllEmployees() { + return em.createQuery( + "select e " + + "from Employee e " + + "where e.salary <= all(select e2.salary from Employee e2) " + , Employee.class).getResultList(); + } + + //------------------------------------------------------------------------------------------------------- + + @Override + public int updateEmployeesSalary(BigDecimal oldSalary, BigDecimal newSalary) { + Query query = em.createQuery("update Employee e set e.salary = :newSalary where e.salary = :oldSalary"); + query.setParameter("newSalary", newSalary); + query.setParameter("oldSalary", oldSalary); + return query.executeUpdate(); + } + + @Override + public int doubleEmployeesSalary(BigDecimal oldSalary) { + Query query = em.createQuery("update Employee e set e.salary = e.salary * 2 where e.salary = :oldSalary"); + query.setParameter("oldSalary", oldSalary); + return query.executeUpdate(); + } + + @Override + public int deleteEmployeesWithoutDepartment() { + return em.createQuery("delete from Employee e where e.department is null") + .executeUpdate(); + } +} diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/resources/application.yml b/2021-03/spring-11-jpql/jpql-demo/src/main/resources/application.yml new file mode 100644 index 00000000..d9f3dc49 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + properties: + hibernate: + #format_sql: true + diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/resources/jpql.sql b/2021-03/spring-11-jpql/jpql-demo/src/main/resources/jpql.sql new file mode 100644 index 00000000..8fcc220f --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/resources/jpql.sql @@ -0,0 +1,74 @@ +select e from Employee e where e.id = :id +---------------------------------------------------------------------------------- +select e from Employee e where e.salary > 100000 +---------------------------------------------------------------------------------- +select e.firstName from Employee e +---------------------------------------------------------------------------------- +select e.firstName, e.lastName from Employee e +---------------------------------------------------------------------------------- +select count(e) from Employee e +---------------------------------------------------------------------------------- +select max(e.salary) from Employee e +---------------------------------------------------------------------------------- +select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) +from Employee e +group by e.address.city +---------------------------------------------------------------------------------- +select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) +from Employee e +group by e.address.city +order by avg(e.salary) +---------------------------------------------------------------------------------- +select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) +from Employee e +group by e.address.city +having avg(e.salary) > 100000 +order by avg(e.salary) +---------------------------------------------------------------------------------- +select e +from Employee e join e.projects p1 join e.projects p2 +where p1.name = :p1 and p2.name = :p2 +---------------------------------------------------------------------------------- +select new ru.otus.example.jpql_demo.dto.EmployeeProjects(e, count(p)) +from Employee e left join e.projects p +group by e +order by count(p) desc +---------------------------------------------------------------------------------- +select e +from Employee e +where e.firstName in (:name1, :name2) +---------------------------------------------------------------------------------- +select e +from Employee e +where e.firstName in :names +---------------------------------------------------------------------------------- +select e +from Employee e +where e.firstName in (select e2.firstName + from Employee e2 + where e2.lastName = :lastName and + e2.id <> :id) +---------------------------------------------------------------------------------- +select e +from Employee e +where e.firstName = (select e2.firstName + from Employee e2 + where e2.id = :id) and e.id <> :id +---------------------------------------------------------------------------------- +select e +from Employee e +where e.salary < (select e2.salary + from Employee e2 + where e2.id = :id) +---------------------------------------------------------------------------------- +select e +from Employee e +where e.firstName = any(select e2.firstName + from Employee e2 + where e2.id <> e.id) +---------------------------------------------------------------------------------- +select e +from Employee e +where e.salary <= all(select e2.salary + from Employee e2) +---------------------------------------------------------------------------------- \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-demo/src/main/resources/schema.sql b/2021-03/spring-11-jpql/jpql-demo/src/main/resources/schema.sql new file mode 100644 index 00000000..f1425901 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/main/resources/schema.sql @@ -0,0 +1,39 @@ +DROP TABLE IF EXISTS employees_projects; +DROP TABLE IF EXISTS addresses; +DROP TABLE IF EXISTS departments; +DROP TABLE IF EXISTS projects; +DROP TABLE IF EXISTS employees; + +CREATE TABLE addresses ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + city VARCHAR(255) +); + +CREATE TABLE departments ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) +); + +CREATE TABLE projects ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) +); + + +CREATE TABLE employees ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + first_name VARCHAR(255), + last_name VARCHAR(255), + salary BIGINT, + address_id BIGINT, + department_id BIGINT, + FOREIGN KEY(address_id) REFERENCES addresses(id) ON DELETE CASCADE, + FOREIGN KEY(department_id) REFERENCES departments(id) ON DELETE CASCADE +); + +CREATE TABLE employees_projects ( + employee_id BIGINT, + project_id BIGINT, + FOREIGN KEY(employee_id) REFERENCES employees(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-demo/src/test/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImplTest.java b/2021-03/spring-11-jpql/jpql-demo/src/test/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImplTest.java new file mode 100644 index 00000000..92fcc9e8 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/test/java/ru/otus/example/jpql_demo/repositories/EmployeeRepositoryImplTest.java @@ -0,0 +1,282 @@ +package ru.otus.example.jpql_demo.repositories; + +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import ru.otus.example.jpql_demo.dto.CitySalary; +import ru.otus.example.jpql_demo.dto.EmployeeProjects; +import ru.otus.example.jpql_demo.models.Employee; + +import javax.persistence.NonUniqueResultException; +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.*; + +@DisplayName("Репозиторий Employee должен") +@DataJpaTest +@Import(EmployeeRepositoryImpl.class) +class EmployeeRepositoryImplTest { + + private static final long FIRST_EMPLOYEE_ID = 1L; + private static final long SECOND_EMPLOYEE_ID = 2L; + private static final long THIRD_EMPLOYEE_ID = 3L; + private static final long FOURTH_EMPLOYEE_ID = 4L; + private static final long SEVENTH_EMPLOYEE_ID = 7L; + private static final long EIGTH_EMPLOYEE_ID = 8L; + + private static final int EMPLOYEES_COUNT = 8; + private static final String FIRST_EMPLOYEE_FIRST_NAME = "fn1"; + + private static final String PROJECT_3 = "Project #3"; + private static final String PROJECT_4 = "Project #4"; + + private static final CitySalary SARATOV_SALARY = new CitySalary("Saratov", 66666.0); + private static final CitySalary OMSK_SALARY = new CitySalary("Omsk", 170000.0); + private static final CitySalary MOSCOW_SALARY = new CitySalary("Moscow", 330100.0); + + private static final int MAX_SALARY = 1000000; + private static final double AVG_SALARY = 211299.75d; + private static final int EMPLOYEES_WITH_SALARY_OVER_100000_COUNT = 4; + private static final int FOURTH_EMPLOYEE_PROJECTS_COUNT = 4; + private static final String NAME_SAKE_NAME_1 = "NameSake1"; + private static final String NAME_SAKE_NAME_2 = "NameSake2"; + + @Autowired + private TestEntityManager em; + + @Autowired + private EmployeeRepositoryImpl employeeRepository; + + + @DisplayName("возвращать список всех сотрудников") + @Test + void shouldFindAllEmployees() { + List employees = employeeRepository.findAll(); + assertThat(employees).hasSize(EMPLOYEES_COUNT); + } + + @DisplayName("возвращать сотрудника по его id") + @Test + void shouldFindEmployeeById() { + Optional employee = employeeRepository.findEmployeeById(FIRST_EMPLOYEE_ID); + assertThat(employee).isNotEmpty().get() + .hasFieldOrPropertyWithValue("firstName", FIRST_EMPLOYEE_FIRST_NAME); + } + + @DisplayName("возвращать список всех сотрудников c окладом более 100000") + @Test + void shouldFindAllEmployeesWithSalaryOver100000() { + List allEmployeesWithSalaryOver100000 = employeeRepository.findAllEmployeesWithSalaryOver100000(); + assertThat(allEmployeesWithSalaryOver100000).size().isEqualTo(EMPLOYEES_WITH_SALARY_OVER_100000_COUNT); + } + + @DisplayName("возвращать список имен всех сотрудников") + @Test + void shouldFindEmployeesFirstNames() { + List employeesFirstNames = employeeRepository.findEmployeesFirstNames(); + assertThat(employeesFirstNames) + .containsExactlyInAnyOrder("fn1", "fn2", "fn3", "fn4", "fn5", "fn6", "fn7", "fn8"); + } + + @DisplayName("возвращать список имен и фамилий всех сотрудников") + @Test + void shouldFindEmployeesFirstAndLastNames() { + List employeesFirstAndLastNames = employeeRepository.findEmployeesFirstAndLastNames(); + String[][] expectedFirstAndLastNames = new String[EMPLOYEES_COUNT][2]; + IntStream.range(1, EMPLOYEES_COUNT + 1) + .forEachOrdered(i -> expectedFirstAndLastNames[i - 1] = new String[]{"fn" + i, "ln" + i}); + assertThat(employeesFirstAndLastNames).containsExactlyInAnyOrder(expectedFirstAndLastNames); + } + + @DisplayName("считать общее количество сотрудников") + @Test + void shouldCalcEmployeesCount() { + long employeesCount = employeeRepository.calcEmployeesCount(); + assertThat(employeesCount).isEqualTo(EMPLOYEES_COUNT); + } + + @DisplayName("находить максимальный оклад сотрудников") + @Test + void shouldFindMaxEmployeeSalary() { + BigDecimal maxSalary = employeeRepository.findMaxEmployeeSalary(); + assertThat(maxSalary).isEqualTo(new BigDecimal(MAX_SALARY)); + } + + @DisplayName("считать средний оклад всех сотрудников") + @Test + void shouldCalcAvgEmployeeSalary() { + Double avgSalary = employeeRepository.calcAvgEmployeeSalary(); + assertThat(avgSalary).isEqualTo(AVG_SALARY, offset(0.01d)); + } + + //------------------------------------------------------------------------------------------------------- + + @DisplayName("возвращать список окладов по городам") + @Test + void shouldCalcAvgEmployeeSalaryByCities() { + List avgSalaryByCities = employeeRepository.calcAvgSalaryByCities(); + assertThat(avgSalaryByCities) + .containsExactlyInAnyOrder(SARATOV_SALARY, MOSCOW_SALARY, OMSK_SALARY); + } + + @DisplayName("возвращать сортированный список окладов по городам") + @Test + void shouldCalcAvgEmployeeSalaryByCitiesSorted() { + List avgSalaryByCities = employeeRepository.calcAvgSalaryByCitiesSorted(); + assertThat(avgSalaryByCities) + .containsExactly(SARATOV_SALARY, OMSK_SALARY, MOSCOW_SALARY); + } + + @DisplayName("возвращать список окладов по городам, где средний доход сотрудников более 100000") + @Test + void shouldCalcAvgEmployeeSalaryByCitiesHavingValueOver100000() { + List avgSalaryByCities = employeeRepository.calcAvgSalaryByCitiesHavingValueOver100000(); + assertThat(avgSalaryByCities).containsExactly(OMSK_SALARY, MOSCOW_SALARY); + } + + //------------------------------------------------------------------------------------------------------- + + @DisplayName("возвращать список всех сотрудников работающих над заданными проектами") + @Test + void shouldFindEmployeesWithGivenProjects() { + Employee employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID); + Employee employee4 = em.find(Employee.class, FOURTH_EMPLOYEE_ID); + List employees = employeeRepository.findEmployeesWithGivenProjects(PROJECT_3, PROJECT_4); + assertThat(employees).hasSize(2).containsExactlyInAnyOrder(employee2, employee4); + } + + @DisplayName("возвращать количество проектов по сотрудникам") + @Test + void shouldFindEmployeesProjectsCount() { + Employee employee4 = em.find(Employee.class, FOURTH_EMPLOYEE_ID); + List employeeProjects = employeeRepository.findEmployeesProjectsCount(); + assertThat(employeeProjects).hasSize(EMPLOYEES_COUNT) + .contains(new EmployeeProjects(employee4, FOURTH_EMPLOYEE_PROJECTS_COUNT)); + } + + //------------------------------------------------------------------------------------------------------- + + @DisplayName("возвращать список всех сотрудников имеющих одно из двух заданных имен") + @Test + void shouldFindEmployeesWithGivenFirstNames() { + Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID); + Employee employee7 = em.find(Employee.class, SEVENTH_EMPLOYEE_ID); + List employees = employeeRepository.findEmployeesWithGivenFirstNames("fn1", "fn7"); + assertThat(employees).hasSize(2).containsExactlyInAnyOrder(employee1, employee7); + } + + @DisplayName("возвращать список всех сотрудников имеющих имя, совпадающее с одним из заданного списка") + @Test + void shouldFindEmployeesWithFirstNamesFromGivenList() { + Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID); + Employee employee7 = em.find(Employee.class, SEVENTH_EMPLOYEE_ID); + List employees = employeeRepository.findEmployeesWithFirstNamesFromGivenList(List.of("fn1", "fn7")); + assertThat(employees).hasSize(2).containsExactlyInAnyOrder(employee1, employee7); + } + + @DisplayName("возвращать список всех однофамильцев заданного сотрудника") + @Test + void shouldFindEmployeesNameSakes() { + Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID); + Employee employee9 = em.persistAndFlush(new Employee(NAME_SAKE_NAME_1, employee1.getLastName())); + Employee employee10 = em.persistAndFlush(new Employee(NAME_SAKE_NAME_2, employee1.getLastName())); + List nameSakes = employeeRepository.findEmployeeNameSakes(employee1); + assertThat(nameSakes).hasSize(2).containsExactlyInAnyOrder(employee9, employee10); + } + + //------------------------------------------------------------------------------------------------------- + + @DisplayName("возвращать список всех тезок заданного сотрудника") + @Test + void shouldFindEmployeeNameSake() { + Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID); + Employee employee9 = em.persistAndFlush(new Employee(employee1.getFirstName(), NAME_SAKE_NAME_1)); + Employee nameSake = employeeRepository.findEmployeeNameSake(employee1); + assertThat(nameSake).usingRecursiveComparison().isEqualTo(employee9); + + em.persistAndFlush(new Employee(employee1.getFirstName(), NAME_SAKE_NAME_2)); + assertThatCode(() -> employeeRepository.findEmployeeNameSake(employee1)) + .isInstanceOf(NonUniqueResultException.class); + } + + @DisplayName("возвращать список всех сотрудников имеющих оклад меньше, чем у заданного сотрудника") + @Test + void shouldFindEmployeesWithSalaryLessThanGivenEmployee() { + Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID); + Employee employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID); + Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID); + Employee employee7 = em.find(Employee.class, SEVENTH_EMPLOYEE_ID); + List employees = employeeRepository.findEmployeesWithSalaryLessThanGivenEmployee(employee7); + assertThat(employees).hasSize(3).containsExactlyInAnyOrder(employee1, employee2, employee3); + } + + @DisplayName("возвращать сотрудника, являющегося тезкой любому другому сотруднику") + @Test + void shouldFindEmployeeWithNameMatchingAnyOtherEmployeesNames() { + Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID); + Employee employee9 = em.persistAndFlush(new Employee(employee1.getFirstName(), NAME_SAKE_NAME_1)); + List nameSakes = employeeRepository.findEmployeeWithNameMatchingAnyOtherEmployeesNames(); + assertThat(nameSakes).hasSize(2).containsExactlyInAnyOrder(employee9, employee1); + } + + @DisplayName("возвращать сотрудника имеющго оклад меньше, чем у всех") + @Test + void shouldFindEmployeesWithSalaryLessThanAllEmployees() { + Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID); + List employees = employeeRepository.findEmployeesWithSalaryLessThanAllEmployees(); + assertThat(employees).hasSize(1).containsOnly(employee3); + + } + + //------------------------------------------------------------------------------------------------------- + + @DisplayName("изменять значение оклада сотрудника имеющего заданный оклад") + @Test + void shouldUpdateEmployeesSalary() { + Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID); + BigDecimal oldSalary = employee3.getSalary(); + BigDecimal newSalary = oldSalary.multiply(new BigDecimal(2)); + em.detach(employee3); + employeeRepository.updateEmployeesSalary(oldSalary, newSalary); + + employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID); + assertThat(employee3.getSalary()).isEqualTo(newSalary); + } + + @DisplayName("изменять значение оклада в два раза, у сотрудника имеющего заданный оклад") + @Test + void shouldDoubleEmployeesSalary() { + Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID); + BigDecimal oldSalary = employee3.getSalary(); + BigDecimal newSalary = oldSalary.multiply(new BigDecimal(2)); + em.detach(employee3); + employeeRepository.doubleEmployeesSalary(oldSalary); + + employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID); + assertThat(employee3.getSalary()).isEqualTo(newSalary); + } + + @DisplayName("удалять сотрудников не относящихся ни к одному отделу") + @Test + void shouldDeleteEmployeesWithoutDepartment() { + Employee employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID); + Employee employee8 = em.find(Employee.class, EIGTH_EMPLOYEE_ID); + assertThat(employee2).isNotNull(); + assertThat(employee8).isNotNull(); + employeeRepository.deleteEmployeesWithoutDepartment(); + + em.clear(); + + employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID); + employee8 = em.find(Employee.class, EIGTH_EMPLOYEE_ID); + assertThat(employee2).isNull(); + assertThat(employee8).isNull(); + } + +} \ No newline at end of file diff --git a/2021-03/spring-11-jpql/jpql-demo/src/test/resources/application.yml b/2021-03/spring-11-jpql/jpql-demo/src/test/resources/application.yml new file mode 100644 index 00000000..30cb8ab6 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/test/resources/application.yml @@ -0,0 +1,16 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: always + data: test-data.sql + + jpa: + generate-ddl: false + hibernate: + ddl-auto: none + + show-sql: true + properties: + hibernate: + #format_sql: true + diff --git a/2021-03/spring-11-jpql/jpql-demo/src/test/resources/test-data.sql b/2021-03/spring-11-jpql/jpql-demo/src/test/resources/test-data.sql new file mode 100644 index 00000000..6ac15df5 --- /dev/null +++ b/2021-03/spring-11-jpql/jpql-demo/src/test/resources/test-data.sql @@ -0,0 +1,21 @@ +INSERT INTO addresses (city) VALUES ('Saratov'), ('Omsk'), ('Moscow'); +INSERT INTO departments (name) VALUES ('IT'), ('AHO'); +INSERT INTO projects (name) VALUES ('Project #1'), ('Project #2'), ('Project #3'), ('Project #4'); + +INSERT INTO employees (first_name, last_name, salary, address_id, department_id) +VALUES ('fn1', 'ln1', 70000, 1, 1), + ('fn2', 'ln2', 99998, 1, null), + ('fn3', 'ln3', 30000, 1, 2), + + ('fn4', 'ln4', 170000, 2, 1), + + ('fn5', 'ln5', 120000, 3, 1), + ('fn6', 'ln6', 100400, 3, 1), + ('fn7', 'ln7', 100000, 3, 1), + ('fn8', 'ln8', 1000000, 3, null); + + +INSERT INTO employees_projects (employee_id, project_id) +VALUES (1, 1), (1, 2), (1, 3), + (2, 3), (2, 4), + (4, 1), (4, 2), (4, 3), (4, 4); \ No newline at end of file From 68d1dab090cd36a328077078a15c894bb61effaa Mon Sep 17 00:00:00 2001 From: saroff Date: Wed, 28 Apr 2021 19:37:37 +0300 Subject: [PATCH 05/25] 21-02 Spring 18 Reactive programming --- 2021-02/spring-18/.gitignore | 7 ++ 2021-02/spring-18/pom.xml | 33 ++++++++ .../src/main/java/ru/otus/CreateExamples.java | 35 ++++++++ .../main/java/ru/otus/LiveLikeExample.java | 26 ++++++ .../main/java/ru/otus/OperatorsExample.java | 79 +++++++++++++++++++ .../src/main/java/ru/otus/Person.java | 66 ++++++++++++++++ .../ru/otus/comparison/AsyncComparison.java | 40 ++++++++++ .../ru/otus/comparison/SyncComparison.java | 31 ++++++++ 8 files changed, 317 insertions(+) create mode 100644 2021-02/spring-18/.gitignore create mode 100644 2021-02/spring-18/pom.xml create mode 100644 2021-02/spring-18/src/main/java/ru/otus/CreateExamples.java create mode 100644 2021-02/spring-18/src/main/java/ru/otus/LiveLikeExample.java create mode 100644 2021-02/spring-18/src/main/java/ru/otus/OperatorsExample.java create mode 100644 2021-02/spring-18/src/main/java/ru/otus/Person.java create mode 100644 2021-02/spring-18/src/main/java/ru/otus/comparison/AsyncComparison.java create mode 100644 2021-02/spring-18/src/main/java/ru/otus/comparison/SyncComparison.java diff --git a/2021-02/spring-18/.gitignore b/2021-02/spring-18/.gitignore new file mode 100644 index 00000000..fbe7a1ed --- /dev/null +++ b/2021-02/spring-18/.gitignore @@ -0,0 +1,7 @@ +.idea/ +*.iml + +target/ + +/node_modules +/output diff --git a/2021-02/spring-18/pom.xml b/2021-02/spring-18/pom.xml new file mode 100644 index 00000000..d4fa3309 --- /dev/null +++ b/2021-02/spring-18/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + ru.otus + spring-18 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.2.4.RELEASE + + + + 11 + 11 + + + + + io.reactivex.rxjava2 + rxjava + + + com.google.guava + guava + 27.1-jre + + + diff --git a/2021-02/spring-18/src/main/java/ru/otus/CreateExamples.java b/2021-02/spring-18/src/main/java/ru/otus/CreateExamples.java new file mode 100644 index 00000000..b8a21736 --- /dev/null +++ b/2021-02/spring-18/src/main/java/ru/otus/CreateExamples.java @@ -0,0 +1,35 @@ +package ru.otus; + +import io.reactivex.Observable; + +@SuppressWarnings("ResultOfMethodCallIgnored") +public class CreateExamples { + + public static void main(String[] args) { + Observable obs = justExample(); + obs.forEach(System.out::println); + obs.forEach(System.out::println); + } + + public static Observable justExample() { + return Observable.just("one", "two", "three"); + } + + public static Observable createExample() { + return Observable.create(emitter -> { + if (emitter.isDisposed()) { + return; + } + emitter.onNext("one"); + emitter.onNext("two");//! + emitter.onNext("three"); + if (!emitter.isDisposed()) { + emitter.onComplete(); + } + }); + } + + public static Observable deferExample() { + return Observable.defer(() -> Observable.just("one", "two", "three")); + } +} diff --git a/2021-02/spring-18/src/main/java/ru/otus/LiveLikeExample.java b/2021-02/spring-18/src/main/java/ru/otus/LiveLikeExample.java new file mode 100644 index 00000000..3f8e31d4 --- /dev/null +++ b/2021-02/spring-18/src/main/java/ru/otus/LiveLikeExample.java @@ -0,0 +1,26 @@ +package ru.otus; + +import io.reactivex.Observable; + +import java.io.IOException; + +public class LiveLikeExample { + + public static void main(String[] args) throws IOException { + + System.in.read(); + } + + static Observable getName() { + return Observable.just("Jake"); + } + + static Observable getSurname() { + return Observable.just("Foo"); + } + + static Observable save(String fullName) { + System.out.println(fullName + " saved!"); + return Observable.just("OK!"); + } +} diff --git a/2021-02/spring-18/src/main/java/ru/otus/OperatorsExample.java b/2021-02/spring-18/src/main/java/ru/otus/OperatorsExample.java new file mode 100644 index 00000000..92efffa6 --- /dev/null +++ b/2021-02/spring-18/src/main/java/ru/otus/OperatorsExample.java @@ -0,0 +1,79 @@ +package ru.otus; + +import com.google.common.collect.ImmutableList; +import io.reactivex.Observable; +import io.reactivex.ObservableTransformer; +import io.reactivex.schedulers.Schedulers; +import io.reactivex.subjects.PublishSubject; + +import java.time.LocalDate; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +@SuppressWarnings("ResultOfMethodCallIgnored") +public class OperatorsExample { + public static void main(String[] args) throws Exception { + simpleExample(); + System.in.read(); + } + + public static void simpleExample() throws Exception { + List persons = ImmutableList.of( + new Person("John", "Dow", "male", LocalDate.of(1992, 3, 12)), + new Person("Jane", "Dow", "female", LocalDate.of(2001, 6, 23)), + new Person("Howard", "Lovecraft", "male", LocalDate.of(1890, 8, 20)), + new Person("Joanne", "Rowling", "female", LocalDate.of(1965, 6, 30))); + + Observable.fromIterable(persons) + .filter( + person -> person.getBirth().isAfter(LocalDate.of(1990, 1, 1)) + ) + .map(p -> p.getFirstName() + " " + p.getLastName()) + .toList() + .subscribe(System.out::println); + } + + public static void publisherExample() throws Exception { + final Observable ob = magicPublisher(); + System.out.println("First subscribed"); + ob.subscribe(System.out::println); + Thread.sleep(5000); + System.out.println("Second subscribed"); + ob.subscribe(System.out::println); + } + + public static Observable magicPublisher() { + Random r = new Random(1); + AtomicInteger i = new AtomicInteger(); + final Observable obs = Observable.generate(emitter -> + emitter.onNext("" + i.incrementAndGet())) + .concatMap(s -> Observable.just(s).delay(r.nextInt(1000), TimeUnit.MILLISECONDS)) + .subscribeOn(Schedulers.newThread()); + PublishSubject subject = PublishSubject.create(); + +// BehaviorSubject subject = BehaviorSubject.create(); + +// AsyncSubject subject = AsyncSubject.create(); +// CompletableFuture.runAsync(() -> { +// try { +// Thread.sleep(7000); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// subject.onComplete(); +// }); + +// ReplaySubject subject = ReplaySubject.create(); + obs.subscribe(subject); + return subject; + } + + //composeExmaple + private static ObservableTransformer filterAndUpperCase() { + return upstream -> upstream + .filter(s -> s.length() >= 4) + .map(String::toUpperCase); + } +} diff --git a/2021-02/spring-18/src/main/java/ru/otus/Person.java b/2021-02/spring-18/src/main/java/ru/otus/Person.java new file mode 100644 index 00000000..72d3f520 --- /dev/null +++ b/2021-02/spring-18/src/main/java/ru/otus/Person.java @@ -0,0 +1,66 @@ +package ru.otus; + +import java.time.LocalDate; +import java.util.Objects; + +public class Person { + private String firstName; + private String lastName; + private String gender; + private LocalDate birth; + + public Person(String firstName, String lastName, String gender, LocalDate birth) { + this.firstName = firstName; + this.lastName = lastName; + this.gender = gender; + this.birth = birth; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public LocalDate getBirth() { + return birth; + } + + public void setBirth(LocalDate birth) { + this.birth = birth; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Person person = (Person) o; + return Objects.equals(firstName, person.firstName) && + Objects.equals(lastName, person.lastName) && + Objects.equals(gender, person.gender) && + Objects.equals(birth, person.birth); + } + + @Override + public int hashCode() { + return Objects.hash(firstName, lastName, gender, birth); + } +} diff --git a/2021-02/spring-18/src/main/java/ru/otus/comparison/AsyncComparison.java b/2021-02/spring-18/src/main/java/ru/otus/comparison/AsyncComparison.java new file mode 100644 index 00000000..5b0b227a --- /dev/null +++ b/2021-02/spring-18/src/main/java/ru/otus/comparison/AsyncComparison.java @@ -0,0 +1,40 @@ +package ru.otus.comparison; + +import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; + +import java.io.IOException; + +public class AsyncComparison { + + public static void main(String[] args) throws IOException { + final long timeStarted = System.currentTimeMillis(); + final Observable obs = controller(); + obs.subscribe(System.out::println); + System.out.println("Wait time " + (System.currentTimeMillis() - timeStarted)); + System.in.read(); + } + + static Observable controller() { + return service(); + } + + static Observable service() { + return repository(); + } + + static Observable repository() { + return database(); + } + + static Observable database() { + return Observable.defer(() -> { + try { + Thread.sleep(4000); + } catch (Exception e) { + System.out.println("Don't do this"); + } + return Observable.just("Hello world"); + }).subscribeOn(Schedulers.newThread()); + } +} diff --git a/2021-02/spring-18/src/main/java/ru/otus/comparison/SyncComparison.java b/2021-02/spring-18/src/main/java/ru/otus/comparison/SyncComparison.java new file mode 100644 index 00000000..c2bf7077 --- /dev/null +++ b/2021-02/spring-18/src/main/java/ru/otus/comparison/SyncComparison.java @@ -0,0 +1,31 @@ +package ru.otus.comparison; + +public class SyncComparison { + + public static void main(String[] args) { + final long timeStarted = System.currentTimeMillis(); + System.out.println(controller()); + System.out.println(System.currentTimeMillis() - timeStarted); + } + + static String controller() { + return service(); + } + + static String service() { + return repository(); + } + + static String repository() { + return database(); + } + + static String database() { + try { + Thread.sleep(4000); + } catch (Exception e) { + System.out.println("Don't do this"); + } + return "Hello world"; + } +} From 73588454a34c11b42795361a8a0a0aaada5619ee Mon Sep 17 00:00:00 2001 From: saroff Date: Wed, 12 May 2021 19:35:41 +0300 Subject: [PATCH 06/25] Reactive spring 21-02 --- 2021-02/spring-19/.gitignore | 24 +++++++++ 2021-02/spring-19/pom.xml | 18 +++++++ .../spring-19-reactive-spring-data/pom.xml | 51 +++++++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 20 ++++++++ .../java/ru/otus/spring/domain/Account.java | 37 ++++++++++++++ .../java/ru/otus/spring/domain/Person.java | 27 ++++++++++ .../spring/repostory/AccountRepository.java | 8 +++ .../spring/repostory/PersonRepository.java | 15 ++++++ .../src/main/resources/application.yml | 0 2021-02/spring-19/spring-19-reactor/pom.xml | 42 +++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 20 ++++++++ .../ru/otus/spring/reactor/FluxService.java | 50 ++++++++++++++++++ .../java/ru/otus/spring/reactor/Message.java | 14 +++++ .../otus/spring/reactor/NonFluxService.java | 24 +++++++++ 2021-02/spring-19/spring-19-web-flux/pom.xml | 44 ++++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 14 +++++ .../ru/otus/spring/ReactorController.java | 33 ++++++++++++ .../ru/otus/spring/RxJava2Controller.java | 20 ++++++++ 18 files changed, 461 insertions(+) create mode 100644 2021-02/spring-19/.gitignore create mode 100644 2021-02/spring-19/pom.xml create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/pom.xml create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Account.java create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/AccountRepository.java create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-02/spring-19/spring-19-reactive-spring-data/src/main/resources/application.yml create mode 100644 2021-02/spring-19/spring-19-reactor/pom.xml create mode 100644 2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/FluxService.java create mode 100644 2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/Message.java create mode 100644 2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/NonFluxService.java create mode 100644 2021-02/spring-19/spring-19-web-flux/pom.xml create mode 100644 2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/ReactorController.java create mode 100644 2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/RxJava2Controller.java diff --git a/2021-02/spring-19/.gitignore b/2021-02/spring-19/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-02/spring-19/.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-19/pom.xml b/2021-02/spring-19/pom.xml new file mode 100644 index 00000000..06dd04ce --- /dev/null +++ b/2021-02/spring-19/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + ru.otus + spring-19 + 1.0 + + pom + + + spring-19-web-flux + spring-19-reactor + spring-19-reactive-spring-data + + diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/pom.xml b/2021-02/spring-19/spring-19-reactive-spring-data/pom.xml new file mode 100644 index 00000000..5e963c48 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactive-spring-data/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + ru.otus + spring-19-reactive-spring-data + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + + 13 + 13 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + org.mongodb + mongodb-driver-reactivestreams + + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..5929a2e2 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,20 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import ru.otus.spring.repostory.AccountRepository; +import ru.otus.spring.repostory.PersonRepository; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) throws InterruptedException { + ApplicationContext context = SpringApplication.run(Main.class); + + PersonRepository repository = context.getBean(PersonRepository.class); + AccountRepository accountRepository = context.getBean(AccountRepository.class); + + Thread.sleep(20000); + } +} diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Account.java b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Account.java new file mode 100644 index 00000000..2158e0b7 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Account.java @@ -0,0 +1,37 @@ +package ru.otus.spring.domain; + +public class Account { + private String id; + private String personId; + private Long amount; + + public Account(String id, String personId, Long amount) { + this.id = id; + this.personId = personId; + this.amount = amount; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPersonId() { + return personId; + } + + public void setPersonId(String personId) { + this.personId = personId; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } +} diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Person.java b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..2bdc3894 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,27 @@ +package ru.otus.spring.domain; + +public class Person { + + private String id; + private String name; + + public Person(String name) { + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/AccountRepository.java b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/AccountRepository.java new file mode 100644 index 00000000..22bd9275 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/AccountRepository.java @@ -0,0 +1,8 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import ru.otus.spring.domain.Account; +import ru.otus.spring.domain.Person; + +public interface AccountRepository extends ReactiveMongoRepository { +} diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/PersonRepository.java new file mode 100644 index 00000000..ddd15cef --- /dev/null +++ b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/java/ru/otus/spring/repostory/PersonRepository.java @@ -0,0 +1,15 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.mongodb.repository.Query; +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; + +public interface PersonRepository extends ReactiveMongoRepository { + Flux findByName(String name); + + @Query("{ 'name': ?0 }") + Mono findFirstByName(String name); +} diff --git a/2021-02/spring-19/spring-19-reactive-spring-data/src/main/resources/application.yml b/2021-02/spring-19/spring-19-reactive-spring-data/src/main/resources/application.yml new file mode 100644 index 00000000..e69de29b diff --git a/2021-02/spring-19/spring-19-reactor/pom.xml b/2021-02/spring-19/spring-19-reactor/pom.xml new file mode 100644 index 00000000..d66f1f55 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactor/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + ru.otus + spring-19-reactor + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + + 13 + 13 + + + + + org.springframework.boot + spring-boot-starter + + + io.projectreactor + reactor-core + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..cc406c39 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,20 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import ru.otus.spring.reactor.FluxService; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + + FluxService service = context.getBean(FluxService.class); + + service.printHello("Ivan"); + } +} + + diff --git a/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/FluxService.java b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/FluxService.java new file mode 100644 index 00000000..e2e67f71 --- /dev/null +++ b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/FluxService.java @@ -0,0 +1,50 @@ +package ru.otus.spring.reactor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import reactor.core.Disposable; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Service +public class FluxService { + + private final Logger logger = LoggerFactory.getLogger(FluxService.class); + + private final NonFluxService nonFluxService; + private final DirectProcessor processor; + private final Disposable flow; + + @Autowired + public FluxService(NonFluxService nonFluxService) { + this.nonFluxService = nonFluxService; + // Создаём процессор - это reactor-овская реализация reactive-stream интерфейса + // Direct processor, кстати - это простой последовательный вызов методов) + processor = DirectProcessor.create(); + // Здесь мы настриваем flow + flow = Mono.from(processor) + .map(nonFluxService::nonFluxSayHello) + .subscribe(this::printMessage); + } + + /** + * Этот метод будет инициировать асинзронную обрабтку сообщения + * + * @param name это имя будет приходить из не-reactor окружения + */ + public void printHello(String name) { + processor.onNext(new Message(name)); + } + + /** + * А это терминальный шаг для сообщения + * + * @param message а это финальный шаг для сообщения, отсюда можно вернуть рзультат в не-реактив окружение + */ + private void printMessage(Message message) { + logger.info("Message received: {}", message.getValue()); + } +} diff --git a/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/Message.java b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/Message.java new file mode 100644 index 00000000..ac58a62c --- /dev/null +++ b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/Message.java @@ -0,0 +1,14 @@ +package ru.otus.spring.reactor; + +public class Message { + + private final String value; + + public Message(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/NonFluxService.java b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/NonFluxService.java new file mode 100644 index 00000000..1b9be28a --- /dev/null +++ b/2021-02/spring-19/spring-19-reactor/src/main/java/ru/otus/spring/reactor/NonFluxService.java @@ -0,0 +1,24 @@ +package ru.otus.spring.reactor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class NonFluxService { + + private final Logger logger = LoggerFactory.getLogger(NonFluxService.class); + + public Message nonFluxSayHello(Message message) { + logger.info("Message received in non-flux service: {}", message.getValue()); + + final String name = message.getValue(); + final String withHello = "Hello, " + name + "!"; + try { + Thread.sleep(1000); + return new Message(withHello); + } catch (InterruptedException ex) { + return new Message(withHello); + } + } +} diff --git a/2021-02/spring-19/spring-19-web-flux/pom.xml b/2021-02/spring-19/spring-19-web-flux/pom.xml new file mode 100644 index 00000000..15775369 --- /dev/null +++ b/2021-02/spring-19/spring-19-web-flux/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + ru.otus + spring-19-web-flux + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + + 13 + 13 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + io.reactivex.rxjava2 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..cad1ae76 --- /dev/null +++ b/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,14 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + SpringApplication.run(Main.class); + } +} + + diff --git a/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/ReactorController.java b/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/ReactorController.java new file mode 100644 index 00000000..537ead63 --- /dev/null +++ b/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/ReactorController.java @@ -0,0 +1,33 @@ +package ru.otus.spring; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; + +@RestController +public class ReactorController { + + @GetMapping("/flux/one") + public Mono one() { + return Mono.just("one"); + } + + @GetMapping("/flux/ten") + public Flux list() { + return Flux.range(1, 10); + } + + @GetMapping(path = "/flux/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux stream() { + return Flux.generate(() -> 0, (state, emitter) -> { + emitter.next(state); + return state + 1; + }) + .delayElements(Duration.ofSeconds(1L)) + .map(Object::toString); + } +} diff --git a/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/RxJava2Controller.java b/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/RxJava2Controller.java new file mode 100644 index 00000000..edf0035f --- /dev/null +++ b/2021-02/spring-19/spring-19-web-flux/src/main/java/ru/otus/spring/RxJava2Controller.java @@ -0,0 +1,20 @@ +package ru.otus.spring; + +import io.reactivex.Flowable; +import io.reactivex.Single; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class RxJava2Controller { + + @GetMapping("/rx/one") + public Single single() { + return Single.just("one"); + } + + @GetMapping("/rx/ten") + public Flowable list() { + return Flowable.range(1, 10); + } +} From 7e5907087bb26a522b07c69d2a82e5cdb672d24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Fri, 14 May 2021 21:46:23 +0400 Subject: [PATCH 07/25] 2021-03 spring-13-data-jpa added --- 2021-03/spring-13-data-jpa/.gitignore | 4 ++ 2021-03/spring-13-data-jpa/pom.xml | 17 ++++++ .../spring-13-exercise/.gitignore | 4 ++ .../spring-13-exercise/pom.xml | 50 +++++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 22 ++++++++ .../java/ru/otus/springdata/domain/Email.java | 21 +++++++ .../ru/otus/springdata/domain/Person.java | 26 +++++++++ .../repository/EmailRepository.java | 4 ++ .../repository/PersonRepository.java | 4 ++ .../spring-13-solution/.gitignore | 4 ++ .../spring-13-solution/pom.xml | 55 +++++++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 48 ++++++++++++++++ .../java/ru/otus/springdata/domain/Email.java | 27 +++++++++ .../ru/otus/springdata/domain/Person.java | 30 ++++++++++ .../repository/EmailRepository.java | 21 +++++++ .../repository/PersonRepository.java | 16 ++++++ 16 files changed, 353 insertions(+) create mode 100644 2021-03/spring-13-data-jpa/.gitignore create mode 100644 2021-03/spring-13-data-jpa/pom.xml create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/.gitignore create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Email.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Person.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/.gitignore create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/pom.xml create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Email.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Person.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java diff --git a/2021-03/spring-13-data-jpa/.gitignore b/2021-03/spring-13-data-jpa/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/pom.xml b/2021-03/spring-13-data-jpa/pom.xml new file mode 100644 index 00000000..da37d94f --- /dev/null +++ b/2021-03/spring-13-data-jpa/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + ru.otus + spring-13-data-jpa + 1.0 + + pom + + + spring-13-exercise + spring-13-solution + + diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/.gitignore b/2021-03/spring-13-data-jpa/spring-13-exercise/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml b/2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml new file mode 100644 index 00000000..5f36b7ef --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + ru.otus + spring-13-exercise + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..59a032bd --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,22 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +//import ru.otus.spring10.repostory.PersonRepository; + + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + //PersonRepository personRepository = context.getBean(PersonRepository.class); + //EmailRepository emailRepository = context.getBean(EmailRepository.class); + + // personRepository.save(new Person("Pushkin")); + // personRepository.save(new Person("Lermontov")); + } + + +} diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Email.java new file mode 100644 index 00000000..5cb61fa0 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Email.java @@ -0,0 +1,21 @@ +package ru.otus.springdata.domain; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Email { + + private long id; + + private String address; + + public Email(String address) { + this.address = address; + } + +} diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Person.java new file mode 100644 index 00000000..cc8e6d0a --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Person.java @@ -0,0 +1,26 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Person { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + public Person(String name) { + this.name = name; + } +} diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java new file mode 100644 index 00000000..f0f5424f --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -0,0 +1,4 @@ +package ru.otus.springdata.repository; + +public interface EmailRepository { +} diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java new file mode 100644 index 00000000..a084d42a --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -0,0 +1,4 @@ +package ru.otus.springdata.repository; + +public interface PersonRepository { +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/.gitignore b/2021-03/spring-13-data-jpa/spring-13-solution/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/pom.xml b/2021-03/spring-13-data-jpa/spring-13-solution/pom.xml new file mode 100644 index 00000000..5bc2c71d --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + spring-13-solution + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..1e634ab6 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,48 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import ru.otus.springdata.domain.Email; +import ru.otus.springdata.domain.Person; +import ru.otus.springdata.repository.EmailRepository; +import ru.otus.springdata.repository.PersonRepository; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + PersonRepository personRepository = context.getBean(PersonRepository.class); + EmailRepository emailRepository = context.getBean(EmailRepository.class); + + Email emailP = new Email("alex@pushkin.ru"); + emailP = emailRepository.save(emailP); + + Email emailL = new Email("michail@lermontov.ru"); + emailL = emailRepository.save(emailL); + + personRepository.save(new Person("Pushkin", emailP)); + personRepository.save(new Person("Lermontov",emailL)); + + System.out.println("\n\nИщем Пушкина"); + personRepository.findByName("Pushkin").ifPresent(System.out::println); + + System.out.println("\n\nИщем всех пёрсонов"); + System.out.println(emailRepository.findAll()); + + System.out.println("\n\nИщем Пушкина по его почте"); + personRepository.findByEmailAddress("alex@pushkin.ru").ifPresent(System.out::println); + + System.out.println("\n\nИщем почту Пушкина"); + emailRepository.findByEmailAddress("alex@pushkin.ru").ifPresent(System.out::println); + + System.out.println("\n\nОбновляем почту Лермонтову"); + System.out.println("До обновления: " + emailL); + emailRepository.updateEmailById(emailL.getId(), "michail1984@lermontov.ru"); + emailRepository.findById(emailL.getId()).ifPresent(e -> System.out.println("После обновления: " + e)); + + System.out.println("\n\n"); + + } +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Email.java new file mode 100644 index 00000000..e3b8d344 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Email.java @@ -0,0 +1,27 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Email { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String address; + + public Email(String address) { + this.address = address; + } +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Person.java new file mode 100644 index 00000000..e24e42aa --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Person.java @@ -0,0 +1,30 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + @OneToOne(orphanRemoval = true) + @JoinColumn(name = "email_id") + private Email email; + + public Person(String name, Email email) { + this.name = name; + this.email = email; + } + +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java new file mode 100644 index 00000000..66b4da80 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -0,0 +1,21 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.springdata.domain.Email; + +import java.util.Optional; + +public interface EmailRepository extends JpaRepository { + + @Query("select e from Email e where e.address = :address") + Optional findByEmailAddress(@Param("address") String email); + + @Modifying + @Transactional + @Query("update Email e set e.address = :address where e.id = :id") + void updateEmailById(@Param("id") long id, @Param("address") String address); +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java new file mode 100644 index 00000000..8f41f889 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -0,0 +1,16 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.springdata.domain.Person; + +import java.util.List; +import java.util.Optional; + +public interface PersonRepository extends CrudRepository { + + List findAll(); + + Optional findByName(String s); + + Optional findByEmailAddress(String email); +} From 36f1839d6538db142f7664835d142527dd5d3867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Fri, 14 May 2021 22:22:13 +0400 Subject: [PATCH 08/25] 2021-03 spring-13-data-jpa updated --- .../src/main/resources/application.yml | 20 +++++++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 14 +++++++++---- .../repository/EmailRepository.java | 2 +- .../repository/EmailRepositoryCustom.java | 9 +++++++++ .../repository/EmailRepositoryCustomImpl.java | 20 +++++++++++++++++++ .../repository/PersonRepository.java | 2 ++ .../src/main/resources/application.yml | 20 +++++++++++++++++++ 7 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/resources/application.yml create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java create mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/resources/application.yml diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/resources/application.yml new file mode 100644 index 00000000..bd5b0f98 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: false + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java index 1e634ab6..4a390580 100644 --- a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java @@ -22,21 +22,27 @@ public class Main { Email emailL = new Email("michail@lermontov.ru"); emailL = emailRepository.save(emailL); - personRepository.save(new Person("Pushkin", emailP)); - personRepository.save(new Person("Lermontov",emailL)); + Person pushkin = personRepository.save(new Person("Pushkin", emailP)); + Person lermontov = personRepository.save(new Person("Lermontov",emailL)); + + System.out.println("\n\nИщем всех пёрсонов"); + System.out.println(personRepository.findAll()); System.out.println("\n\nИщем Пушкина"); personRepository.findByName("Pushkin").ifPresent(System.out::println); - System.out.println("\n\nИщем всех пёрсонов"); + System.out.println("\n\nИщем все почты"); System.out.println(emailRepository.findAll()); System.out.println("\n\nИщем Пушкина по его почте"); personRepository.findByEmailAddress("alex@pushkin.ru").ifPresent(System.out::println); - System.out.println("\n\nИщем почту Пушкина"); + System.out.println("\n\nИщем почту Пушкина по ее адресу"); emailRepository.findByEmailAddress("alex@pushkin.ru").ifPresent(System.out::println); + System.out.println("\n\nИщем почту Лермонтова по его (Лермонтова) id"); + emailRepository.findByPersonId(lermontov.getId()).ifPresent(System.out::println); + System.out.println("\n\nОбновляем почту Лермонтову"); System.out.println("До обновления: " + emailL); emailRepository.updateEmailById(emailL.getId(), "michail1984@lermontov.ru"); diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java index 66b4da80..a76a43ff 100644 --- a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -9,7 +9,7 @@ import ru.otus.springdata.domain.Email; import java.util.Optional; -public interface EmailRepository extends JpaRepository { +public interface EmailRepository extends JpaRepository, EmailRepositoryCustom { @Query("select e from Email e where e.address = :address") Optional findByEmailAddress(@Param("address") String email); diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java new file mode 100644 index 00000000..01daff77 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java @@ -0,0 +1,9 @@ +package ru.otus.springdata.repository; + +import ru.otus.springdata.domain.Email; + +import java.util.Optional; + +public interface EmailRepositoryCustom { + Optional findByPersonId(long personId); +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java new file mode 100644 index 00000000..26e501a7 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java @@ -0,0 +1,20 @@ +package ru.otus.springdata.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import ru.otus.springdata.domain.Email; +import ru.otus.springdata.domain.Person; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class EmailRepositoryCustomImpl implements EmailRepositoryCustom { + + private final PersonRepository personRepository; + + @Override + public Optional findByPersonId(long personId) { + return personRepository.findById(personId).map(Person::getEmail); + } +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java index 8f41f889..a6aa8dd3 100644 --- a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -1,5 +1,6 @@ package ru.otus.springdata.repository; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.repository.CrudRepository; import ru.otus.springdata.domain.Person; @@ -8,6 +9,7 @@ import java.util.Optional; public interface PersonRepository extends CrudRepository { + @EntityGraph(attributePaths = "email") List findAll(); Optional findByName(String s); diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/resources/application.yml new file mode 100644 index 00000000..bd5b0f98 --- /dev/null +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: false + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file From 066a0245129a5d2feb0284b2992de7b0d2eb2db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Fri, 14 May 2021 22:32:39 +0400 Subject: [PATCH 09/25] 2021-03 spring-13-data-jpa updated --- .../src/main/java/ru/otus/springdata/Main.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java index 4a390580..912a77cc 100644 --- a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java +++ b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java @@ -3,6 +3,8 @@ package ru.otus.springdata; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; import ru.otus.springdata.domain.Email; import ru.otus.springdata.domain.Person; import ru.otus.springdata.repository.EmailRepository; @@ -23,7 +25,8 @@ public class Main { emailL = emailRepository.save(emailL); Person pushkin = personRepository.save(new Person("Pushkin", emailP)); - Person lermontov = personRepository.save(new Person("Lermontov",emailL)); + Person lermontov = personRepository.save(new Person("Lermontov", emailL)); + System.out.println("\n\nИщем всех пёрсонов"); System.out.println(personRepository.findAll()); @@ -48,6 +51,16 @@ public class Main { emailRepository.updateEmailById(emailL.getId(), "michail1984@lermontov.ru"); emailRepository.findById(emailL.getId()).ifPresent(e -> System.out.println("После обновления: " + e)); + + System.out.println("\n\nИщем все почты по вхождению \".ru\""); + ExampleMatcher ignoringExampleMatcher = ExampleMatcher.matchingAny() + .withMatcher("address", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()) + .withIgnorePaths("id"); + + Example example = Example.of(new Email(1, ".ru"), ignoringExampleMatcher); + + System.out.println(emailRepository.findAll(example)); + System.out.println("\n\n"); } From 548bd29b0a4f3d06a873928e25c4f05c79fc82c8 Mon Sep 17 00:00:00 2001 From: Yuriy Dvorzhetskiy Date: Sat, 15 May 2021 10:53:40 +0300 Subject: [PATCH 10/25] 2021-02 - 20 --- 2021-02/spring-20/.gitignore | 4 + 2021-02/spring-20/pom.xml | 17 ++++ 2021-02/spring-20/spring-20-exercise/pom.xml | 60 ++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 80 +++++++++++++++++++ .../java/ru/otus/spring/domain/Person.java | 55 +++++++++++++ .../spring/repository/PersonRepository.java | 20 +++++ .../otus/spring/rest/AnnotatedController.java | 33 ++++++++ .../ru/otus/spring/rest/PersonController.java | 37 +++++++++ .../repository/PersonRepositoryTest.java | 31 +++++++ .../spring/rest/PersonControllerTest.java | 32 ++++++++ 2021-02/spring-20/spring-20-solution/pom.xml | 60 ++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 58 ++++++++++++++ .../java/ru/otus/spring/domain/Person.java | 55 +++++++++++++ .../spring/repository/PersonRepository.java | 20 +++++ .../otus/spring/rest/AnnotatedController.java | 33 ++++++++ .../ru/otus/spring/rest/PersonController.java | 42 ++++++++++ .../repository/PersonRepositoryTest.java | 44 ++++++++++ .../spring/rest/PersonControllerTest.java | 32 ++++++++ 18 files changed, 713 insertions(+) create mode 100644 2021-02/spring-20/.gitignore create mode 100644 2021-02/spring-20/pom.xml create mode 100644 2021-02/spring-20/spring-20-exercise/pom.xml create mode 100644 2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/repository/PersonRepository.java create mode 100644 2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java create mode 100644 2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java create mode 100644 2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java create mode 100644 2021-02/spring-20/spring-20-solution/pom.xml create mode 100644 2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/repository/PersonRepository.java create mode 100644 2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java create mode 100644 2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java create mode 100644 2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java diff --git a/2021-02/spring-20/.gitignore b/2021-02/spring-20/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-02/spring-20/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-02/spring-20/pom.xml b/2021-02/spring-20/pom.xml new file mode 100644 index 00000000..8997be90 --- /dev/null +++ b/2021-02/spring-20/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + ru.otus + spring-20 + 1.0 + + pom + + + spring-20-exercise + spring-20-solution + + diff --git a/2021-02/spring-20/spring-20-exercise/pom.xml b/2021-02/spring-20/spring-20-exercise/pom.xml new file mode 100644 index 00000000..7c53ac89 --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + ru.otus + spring-20-exercise + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + + 11 + 11 + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + + + + + io.projectreactor + reactor-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..dcecffd5 --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,80 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repository.PersonRepository; + +import java.util.Arrays; + +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +@SpringBootApplication +public class Main { + + public static void main( String[] args ) { + ApplicationContext context = SpringApplication.run( Main.class ); + PersonRepository repository = context.getBean( PersonRepository.class ); + + repository.saveAll( Arrays.asList( + new Person( "Pushkin", 22 ), + new Person( "Lermontov", 22 ), + new Person( "Tolstoy", 60 ) + ) ).subscribe( p -> System.out.println( p.getLastName() ) ); + + } + + @Bean + public RouterFunction composedRoutes( PersonRepository repository ) { + + PersonHandler handler = new PersonHandler( repository ); + + RouterFunction route = route() + .GET( "/func/person", accept( APPLICATION_JSON ), handler::list ) + .GET( "/func/person/{id}", accept( APPLICATION_JSON ), + request -> repository.findById( request.pathVariable( "id" ) ) + .flatMap( person -> ok().contentType( APPLICATION_JSON ).body( fromObject( person ) ) ) + ) + .GET( "/func/person/age/{age}", accept( APPLICATION_JSON ), + serverRequest -> ok().contentType( APPLICATION_JSON ) + .body( repository.findAllByAge( Integer.valueOf( serverRequest.pathVariable( "age" ) ) ), Person.class ) ) + .GET( "/func/person/find", accept( APPLICATION_JSON ), + serverRequest -> ok().contentType( APPLICATION_JSON ) + .body( repository.findAllByAge( Integer.valueOf( serverRequest.queryParam( "age" ).get() ) ), Person.class ) ) + .build(); + + return route; + } + + + static class PersonHandler { + + private PersonRepository repository; + + PersonHandler( PersonRepository repository ) { + this.repository = repository; + } + + Mono list( ServerRequest request ) { + return ok().contentType( APPLICATION_JSON ).body( repository.findAll(), Person.class ); + } + + Mono listAge( ServerRequest request ) { + System.out.println( "I'm here" ); + return ok().contentType( APPLICATION_JSON ) + .body( repository.findAllByAge( Integer.valueOf( request.queryParam( "age" ).get() ) ), Person.class ); + } + } +} + + diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/domain/Person.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..d40218ec --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,55 @@ +package ru.otus.spring.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +@Document +public class Person { + + @Id + private String id; + + @JsonProperty("name") + @Field("name") + private String lastName; + + private int age; + + public Person() { + } + + public Person(String lastName) { + this.lastName = lastName; + } + + public Person(String lastName, int age) { + this.lastName = lastName; + this.age = age; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/repository/PersonRepository.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/repository/PersonRepository.java new file mode 100644 index 00000000..0584f0cb --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/repository/PersonRepository.java @@ -0,0 +1,20 @@ +package ru.otus.spring.repository; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; + +public interface PersonRepository + extends ReactiveMongoRepository { + + Flux findAll(); + + Mono findById(String id); + + Mono save(Mono person); + + Flux findAllByLastName(String lastName); + + Flux findAllByAge(int age); +} diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java new file mode 100644 index 00000000..0f1a75b5 --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java @@ -0,0 +1,33 @@ +package ru.otus.spring.rest; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; + +@RestController +public class AnnotatedController { + + @GetMapping("/flux/one") + public Mono one() { + return Mono.just( "one" ); + } + + @GetMapping("/flux/ten") + public Flux list() { + return Flux.range( 1, 10 ).delayElements( Duration.ofSeconds( 1 ) ); + } + + @GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux stream() { + return Flux.generate( () -> 0, ( state, emitter ) -> { + emitter.next( state ); + return state + 1; + } ) + .delayElements( Duration.ofSeconds( 1L ) ) + .map( i -> "" + i ); + } +} diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..0ed2451c --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,37 @@ +package ru.otus.spring.rest; + +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repository.PersonRepository; + +@RestController +public class PersonController { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @GetMapping("/person") + public Flux all() { + return repository.findAll(); + } + + @GetMapping("/person/{id}") + public Mono byId(@PathVariable("id") String id) { + return repository.findById(id); + } + + @PostMapping("/person") + public Mono save(@RequestBody Mono dto) { + return repository.save(dto); + } + + @GetMapping("/person/find") + public Flux byName(@RequestParam("name") String name){ + return repository.findAllByLastName( name ); + } +} diff --git a/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java new file mode 100644 index 00000000..0ecf3ca8 --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java @@ -0,0 +1,31 @@ +package ru.otus.spring.repository; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import ru.otus.spring.domain.Person; + +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@DataMongoTest +public class PersonRepositoryTest { + + @Autowired + private PersonRepository repository; + + @Test + public void shouldSetIdOnSave() { + Mono personMono = repository.save(new Person("Bill", 12)); + + StepVerifier + .create(personMono) + .assertNext(person -> assertNotNull(person.getId())) + .expectComplete() + .verify(); + } +} diff --git a/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..b6eb9806 --- /dev/null +++ b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,32 @@ +package ru.otus.spring.rest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.function.server.RouterFunction; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class PersonControllerTest { + + @Autowired + private RouterFunction route; + + @Test + public void testRoute() { + WebTestClient client = WebTestClient + .bindToRouterFunction(route) + .build(); + + client.get() + .uri("/func/person") + .exchange() + .expectStatus() + .isOk(); + } + + +} diff --git a/2021-02/spring-20/spring-20-solution/pom.xml b/2021-02/spring-20/spring-20-solution/pom.xml new file mode 100644 index 00000000..2ac16da7 --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + ru.otus + spring-20-solution + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.4 + + + + 11 + 11 + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + + + + + io.projectreactor + reactor-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..934532de --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,58 @@ +package ru.otus.spring; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repository.PersonRepository; + +import java.util.Arrays; + +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RequestPredicates.queryParam; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.ServerResponse.notFound; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ApplicationContext context = SpringApplication.run(Main.class); + PersonRepository repository = context.getBean(PersonRepository.class); + + repository.saveAll(Arrays.asList( + new Person("Pushkin", 22), + new Person("Lermontov", 22), + new Person("Tolstoy", 60) + )).subscribe(p -> System.out.println(p.getLastName())); + + } + + @Bean + public RouterFunction composedRoutes(PersonRepository repository) { + return route() + .GET("/func/person", queryParam("name", StringUtils::isNotEmpty), + request -> request.queryParam("name") + .map(repository::findAllByLastName) + .map(persons -> ok().body(persons, Person.class)) + .orElse(notFound().build()) + ) + .GET("/func/person", accept(APPLICATION_JSON), + request -> ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class)) + .GET("/func/person/{id}", accept(APPLICATION_JSON), + request -> repository.findById(request.pathVariable("id")) + .flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromObject(person))) + ).build(); + } +} + + diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/domain/Person.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..d40218ec --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,55 @@ +package ru.otus.spring.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +@Document +public class Person { + + @Id + private String id; + + @JsonProperty("name") + @Field("name") + private String lastName; + + private int age; + + public Person() { + } + + public Person(String lastName) { + this.lastName = lastName; + } + + public Person(String lastName, int age) { + this.lastName = lastName; + this.age = age; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/repository/PersonRepository.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/repository/PersonRepository.java new file mode 100644 index 00000000..0584f0cb --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/repository/PersonRepository.java @@ -0,0 +1,20 @@ +package ru.otus.spring.repository; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; + +public interface PersonRepository + extends ReactiveMongoRepository { + + Flux findAll(); + + Mono findById(String id); + + Mono save(Mono person); + + Flux findAllByLastName(String lastName); + + Flux findAllByAge(int age); +} diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java new file mode 100644 index 00000000..3d723f35 --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java @@ -0,0 +1,33 @@ +package ru.otus.spring.rest; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; + +@RestController +public class AnnotatedController { + + @GetMapping("/flux/one") + public Mono one() { + return Mono.just("one"); + } + + @GetMapping("/flux/ten") + public Flux list() { + return Flux.range(1, 10); + } + + @GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux stream() { + return Flux.generate(() -> 0, (state, emitter) -> { + emitter.next(state); + return state + 1; + }) + .delayElements(Duration.ofSeconds(1L)) + .map(i -> "" + i); + } +} diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..2edcaea9 --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,42 @@ +package ru.otus.spring.rest; + +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repository.PersonRepository; + +@RestController +public class PersonController { + + private PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @GetMapping("/person") + public Flux all() { + return repository.findAll(); + } + + @GetMapping("/person/{id}") + public Mono byId(@PathVariable("id") String id) { + return repository.findById(id); + } + + @GetMapping("/person/byname") + public Flux byName(@RequestParam("name") String lastName) { + return repository.findAllByLastName(lastName); + } + + @GetMapping("/person/byage") + public Flux byAge(@RequestParam int age) { + return repository.findAllByAge(age); + } + + @PostMapping("/person") + public Mono save(@RequestBody Mono dto) { + return repository.save(dto); + } +} diff --git a/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java new file mode 100644 index 00000000..037dc9b7 --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java @@ -0,0 +1,44 @@ +package ru.otus.spring.repository; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import ru.otus.spring.domain.Person; + +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@DataMongoTest +public class PersonRepositoryTest { + + @Autowired + private PersonRepository repository; + + @Test + public void shouldSetIdOnSave() { + Mono personMono = repository.save(new Person("Bill", 12)); + + StepVerifier + .create(personMono) + .assertNext(person -> assertNotNull(person.getId())) + .expectComplete() + .verify(); + } + + @Test + public void shouldFindByAge() { + repository.save(new Person("Pushkin", 18)).block(); + + StepVerifier.create( + repository.findAllByAge(18) + ) + .expectFusion() + .expectNextCount(1) + .expectComplete() + .verify(); + } +} diff --git a/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..b6eb9806 --- /dev/null +++ b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,32 @@ +package ru.otus.spring.rest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.function.server.RouterFunction; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class PersonControllerTest { + + @Autowired + private RouterFunction route; + + @Test + public void testRoute() { + WebTestClient client = WebTestClient + .bindToRouterFunction(route) + .build(); + + client.get() + .uri("/func/person") + .exchange() + .expectStatus() + .isOk(); + } + + +} From 5ef742ba5cb8a9e1eef1b9c7ae3c4f94f1a3ec71 Mon Sep 17 00:00:00 2001 From: Yuriy Dvorzhetskiy Date: Sat, 15 May 2021 10:56:07 +0300 Subject: [PATCH 11/25] 2021-02 - 20 --- 2021-02/spring-20/spring-20-exercise/pom.xml | 1 + 2021-02/spring-20/spring-20-solution/pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/2021-02/spring-20/spring-20-exercise/pom.xml b/2021-02/spring-20/spring-20-exercise/pom.xml index 7c53ac89..f3041e59 100644 --- a/2021-02/spring-20/spring-20-exercise/pom.xml +++ b/2021-02/spring-20/spring-20-exercise/pom.xml @@ -12,6 +12,7 @@ org.springframework.boot spring-boot-starter-parent 2.4.4 + diff --git a/2021-02/spring-20/spring-20-solution/pom.xml b/2021-02/spring-20/spring-20-solution/pom.xml index 2ac16da7..5dfdf065 100644 --- a/2021-02/spring-20/spring-20-solution/pom.xml +++ b/2021-02/spring-20/spring-20-solution/pom.xml @@ -12,6 +12,7 @@ org.springframework.boot spring-boot-starter-parent 2.4.4 + From 209f82147718874a152aacc276f54e805b8e6cdf Mon Sep 17 00:00:00 2001 From: Yuriy Dvorzhetskiy Date: Sat, 15 May 2021 11:22:54 +0300 Subject: [PATCH 12/25] Fix --- 2021-02/spring-20/spring-20-exercise/pom.xml | 2 +- 2021-02/spring-20/spring-20-solution/pom.xml | 2 +- .../src/main/java/ru/otus/spring/rest/AnnotatedController.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/2021-02/spring-20/spring-20-exercise/pom.xml b/2021-02/spring-20/spring-20-exercise/pom.xml index f3041e59..71bbd272 100644 --- a/2021-02/spring-20/spring-20-exercise/pom.xml +++ b/2021-02/spring-20/spring-20-exercise/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.4 + 2.2.4.RELEASE diff --git a/2021-02/spring-20/spring-20-solution/pom.xml b/2021-02/spring-20/spring-20-solution/pom.xml index 5dfdf065..13d66a4f 100644 --- a/2021-02/spring-20/spring-20-solution/pom.xml +++ b/2021-02/spring-20/spring-20-solution/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.4 + 2.2.4.RELEASE diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java index 3d723f35..184da872 100644 --- a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/rest/AnnotatedController.java @@ -13,7 +13,8 @@ public class AnnotatedController { @GetMapping("/flux/one") public Mono one() { - return Mono.just("one"); + return Mono.just("one") + .map(String::toUpperCase); } @GetMapping("/flux/ten") From 9cbcf9ae5f34e01fa005a59488092c3319bb009c Mon Sep 17 00:00:00 2001 From: Yuriy Dvorzhetskiy Date: Sat, 15 May 2021 17:44:56 +0300 Subject: [PATCH 13/25] Fix version and format --- 2021-02/spring-20/spring-20-exercise/pom.xml | 2 +- .../src/main/java/ru/otus/spring/Main.java | 54 +++++++++---------- .../otus/spring/rest/AnnotatedController.java | 14 ++--- .../ru/otus/spring/rest/PersonController.java | 4 +- .../repository/PersonRepositoryTest.java | 7 +-- .../spring/rest/PersonControllerTest.java | 10 ++-- 2021-02/spring-20/spring-20-solution/pom.xml | 2 +- .../src/main/java/ru/otus/spring/Main.java | 2 - .../repository/PersonRepositoryTest.java | 20 +------ .../spring/rest/PersonControllerTest.java | 10 ++-- 10 files changed, 48 insertions(+), 77 deletions(-) diff --git a/2021-02/spring-20/spring-20-exercise/pom.xml b/2021-02/spring-20/spring-20-exercise/pom.xml index 71bbd272..7fb51891 100644 --- a/2021-02/spring-20/spring-20-exercise/pom.xml +++ b/2021-02/spring-20/spring-20-exercise/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.2.4.RELEASE + 2.4.5 diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java index dcecffd5..e6fbbc70 100644 --- a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java @@ -22,35 +22,35 @@ import static org.springframework.web.reactive.function.server.ServerResponse.ok @SpringBootApplication public class Main { - public static void main( String[] args ) { - ApplicationContext context = SpringApplication.run( Main.class ); - PersonRepository repository = context.getBean( PersonRepository.class ); + public static void main(String[] args) { + ApplicationContext context = SpringApplication.run(Main.class); + PersonRepository repository = context.getBean(PersonRepository.class); - repository.saveAll( Arrays.asList( - new Person( "Pushkin", 22 ), - new Person( "Lermontov", 22 ), - new Person( "Tolstoy", 60 ) - ) ).subscribe( p -> System.out.println( p.getLastName() ) ); + repository.saveAll(Arrays.asList( + new Person("Pushkin", 22), + new Person("Lermontov", 22), + new Person("Tolstoy", 60) + )).subscribe(p -> System.out.println(p.getLastName())); } @Bean - public RouterFunction composedRoutes( PersonRepository repository ) { + public RouterFunction composedRoutes(PersonRepository repository) { - PersonHandler handler = new PersonHandler( repository ); + PersonHandler handler = new PersonHandler(repository); RouterFunction route = route() - .GET( "/func/person", accept( APPLICATION_JSON ), handler::list ) - .GET( "/func/person/{id}", accept( APPLICATION_JSON ), - request -> repository.findById( request.pathVariable( "id" ) ) - .flatMap( person -> ok().contentType( APPLICATION_JSON ).body( fromObject( person ) ) ) + .GET("/func/person", accept(APPLICATION_JSON), handler::list) + .GET("/func/person/{id}", accept(APPLICATION_JSON), + request -> repository.findById(request.pathVariable("id")) + .flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromObject(person))) ) - .GET( "/func/person/age/{age}", accept( APPLICATION_JSON ), - serverRequest -> ok().contentType( APPLICATION_JSON ) - .body( repository.findAllByAge( Integer.valueOf( serverRequest.pathVariable( "age" ) ) ), Person.class ) ) - .GET( "/func/person/find", accept( APPLICATION_JSON ), - serverRequest -> ok().contentType( APPLICATION_JSON ) - .body( repository.findAllByAge( Integer.valueOf( serverRequest.queryParam( "age" ).get() ) ), Person.class ) ) + .GET("/func/person/age/{age}", accept(APPLICATION_JSON), + serverRequest -> ok().contentType(APPLICATION_JSON) + .body(repository.findAllByAge(Integer.valueOf(serverRequest.pathVariable("age"))), Person.class)) + .GET("/func/person/find", accept(APPLICATION_JSON), + serverRequest -> ok().contentType(APPLICATION_JSON) + .body(repository.findAllByAge(Integer.valueOf(serverRequest.queryParam("age").get())), Person.class)) .build(); return route; @@ -61,18 +61,18 @@ public class Main { private PersonRepository repository; - PersonHandler( PersonRepository repository ) { + PersonHandler(PersonRepository repository) { this.repository = repository; } - Mono list( ServerRequest request ) { - return ok().contentType( APPLICATION_JSON ).body( repository.findAll(), Person.class ); + Mono list(ServerRequest request) { + return ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class); } - Mono listAge( ServerRequest request ) { - System.out.println( "I'm here" ); - return ok().contentType( APPLICATION_JSON ) - .body( repository.findAllByAge( Integer.valueOf( request.queryParam( "age" ).get() ) ), Person.class ); + Mono listAge(ServerRequest request) { + System.out.println("I'm here"); + return ok().contentType(APPLICATION_JSON) + .body(repository.findAllByAge(Integer.valueOf(request.queryParam("age").get())), Person.class); } } } diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java index 0f1a75b5..fc3bded8 100644 --- a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/AnnotatedController.java @@ -13,21 +13,21 @@ public class AnnotatedController { @GetMapping("/flux/one") public Mono one() { - return Mono.just( "one" ); + return Mono.just("one"); } @GetMapping("/flux/ten") public Flux list() { - return Flux.range( 1, 10 ).delayElements( Duration.ofSeconds( 1 ) ); + return Flux.range(1, 10).delayElements(Duration.ofSeconds(1)); } @GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux stream() { - return Flux.generate( () -> 0, ( state, emitter ) -> { - emitter.next( state ); + return Flux.generate(() -> 0, (state, emitter) -> { + emitter.next(state); return state + 1; - } ) - .delayElements( Duration.ofSeconds( 1L ) ) - .map( i -> "" + i ); + }) + .delayElements(Duration.ofSeconds(1L)) + .map(i -> "" + i); } } diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java index 0ed2451c..c6f2df03 100644 --- a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/rest/PersonController.java @@ -31,7 +31,7 @@ public class PersonController { } @GetMapping("/person/find") - public Flux byName(@RequestParam("name") String name){ - return repository.findAllByLastName( name ); + public Flux byName(@RequestParam("name") String name) { + return repository.findAllByLastName(name); } } diff --git a/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java index 0ecf3ca8..2f373b60 100644 --- a/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java +++ b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java @@ -1,17 +1,14 @@ package ru.otus.spring.repository; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; -import org.springframework.test.context.junit4.SpringRunner; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import ru.otus.spring.domain.Person; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; -@RunWith(SpringRunner.class) @DataMongoTest public class PersonRepositoryTest { diff --git a/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java index b6eb9806..d1504547 100644 --- a/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java +++ b/2021-02/spring-20/spring-20-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -1,19 +1,17 @@ package ru.otus.spring.rest; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; -@RunWith(SpringRunner.class) @SpringBootTest public class PersonControllerTest { @Autowired - private RouterFunction route; + private RouterFunction route; @Test public void testRoute() { @@ -27,6 +25,4 @@ public class PersonControllerTest { .expectStatus() .isOk(); } - - } diff --git a/2021-02/spring-20/spring-20-solution/pom.xml b/2021-02/spring-20/spring-20-solution/pom.xml index 13d66a4f..60a6d129 100644 --- a/2021-02/spring-20/spring-20-solution/pom.xml +++ b/2021-02/spring-20/spring-20-solution/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.2.4.RELEASE + 2.4.5 diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java index 934532de..3a07de5c 100644 --- a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java @@ -6,9 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Mono; import ru.otus.spring.domain.Person; import ru.otus.spring.repository.PersonRepository; diff --git a/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java index 037dc9b7..2f373b60 100644 --- a/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java +++ b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/repository/PersonRepositoryTest.java @@ -1,17 +1,14 @@ package ru.otus.spring.repository; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; -import org.springframework.test.context.junit4.SpringRunner; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import ru.otus.spring.domain.Person; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; -@RunWith(SpringRunner.class) @DataMongoTest public class PersonRepositoryTest { @@ -28,17 +25,4 @@ public class PersonRepositoryTest { .expectComplete() .verify(); } - - @Test - public void shouldFindByAge() { - repository.save(new Person("Pushkin", 18)).block(); - - StepVerifier.create( - repository.findAllByAge(18) - ) - .expectFusion() - .expectNextCount(1) - .expectComplete() - .verify(); - } } diff --git a/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java index b6eb9806..d1504547 100644 --- a/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java +++ b/2021-02/spring-20/spring-20-solution/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -1,19 +1,17 @@ package ru.otus.spring.rest; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; -@RunWith(SpringRunner.class) @SpringBootTest public class PersonControllerTest { @Autowired - private RouterFunction route; + private RouterFunction route; @Test public void testRoute() { @@ -27,6 +25,4 @@ public class PersonControllerTest { .expectStatus() .isOk(); } - - } From 299c36e0bece4b48f6365c8b89329aabbac683d3 Mon Sep 17 00:00:00 2001 From: Yuriy Dvorzhetskiy Date: Sat, 15 May 2021 18:13:43 +0300 Subject: [PATCH 14/25] Fix exercise --- .../src/main/java/ru/otus/spring/Main.java | 39 +++++++----------- .../src/main/java/ru/otus/spring/Main.java | 40 +++++++++++++++---- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java index e6fbbc70..7b3c42bf 100644 --- a/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java +++ b/2021-02/spring-20/spring-20-exercise/src/main/java/ru/otus/spring/Main.java @@ -1,5 +1,6 @@ package ru.otus.spring; +import org.apache.commons.lang3.StringUtils; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @@ -15,9 +16,11 @@ import java.util.Arrays; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RequestPredicates.queryParam; import static org.springframework.web.reactive.function.server.RouterFunctions.route; -import static org.springframework.web.reactive.function.server.ServerResponse.ok; +import static org.springframework.web.reactive.function.server.ServerResponse.*; @SpringBootApplication public class Main { @@ -36,44 +39,30 @@ public class Main { @Bean public RouterFunction composedRoutes(PersonRepository repository) { - - PersonHandler handler = new PersonHandler(repository); - - RouterFunction route = route() - .GET("/func/person", accept(APPLICATION_JSON), handler::list) + return route() + // Обратите внимание на использование хэндлера + .GET("/func/person", accept(APPLICATION_JSON), new PersonHandler(repository)::list) + // Обратите внимание на использование pathVariable .GET("/func/person/{id}", accept(APPLICATION_JSON), request -> repository.findById(request.pathVariable("id")) - .flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromObject(person))) - ) - .GET("/func/person/age/{age}", accept(APPLICATION_JSON), - serverRequest -> ok().contentType(APPLICATION_JSON) - .body(repository.findAllByAge(Integer.valueOf(serverRequest.pathVariable("age"))), Person.class)) - .GET("/func/person/find", accept(APPLICATION_JSON), - serverRequest -> ok().contentType(APPLICATION_JSON) - .body(repository.findAllByAge(Integer.valueOf(serverRequest.queryParam("age").get())), Person.class)) - .build(); - - return route; + .flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromValue(person))) + .switchIfEmpty(notFound().build()) + ).build(); } - + // Это пример хэндлера, который даже не бин static class PersonHandler { - private PersonRepository repository; + private final PersonRepository repository; PersonHandler(PersonRepository repository) { this.repository = repository; } Mono list(ServerRequest request) { + // Обратите внимание на пример другого порядка создания response от Flux return ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class); } - - Mono listAge(ServerRequest request) { - System.out.println("I'm here"); - return ok().contentType(APPLICATION_JSON) - .body(repository.findAllByAge(Integer.valueOf(request.queryParam("age").get())), Person.class); - } } } diff --git a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java index 3a07de5c..8bad8f3d 100644 --- a/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java +++ b/2021-02/spring-20/spring-20-solution/src/main/java/ru/otus/spring/Main.java @@ -6,7 +6,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; import ru.otus.spring.domain.Person; import ru.otus.spring.repository.PersonRepository; @@ -14,11 +16,11 @@ import java.util.Arrays; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; import static org.springframework.web.reactive.function.server.RequestPredicates.queryParam; import static org.springframework.web.reactive.function.server.RouterFunctions.route; -import static org.springframework.web.reactive.function.server.ServerResponse.notFound; -import static org.springframework.web.reactive.function.server.ServerResponse.ok; +import static org.springframework.web.reactive.function.server.ServerResponse.*; @SpringBootApplication public class Main { @@ -32,25 +34,49 @@ public class Main { new Person("Lermontov", 22), new Person("Tolstoy", 60) )).subscribe(p -> System.out.println(p.getLastName())); - } @Bean public RouterFunction composedRoutes(PersonRepository repository) { return route() + // эта функция должна стоять раньше findAll - порядок следования роутов - важен .GET("/func/person", queryParam("name", StringUtils::isNotEmpty), request -> request.queryParam("name") .map(repository::findAllByLastName) .map(persons -> ok().body(persons, Person.class)) - .orElse(notFound().build()) + .orElse(badRequest().build()) ) - .GET("/func/person", accept(APPLICATION_JSON), - request -> ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class)) + // пример другой реализации - начиная с запроса репозитория + .GET("/func/person", queryParam("age", StringUtils::isNotEmpty), + req -> repository.findAllByLastName( + req.queryParam("age").orElseThrow(IllegalArgumentException::new) + ) + .collectList() + .flatMap(persons -> ok().body(persons, Person.class))) + // Обратите внимание на использование хэндлера + .GET("/func/person", accept(APPLICATION_JSON), new PersonHandler(repository)::list) + // Обратите внимание на использование pathVariable .GET("/func/person/{id}", accept(APPLICATION_JSON), request -> repository.findById(request.pathVariable("id")) - .flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromObject(person))) + .flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromValue(person))) + .switchIfEmpty(notFound().build()) ).build(); } + + // Это пример хэндлера, который даже не бин + static class PersonHandler { + + private final PersonRepository repository; + + PersonHandler(PersonRepository repository) { + this.repository = repository; + } + + Mono list(ServerRequest request) { + // Обратите внимание на пример другого порядка создания response от Flux + return ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class); + } + } } From 61d17c7734b57184cad140c561fdf4ec57fa5d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Mon, 17 May 2021 14:12:57 +0400 Subject: [PATCH 15/25] spring-13-data-jpa --- .../{spring-13-exercise => demo}/.gitignore | 0 .../{spring-13-solution => demo}/pom.xml | 2 +- .../main/java/ru/otus/springdata/Main.java | 75 +++++++++++++++++++ .../java/ru/otus/springdata/domain/Email.java | 0 .../ru/otus/springdata/domain/Person.java | 0 .../repository/EmailRepository.java | 0 .../repository/EmailRepositoryCustom.java | 0 .../repository/EmailRepositoryCustomImpl.java | 0 .../repository/PersonRepository.java | 20 +++++ .../repository/PersonSpecification.java | 21 ++++++ .../src/main/resources/application.yml | 0 .../.gitignore | 0 .../{spring-13-exercise => exercise}/pom.xml | 2 +- .../main/java/ru/otus/springdata/Main.java | 7 +- .../java/ru/otus/springdata/domain/Email.java | 0 .../ru/otus/springdata/domain/Person.java | 0 .../repository/EmailRepository.java | 0 .../repository/PersonRepository.java | 0 .../src/main/resources/application.yml | 0 2021-03/spring-13-data-jpa/pom.xml | 8 +- .../spring-13-data-jpa/solution-01/.gitignore | 4 + .../spring-13-data-jpa/solution-01/pom.xml | 55 ++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 37 +++++++++ .../java/ru/otus/springdata/domain/Email.java | 24 ++++++ .../ru/otus/springdata/domain/Person.java | 25 +++++++ .../repository/EmailRepository.java | 12 +++ .../repository/PersonRepository.java | 15 ++++ .../src/main/resources/application.yml | 20 +++++ .../spring-13-data-jpa/solution-02/.gitignore | 4 + .../spring-13-data-jpa/solution-02/pom.xml | 55 ++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 53 +++++++++++++ .../java/ru/otus/springdata/domain/Email.java | 27 +++++++ .../ru/otus/springdata/domain/Person.java | 25 +++++++ .../repository/EmailRepository.java | 13 ++++ .../repository/PersonRepository.java | 14 ++++ .../src/main/resources/application.yml | 20 +++++ .../spring-13-data-jpa/solution-03/.gitignore | 4 + .../spring-13-data-jpa/solution-03/pom.xml | 55 ++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 55 ++++++++++++++ .../java/ru/otus/springdata/domain/Email.java | 27 +++++++ .../ru/otus/springdata/domain/Person.java | 30 ++++++++ .../repository/EmailRepository.java | 13 ++++ .../repository/PersonRepository.java | 0 .../src/main/resources/application.yml | 20 +++++ .../spring-13-data-jpa/solution-04/.gitignore | 4 + .../spring-13-data-jpa/solution-04/pom.xml | 55 ++++++++++++++ .../main/java/ru/otus/springdata/Main.java | 64 ++++++++++++++++ .../java/ru/otus/springdata/domain/Email.java | 27 +++++++ .../ru/otus/springdata/domain/Person.java | 30 ++++++++ .../repository/EmailRepository.java | 21 ++++++ .../repository/PersonRepository.java | 18 +++++ .../src/main/resources/application.yml | 20 +++++ .../main/java/ru/otus/springdata/Main.java | 67 ----------------- .../ioservice/example/poll/PollService.java | 1 - .../src/main/resources/application.yml | 2 +- 55 files changed, 975 insertions(+), 76 deletions(-) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => demo}/.gitignore (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => demo}/pom.xml (97%) create mode 100644 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/Main.java rename 2021-03/spring-13-data-jpa/{spring-13-solution => demo}/src/main/java/ru/otus/springdata/domain/Email.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => demo}/src/main/java/ru/otus/springdata/domain/Person.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => demo}/src/main/java/ru/otus/springdata/repository/EmailRepository.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => demo}/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => demo}/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java (100%) create mode 100644 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonRepository.java create mode 100644 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonSpecification.java rename 2021-03/spring-13-data-jpa/{spring-13-exercise => demo}/src/main/resources/application.yml (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => exercise}/.gitignore (100%) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => exercise}/pom.xml (97%) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => exercise}/src/main/java/ru/otus/springdata/Main.java (66%) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => exercise}/src/main/java/ru/otus/springdata/domain/Email.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => exercise}/src/main/java/ru/otus/springdata/domain/Person.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => exercise}/src/main/java/ru/otus/springdata/repository/EmailRepository.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-exercise => exercise}/src/main/java/ru/otus/springdata/repository/PersonRepository.java (100%) rename 2021-03/spring-13-data-jpa/{spring-13-solution => exercise}/src/main/resources/application.yml (100%) create mode 100644 2021-03/spring-13-data-jpa/solution-01/.gitignore create mode 100644 2021-03/spring-13-data-jpa/solution-01/pom.xml create mode 100644 2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/Main.java create mode 100644 2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Email.java create mode 100644 2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Person.java create mode 100644 2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/EmailRepository.java create mode 100644 2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/PersonRepository.java create mode 100644 2021-03/spring-13-data-jpa/solution-01/src/main/resources/application.yml create mode 100644 2021-03/spring-13-data-jpa/solution-02/.gitignore create mode 100644 2021-03/spring-13-data-jpa/solution-02/pom.xml create mode 100644 2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/Main.java create mode 100644 2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Email.java create mode 100644 2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Person.java create mode 100644 2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/EmailRepository.java create mode 100644 2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/PersonRepository.java create mode 100644 2021-03/spring-13-data-jpa/solution-02/src/main/resources/application.yml create mode 100644 2021-03/spring-13-data-jpa/solution-03/.gitignore create mode 100644 2021-03/spring-13-data-jpa/solution-03/pom.xml create mode 100644 2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/Main.java create mode 100644 2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Email.java create mode 100644 2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Person.java create mode 100644 2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/repository/EmailRepository.java rename 2021-03/spring-13-data-jpa/{spring-13-solution => solution-03}/src/main/java/ru/otus/springdata/repository/PersonRepository.java (100%) create mode 100644 2021-03/spring-13-data-jpa/solution-03/src/main/resources/application.yml create mode 100644 2021-03/spring-13-data-jpa/solution-04/.gitignore create mode 100644 2021-03/spring-13-data-jpa/solution-04/pom.xml create mode 100644 2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/Main.java create mode 100644 2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Email.java create mode 100644 2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Person.java create mode 100644 2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/EmailRepository.java create mode 100644 2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/PersonRepository.java create mode 100644 2021-03/spring-13-data-jpa/solution-04/src/main/resources/application.yml delete mode 100644 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/.gitignore b/2021-03/spring-13-data-jpa/demo/.gitignore similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/.gitignore rename to 2021-03/spring-13-data-jpa/demo/.gitignore diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/pom.xml b/2021-03/spring-13-data-jpa/demo/pom.xml similarity index 97% rename from 2021-03/spring-13-data-jpa/spring-13-solution/pom.xml rename to 2021-03/spring-13-data-jpa/demo/pom.xml index 5bc2c71d..48523ba6 100644 --- a/2021-03/spring-13-data-jpa/spring-13-solution/pom.xml +++ b/2021-03/spring-13-data-jpa/demo/pom.xml @@ -5,7 +5,7 @@ 4.0.0 ru.otus - spring-13-solution + demo 1.0-SNAPSHOT diff --git a/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..bd056210 --- /dev/null +++ b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,75 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.jpa.domain.Specification; +import ru.otus.springdata.domain.Email; +import ru.otus.springdata.domain.Person; +import ru.otus.springdata.repository.EmailRepository; +import ru.otus.springdata.repository.PersonRepository; + +import java.util.Objects; +import java.util.stream.Collectors; + +import static ru.otus.springdata.repository.PersonSpecification.emailAddressLike; +import static ru.otus.springdata.repository.PersonSpecification.nameLike; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + + PersonRepository personRepository = context.getBean(PersonRepository.class); + EmailRepository emailRepository = context.getBean(EmailRepository.class); + + var pushkin = new Person("Александр Сергеевич Пушкин", new Email("alex.pushkin@mail.ru")); + var block = new Person("Александр Александрович Блок", new Email("alex.block@mail.ru")); + var lermontov = new Person("Михаил Юрьевич Лермонтов", new Email("michail.lermontov@bk.ru")); + var gorbachev = new Person("Михаил Сергеевич Горбачев", new Email("gorbachev@mail.ru")); + var bulgakov = new Person("Михаил Афанасьевич Булгаков", new Email("bulgakov@mail.ru")); + + emailRepository.save(pushkin.getEmail()); + emailRepository.save(block.getEmail()); + emailRepository.save(lermontov.getEmail()); + emailRepository.save(gorbachev.getEmail()); + emailRepository.save(bulgakov.getEmail()); + + personRepository.save(pushkin); + personRepository.save(block); + personRepository.save(lermontov); + personRepository.save(gorbachev); + personRepository.save(bulgakov); + + System.out.println("\n\nИщем почту Горбачева по его id"); + emailRepository.findByPersonId(gorbachev.getId()) + .ifPresent(System.out::println); + + + System.out.println("\n\nС помощью Example ищем всех пёрсонов с именем \"Михаил\" и почтой на \"mail.ru\""); + ExampleMatcher ignoringExampleMatcher = ExampleMatcher.matchingAll() + .withMatcher("email.address", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()) + .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()) + .withIgnorePaths("id", "email.id"); + + Example example = Example.of(new Person("Михаил", new Email(0, "mail.ru")), ignoringExampleMatcher); + + System.out.println(personRepository.findAll(example).stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + + System.out.println("\n\nС помощью Specification ищем всех пёрсонов с именем \"Александр\" или с почтой на \"bk.ru\""); + + Specification specification = Specification.where(nameLike("Александр")) + .or(emailAddressLike("bk.ru")); + + System.out.println(personRepository.findAll(specification).stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\n"); + + } +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/domain/Email.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Email.java rename to 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/domain/Email.java diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/domain/Person.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/domain/Person.java rename to 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/domain/Person.java diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/EmailRepository.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepository.java rename to 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/EmailRepository.java diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java rename to 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustom.java diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java rename to 2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/EmailRepositoryCustomImpl.java diff --git a/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonRepository.java new file mode 100644 index 00000000..7bb5c825 --- /dev/null +++ b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -0,0 +1,20 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.repository.CrudRepository; +import ru.otus.springdata.domain.Person; + +import java.util.List; +import java.util.Optional; + +public interface PersonRepository extends JpaRepository, JpaSpecificationExecutor { + + @EntityGraph(attributePaths = "email") + List findAll(); + + Optional findByName(String s); + + Optional findByEmailAddress(String email); +} diff --git a/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonSpecification.java b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonSpecification.java new file mode 100644 index 00000000..813f13b8 --- /dev/null +++ b/2021-03/spring-13-data-jpa/demo/src/main/java/ru/otus/springdata/repository/PersonSpecification.java @@ -0,0 +1,21 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.domain.Specification; +import ru.otus.springdata.domain.Person; + +public class PersonSpecification { + + public static Specification nameLike(String name) { + if (name == null) { + return null; + } + return (root, query, cb) -> cb.like(root.get("name"), "%" + name + "%"); + } + + public static Specification emailAddressLike(String address) { + if (address == null) { + return null; + } + return (root, query, cb) -> cb.like(root.join("email").get("address"), "%" + address + "%"); + } +} diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/demo/src/main/resources/application.yml similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/resources/application.yml rename to 2021-03/spring-13-data-jpa/demo/src/main/resources/application.yml diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/.gitignore b/2021-03/spring-13-data-jpa/exercise/.gitignore similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/.gitignore rename to 2021-03/spring-13-data-jpa/exercise/.gitignore diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml b/2021-03/spring-13-data-jpa/exercise/pom.xml similarity index 97% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml rename to 2021-03/spring-13-data-jpa/exercise/pom.xml index 5f36b7ef..172b613d 100644 --- a/2021-03/spring-13-data-jpa/spring-13-exercise/pom.xml +++ b/2021-03/spring-13-data-jpa/exercise/pom.xml @@ -5,7 +5,7 @@ 4.0.0 ru.otus - spring-13-exercise + exercise 1.0-SNAPSHOT diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/Main.java similarity index 66% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java rename to 2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/Main.java index 59a032bd..dbbe3598 100644 --- a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/Main.java +++ b/2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/Main.java @@ -3,7 +3,7 @@ package ru.otus.springdata; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; -//import ru.otus.spring10.repostory.PersonRepository; + @SpringBootApplication @@ -14,8 +14,9 @@ public class Main { //PersonRepository personRepository = context.getBean(PersonRepository.class); //EmailRepository emailRepository = context.getBean(EmailRepository.class); - // personRepository.save(new Person("Pushkin")); - // personRepository.save(new Person("Lermontov")); + // personRepository.save(new Person("Александр Сергеевич Пушкин")); + // personRepository.save(new Person("Михаил Юрьевич Лермонтов")); + // personRepository.save(new Person("Михаил Сергеевич Горбачев")); } diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/domain/Email.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Email.java rename to 2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/domain/Email.java diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/domain/Person.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/domain/Person.java rename to 2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/domain/Person.java diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java rename to 2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/repository/EmailRepository.java diff --git a/2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java rename to 2021-03/spring-13-data-jpa/exercise/src/main/java/ru/otus/springdata/repository/PersonRepository.java diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/exercise/src/main/resources/application.yml similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/resources/application.yml rename to 2021-03/spring-13-data-jpa/exercise/src/main/resources/application.yml diff --git a/2021-03/spring-13-data-jpa/pom.xml b/2021-03/spring-13-data-jpa/pom.xml index da37d94f..bc5b4fae 100644 --- a/2021-03/spring-13-data-jpa/pom.xml +++ b/2021-03/spring-13-data-jpa/pom.xml @@ -11,7 +11,11 @@ pom - spring-13-exercise - spring-13-solution + exercise + solution-01 + solution-02 + solution-03 + solution-04 + demo diff --git a/2021-03/spring-13-data-jpa/solution-01/.gitignore b/2021-03/spring-13-data-jpa/solution-01/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/solution-01/pom.xml b/2021-03/spring-13-data-jpa/solution-01/pom.xml new file mode 100644 index 00000000..355f4335 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + solution-01 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..4961485f --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,37 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import ru.otus.springdata.domain.Person; +import ru.otus.springdata.repository.PersonRepository; + +import java.util.Objects; +import java.util.stream.Collectors; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + + PersonRepository personRepository = context.getBean(PersonRepository.class); + + + personRepository.save(new Person("Александр Сергеевич Пушкин")); + personRepository.save(new Person("Михаил Юрьевич Лермонтов")); + personRepository.save(new Person("Михаил Сергеевич Горбачев")); + + System.out.println("\n\nИщем всех пёрсонов"); + System.out.println(personRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\nИщем Пушкина"); + personRepository.findByName("Александр Сергеевич Пушкин") + .ifPresent(System.out::println); + + + System.out.println("\n\n"); + + } +} diff --git a/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Email.java new file mode 100644 index 00000000..3f0bd00c --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Email.java @@ -0,0 +1,24 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Email { + + private long id; + + private String address; + + public Email(String address) { + this.address = address; + } +} diff --git a/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Person.java new file mode 100644 index 00000000..96d92b91 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/domain/Person.java @@ -0,0 +1,25 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + public Person(String name) { + this.name = name; + } + +} diff --git a/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/EmailRepository.java new file mode 100644 index 00000000..788feda5 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -0,0 +1,12 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.otus.springdata.domain.Email; + +import java.util.List; + +public interface EmailRepository extends JpaRepository { + + @Override + List findAll(); +} diff --git a/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/PersonRepository.java new file mode 100644 index 00000000..012ded26 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -0,0 +1,15 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.springdata.domain.Person; + +import java.util.List; +import java.util.Optional; + +public interface PersonRepository extends CrudRepository { + + @Override + List findAll(); + + Optional findByName(String s); +} diff --git a/2021-03/spring-13-data-jpa/solution-01/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/solution-01/src/main/resources/application.yml new file mode 100644 index 00000000..bd5b0f98 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-01/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: false + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-13-data-jpa/solution-02/.gitignore b/2021-03/spring-13-data-jpa/solution-02/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/solution-02/pom.xml b/2021-03/spring-13-data-jpa/solution-02/pom.xml new file mode 100644 index 00000000..c6dd4ab8 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + solution-02 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..d25a29d2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,53 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import ru.otus.springdata.domain.Email; +import ru.otus.springdata.domain.Person; +import ru.otus.springdata.repository.EmailRepository; +import ru.otus.springdata.repository.PersonRepository; + +import java.util.Objects; +import java.util.stream.Collectors; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + PersonRepository personRepository = context.getBean(PersonRepository.class); + EmailRepository emailRepository = context.getBean(EmailRepository.class); + + var pushkinEmail = new Email("alex.pushkin@mail.ru"); + var lermontovEmail = new Email("michail.lermontov@mail.ru"); + var gorbachevEmail = new Email("gorbachev@mail.ru"); + + var pushkin = new Person("Александр Сергеевич Пушкин"); + var lermontov = new Person("Михаил Юрьевич Лермонтов"); + var gorbachev = new Person("Михаил Сергеевич Горбачев"); + + emailRepository.save(pushkinEmail); + emailRepository.save(lermontovEmail); + emailRepository.save(gorbachevEmail); + + personRepository.save(pushkin); + personRepository.save(lermontov); + personRepository.save(gorbachev); + + System.out.println("\n\nИщем всех пёрсонов"); + System.out.println(personRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\nИщем Пушкина"); + personRepository.findByName("Александр Сергеевич Пушкин") + .ifPresent(System.out::println); + + System.out.println("\n\nИщем все почты"); + System.out.println(emailRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\n"); + } +} diff --git a/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Email.java new file mode 100644 index 00000000..e3b8d344 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Email.java @@ -0,0 +1,27 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Email { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String address; + + public Email(String address) { + this.address = address; + } +} diff --git a/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Person.java new file mode 100644 index 00000000..96d92b91 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/domain/Person.java @@ -0,0 +1,25 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + public Person(String name) { + this.name = name; + } + +} diff --git a/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/EmailRepository.java new file mode 100644 index 00000000..3d5c3152 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -0,0 +1,13 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.springdata.domain.Email; + +import java.util.Optional; + +public interface EmailRepository extends JpaRepository{ +} diff --git a/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/PersonRepository.java new file mode 100644 index 00000000..cd5bb3f0 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -0,0 +1,14 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.springdata.domain.Person; + +import java.util.List; +import java.util.Optional; + +public interface PersonRepository extends CrudRepository { + + List findAll(); + + Optional findByName(String s); +} diff --git a/2021-03/spring-13-data-jpa/solution-02/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/solution-02/src/main/resources/application.yml new file mode 100644 index 00000000..bd5b0f98 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-02/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: false + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-13-data-jpa/solution-03/.gitignore b/2021-03/spring-13-data-jpa/solution-03/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/solution-03/pom.xml b/2021-03/spring-13-data-jpa/solution-03/pom.xml new file mode 100644 index 00000000..4fff22c3 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + solution-03 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..a02e3e25 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,55 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import ru.otus.springdata.domain.Email; +import ru.otus.springdata.domain.Person; +import ru.otus.springdata.repository.EmailRepository; +import ru.otus.springdata.repository.PersonRepository; + +import java.util.Objects; +import java.util.stream.Collectors; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + + PersonRepository personRepository = context.getBean(PersonRepository.class); + EmailRepository emailRepository = context.getBean(EmailRepository.class); + + var pushkin = new Person("Александр Сергеевич Пушкин", new Email("alex.pushkin@mail.ru")); + var lermontov = new Person("Михаил Юрьевич Лермонтов", new Email("michail.lermontov@mail.ru")); + var gorbachev = new Person("Михаил Сергеевич Горбачев", new Email("gorbachev@mail.ru")); + + emailRepository.save(pushkin.getEmail()); + emailRepository.save(lermontov.getEmail()); + emailRepository.save(gorbachev.getEmail()); + + personRepository.save(pushkin); + personRepository.save(lermontov); + personRepository.save(gorbachev); + + System.out.println("\n\nИщем всех пёрсонов"); + System.out.println(personRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\nИщем Пушкина"); + personRepository.findByName("Александр Сергеевич Пушкин") + .ifPresent(System.out::println); + + System.out.println("\n\nИщем все почты"); + System.out.println(emailRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\nИщем Пушкина по его почте"); + personRepository.findByEmailAddress("alex.pushkin@mail.ru") + .ifPresent(System.out::println); + + System.out.println("\n\n"); + } +} diff --git a/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Email.java new file mode 100644 index 00000000..e3b8d344 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Email.java @@ -0,0 +1,27 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Email { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String address; + + public Email(String address) { + this.address = address; + } +} diff --git a/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Person.java new file mode 100644 index 00000000..e24e42aa --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/domain/Person.java @@ -0,0 +1,30 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + @OneToOne(orphanRemoval = true) + @JoinColumn(name = "email_id") + private Email email; + + public Person(String name, Email email) { + this.name = name; + this.email = email; + } + +} diff --git a/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/repository/EmailRepository.java new file mode 100644 index 00000000..16f1d0c9 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -0,0 +1,13 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.springdata.domain.Email; + +import java.util.Optional; + +public interface EmailRepository extends JpaRepository { +} diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/repository/PersonRepository.java similarity index 100% rename from 2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/repository/PersonRepository.java rename to 2021-03/spring-13-data-jpa/solution-03/src/main/java/ru/otus/springdata/repository/PersonRepository.java diff --git a/2021-03/spring-13-data-jpa/solution-03/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/solution-03/src/main/resources/application.yml new file mode 100644 index 00000000..bd5b0f98 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-03/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: false + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-13-data-jpa/solution-04/.gitignore b/2021-03/spring-13-data-jpa/solution-04/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2021-03/spring-13-data-jpa/solution-04/pom.xml b/2021-03/spring-13-data-jpa/solution-04/pom.xml new file mode 100644 index 00000000..6f0a221c --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + ru.otus + solution-04 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/Main.java new file mode 100644 index 00000000..4574b783 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/Main.java @@ -0,0 +1,64 @@ +package ru.otus.springdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import ru.otus.springdata.domain.Email; +import ru.otus.springdata.domain.Person; +import ru.otus.springdata.repository.EmailRepository; +import ru.otus.springdata.repository.PersonRepository; + +import java.util.Objects; +import java.util.stream.Collectors; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Main.class); + + PersonRepository personRepository = context.getBean(PersonRepository.class); + EmailRepository emailRepository = context.getBean(EmailRepository.class); + + var pushkin = new Person("Александр Сергеевич Пушкин", new Email("alex.pushkin@mail.ru")); + var lermontov = new Person("Михаил Юрьевич Лермонтов", new Email("michail.lermontov@mail.ru")); + var gorbachev = new Person("Михаил Сергеевич Горбачев", new Email("gorbachev@mail.ru")); + + emailRepository.save(pushkin.getEmail()); + emailRepository.save(lermontov.getEmail()); + emailRepository.save(gorbachev.getEmail()); + + personRepository.save(pushkin); + personRepository.save(lermontov); + personRepository.save(gorbachev); + + System.out.println("\n\nИщем всех пёрсонов"); + System.out.println(personRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\nИщем Пушкина"); + personRepository.findByName("Александр Сергеевич Пушкин") + .ifPresent(System.out::println); + + System.out.println("\n\nИщем все почты"); + System.out.println(emailRepository.findAll().stream().map(Objects::toString) + .collect(Collectors.joining("\n"))); + + System.out.println("\n\nИщем Пушкина по его почте"); + personRepository.findByEmailAddress("alex.pushkin@mail.ru") + .ifPresent(System.out::println); + + System.out.println("\n\nОбновляем почту Лермонтову"); + System.out.println("До обновления: " + lermontov.getEmail()); + emailRepository.updateEmailById(lermontov.getId(), "michail1984@lermontov.ru"); + + System.out.println("\n\nИщем почту Лермонтова по новому адресу"); + emailRepository.findByEmailAddress("michail1984@lermontov.ru") + .ifPresent(e -> System.out.println("После обновления: " + e)); + + System.out.println("\n\n"); + + } +} diff --git a/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Email.java b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Email.java new file mode 100644 index 00000000..e3b8d344 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Email.java @@ -0,0 +1,27 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Email { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String address; + + public Email(String address) { + this.address = address; + } +} diff --git a/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Person.java b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Person.java new file mode 100644 index 00000000..e24e42aa --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/domain/Person.java @@ -0,0 +1,30 @@ +package ru.otus.springdata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + @OneToOne(orphanRemoval = true) + @JoinColumn(name = "email_id") + private Email email; + + public Person(String name, Email email) { + this.name = name; + this.email = email; + } + +} diff --git a/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/EmailRepository.java b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/EmailRepository.java new file mode 100644 index 00000000..66b4da80 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/EmailRepository.java @@ -0,0 +1,21 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import ru.otus.springdata.domain.Email; + +import java.util.Optional; + +public interface EmailRepository extends JpaRepository { + + @Query("select e from Email e where e.address = :address") + Optional findByEmailAddress(@Param("address") String email); + + @Modifying + @Transactional + @Query("update Email e set e.address = :address where e.id = :id") + void updateEmailById(@Param("id") long id, @Param("address") String address); +} diff --git a/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/PersonRepository.java b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/PersonRepository.java new file mode 100644 index 00000000..a6aa8dd3 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/src/main/java/ru/otus/springdata/repository/PersonRepository.java @@ -0,0 +1,18 @@ +package ru.otus.springdata.repository; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.repository.CrudRepository; +import ru.otus.springdata.domain.Person; + +import java.util.List; +import java.util.Optional; + +public interface PersonRepository extends CrudRepository { + + @EntityGraph(attributePaths = "email") + List findAll(); + + Optional findByName(String s); + + Optional findByEmailAddress(String email); +} diff --git a/2021-03/spring-13-data-jpa/solution-04/src/main/resources/application.yml b/2021-03/spring-13-data-jpa/solution-04/src/main/resources/application.yml new file mode 100644 index 00000000..bd5b0f98 --- /dev/null +++ b/2021-03/spring-13-data-jpa/solution-04/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + initialization-mode: never + + jpa: + generate-ddl: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: false + + show-sql: true + + +logging: + level: + ROOT: ERROR \ No newline at end of file diff --git a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java b/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java deleted file mode 100644 index 912a77cc..00000000 --- a/2021-03/spring-13-data-jpa/spring-13-solution/src/main/java/ru/otus/springdata/Main.java +++ /dev/null @@ -1,67 +0,0 @@ -package ru.otus.springdata; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher; -import ru.otus.springdata.domain.Email; -import ru.otus.springdata.domain.Person; -import ru.otus.springdata.repository.EmailRepository; -import ru.otus.springdata.repository.PersonRepository; - -@SpringBootApplication -public class Main { - - public static void main(String[] args) { - ConfigurableApplicationContext context = SpringApplication.run(Main.class); - PersonRepository personRepository = context.getBean(PersonRepository.class); - EmailRepository emailRepository = context.getBean(EmailRepository.class); - - Email emailP = new Email("alex@pushkin.ru"); - emailP = emailRepository.save(emailP); - - Email emailL = new Email("michail@lermontov.ru"); - emailL = emailRepository.save(emailL); - - Person pushkin = personRepository.save(new Person("Pushkin", emailP)); - Person lermontov = personRepository.save(new Person("Lermontov", emailL)); - - - System.out.println("\n\nИщем всех пёрсонов"); - System.out.println(personRepository.findAll()); - - System.out.println("\n\nИщем Пушкина"); - personRepository.findByName("Pushkin").ifPresent(System.out::println); - - System.out.println("\n\nИщем все почты"); - System.out.println(emailRepository.findAll()); - - System.out.println("\n\nИщем Пушкина по его почте"); - personRepository.findByEmailAddress("alex@pushkin.ru").ifPresent(System.out::println); - - System.out.println("\n\nИщем почту Пушкина по ее адресу"); - emailRepository.findByEmailAddress("alex@pushkin.ru").ifPresent(System.out::println); - - System.out.println("\n\nИщем почту Лермонтова по его (Лермонтова) id"); - emailRepository.findByPersonId(lermontov.getId()).ifPresent(System.out::println); - - System.out.println("\n\nОбновляем почту Лермонтову"); - System.out.println("До обновления: " + emailL); - emailRepository.updateEmailById(emailL.getId(), "michail1984@lermontov.ru"); - emailRepository.findById(emailL.getId()).ifPresent(e -> System.out.println("После обновления: " + e)); - - - System.out.println("\n\nИщем все почты по вхождению \".ru\""); - ExampleMatcher ignoringExampleMatcher = ExampleMatcher.matchingAny() - .withMatcher("address", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()) - .withIgnorePaths("id"); - - Example example = Example.of(new Email(1, ".ru"), ignoringExampleMatcher); - - System.out.println(emailRepository.findAll(example)); - - System.out.println("\n\n"); - - } -} diff --git a/examples/ioservice-replacing-example/src/main/java/ru/otus/ioservice/example/poll/PollService.java b/examples/ioservice-replacing-example/src/main/java/ru/otus/ioservice/example/poll/PollService.java index 169885ea..2818f8b8 100644 --- a/examples/ioservice-replacing-example/src/main/java/ru/otus/ioservice/example/poll/PollService.java +++ b/examples/ioservice-replacing-example/src/main/java/ru/otus/ioservice/example/poll/PollService.java @@ -25,5 +25,4 @@ public class PollService { } ioService.out(String.format("До свидания %s", name)); } - } diff --git a/examples/ioservice-replacing-example/src/main/resources/application.yml b/examples/ioservice-replacing-example/src/main/resources/application.yml index efc3321a..f41c8e93 100644 --- a/examples/ioservice-replacing-example/src/main/resources/application.yml +++ b/examples/ioservice-replacing-example/src/main/resources/application.yml @@ -1 +1 @@ -use.swing: false \ No newline at end of file +use.swing: true \ No newline at end of file From 5402736e76cd45ff47d3eeaf8dfc03685cc9841a Mon Sep 17 00:00:00 2001 From: kataus Date: Sat, 22 May 2021 10:47:09 +0300 Subject: [PATCH 16/25] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=BA=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=BD=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE=20SPring=20?= =?UTF-8?q?Security?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2021-02/spring-22/pom.xml | 53 +++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 12 +++ .../ru/otus/spring/rest/PagesController.java | 32 ++++++++ .../security/SecurityConfiguration.java | 78 +++++++++++++++++++ .../src/main/resources/application.yml | 0 .../resources/templates/authenticated.html | 9 +++ .../src/main/resources/templates/index.html | 11 +++ .../src/main/resources/templates/public.html | 9 +++ .../src/main/resources/templates/success.html | 10 +++ 9 files changed, 214 insertions(+) create mode 100644 2021-02/spring-22/pom.xml create mode 100644 2021-02/spring-22/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-22/src/main/java/ru/otus/spring/rest/PagesController.java create mode 100644 2021-02/spring-22/src/main/java/ru/otus/spring/security/SecurityConfiguration.java create mode 100644 2021-02/spring-22/src/main/resources/application.yml create mode 100644 2021-02/spring-22/src/main/resources/templates/authenticated.html create mode 100644 2021-02/spring-22/src/main/resources/templates/index.html create mode 100644 2021-02/spring-22/src/main/resources/templates/public.html create mode 100644 2021-02/spring-22/src/main/resources/templates/success.html diff --git a/2021-02/spring-22/pom.xml b/2021-02/spring-22/pom.xml new file mode 100644 index 00000000..69a0b0b4 --- /dev/null +++ b/2021-02/spring-22/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + ru.otus + spring-framework-22-spring-security-start + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.3.RELEASE + + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-22/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-22/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..5406a277 --- /dev/null +++ b/2021-02/spring-22/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,12 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + SpringApplication.run(Main.class); + } +} diff --git a/2021-02/spring-22/src/main/java/ru/otus/spring/rest/PagesController.java b/2021-02/spring-22/src/main/java/ru/otus/spring/rest/PagesController.java new file mode 100644 index 00000000..1bdf754e --- /dev/null +++ b/2021-02/spring-22/src/main/java/ru/otus/spring/rest/PagesController.java @@ -0,0 +1,32 @@ +package ru.otus.spring.rest; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class PagesController { + + @GetMapping("/") + public String indexPage() { + return "index"; + } + + @GetMapping("/public") + public String publicPage() { + return "public"; + } + + @GetMapping("/authenticated") + public String authenticatedPage() { + return "authenticated"; + } + + @GetMapping("/success") + public String successPage(){ + return "success"; + } +} diff --git a/2021-02/spring-22/src/main/java/ru/otus/spring/security/SecurityConfiguration.java b/2021-02/spring-22/src/main/java/ru/otus/spring/security/SecurityConfiguration.java new file mode 100644 index 00000000..cd5b072b --- /dev/null +++ b/2021-02/spring-22/src/main/java/ru/otus/spring/security/SecurityConfiguration.java @@ -0,0 +1,78 @@ +package ru.otus.spring.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; + +import java.util.Collection; + +@EnableWebSecurity +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) { + web.ignoring() + .antMatchers("/") + .antMatchers( "/static/**" ); + } + + @Override + public void configure(HttpSecurity http) throws Exception { + http.csrf().disable() + // По умолчанию SecurityContext хранится в сессии. Эта часть вырубает и каждый запросом приходитТ +// .sessionManagement() +// .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) +// .and() + .authorizeRequests() + .antMatchers("/public/").anonymous() + .and() + .authorizeRequests() + .antMatchers("/authenticated").authenticated() +// .and() +// .authorizeRequests().antMatchers("/public").authenticated() + .and() + .httpBasic() + .and() + .anonymous() + .principal( "anonymous" ) + .and() + .rememberMe().key( "Some secret" ) + ; + } + + @Bean + public PasswordEncoder passwordEncoder() { +// return new BCryptPasswordEncoder(10); + return NoOpPasswordEncoder.getInstance(); +// return new PasswordEncoder() { +// @Override +// public String encode(CharSequence charSequence) { +// return charSequence.toString(); +// } +// +// @Override +// public boolean matches(CharSequence charSequence, String s) { +// return charSequence.toString().equals(s); +// } +// }; + } + + @Autowired + public void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication() + .withUser("admin").password("password").roles("ADMIN") + ; + } +} diff --git a/2021-02/spring-22/src/main/resources/application.yml b/2021-02/spring-22/src/main/resources/application.yml new file mode 100644 index 00000000..e69de29b diff --git a/2021-02/spring-22/src/main/resources/templates/authenticated.html b/2021-02/spring-22/src/main/resources/templates/authenticated.html new file mode 100644 index 00000000..9f8b0d7e --- /dev/null +++ b/2021-02/spring-22/src/main/resources/templates/authenticated.html @@ -0,0 +1,9 @@ + + + + + + +Только для авторизованных + + diff --git a/2021-02/spring-22/src/main/resources/templates/index.html b/2021-02/spring-22/src/main/resources/templates/index.html new file mode 100644 index 00000000..f4d11090 --- /dev/null +++ b/2021-02/spring-22/src/main/resources/templates/index.html @@ -0,0 +1,11 @@ + + + + + + +/public +
+/authenticated + + diff --git a/2021-02/spring-22/src/main/resources/templates/public.html b/2021-02/spring-22/src/main/resources/templates/public.html new file mode 100644 index 00000000..77188469 --- /dev/null +++ b/2021-02/spring-22/src/main/resources/templates/public.html @@ -0,0 +1,9 @@ + + + + + + +Доступен всем + + diff --git a/2021-02/spring-22/src/main/resources/templates/success.html b/2021-02/spring-22/src/main/resources/templates/success.html new file mode 100644 index 00000000..89db5f22 --- /dev/null +++ b/2021-02/spring-22/src/main/resources/templates/success.html @@ -0,0 +1,10 @@ + + + + + Вы успешно вошли + + +Вы успешно вошли + + From 90665acc51febba0d5134d62393484e73d8b81b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Mon, 24 May 2021 00:17:24 +0400 Subject: [PATCH 17/25] 2021-03 spring-15-mvc added --- 2021-03/spring-15-mvc/.gitignore | 24 ++++ 2021-03/spring-15-mvc/pom.xml | 20 +++ 2021-03/spring-15-mvc/requests.http | 11 ++ .../spring-mvc-exercise/.gitignore | 24 ++++ .../spring-15-mvc/spring-mvc-exercise/pom.xml | 54 ++++++++ .../src/main/java/ru/otus/spring/Main.java | 28 ++++ .../java/ru/otus/spring/domain/Email.java | 20 +++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++ .../spring/repostory/PersonRepository.java | 11 ++ .../otus/spring/rest/NotFoundException.java | 7 + .../ru/otus/spring/rest/PersonController.java | 13 ++ .../java/ru/otus/spring/rest/PersonDto.java | 54 ++++++++ .../spring/rest/PersonControllerTest.java | 84 ++++++++++++ .../spring-mvc-solution-1/.gitignore | 24 ++++ .../spring-mvc-solution-1/pom.xml | 52 ++++++++ .../src/main/java/ru/otus/spring/Main.java | 28 ++++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++ .../spring/repostory/PersonRepository.java | 11 ++ .../ru/otus/spring/rest/ErrorHandler.java | 4 + .../otus/spring/rest/NotFoundException.java | 7 + .../ru/otus/spring/rest/PersonController.java | 24 ++++ .../java/ru/otus/spring/rest/PersonDto.java | 54 ++++++++ .../spring/rest/PersonControllerTest.java | 84 ++++++++++++ .../spring-mvc-solution-2/.gitignore | 24 ++++ .../spring-mvc-solution-2/pom.xml | 53 ++++++++ .../src/main/java/ru/otus/spring/Main.java | 28 ++++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++ .../spring/repostory/PersonRepository.java | 11 ++ .../ru/otus/spring/rest/ErrorHandler.java | 2 + .../otus/spring/rest/NotFoundException.java | 7 + .../ru/otus/spring/rest/PersonController.java | 38 ++++++ .../java/ru/otus/spring/rest/PersonDto.java | 54 ++++++++ .../spring/rest/PersonControllerTest.java | 84 ++++++++++++ .../spring-mvc-solution-3/.gitignore | 24 ++++ .../spring-mvc-solution-3/pom.xml | 53 ++++++++ .../src/main/java/ru/otus/spring/Main.java | 28 ++++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++ .../spring/repostory/PersonRepository.java | 11 ++ .../ru/otus/spring/rest/ErrorHandler.java | 2 + .../otus/spring/rest/NotFoundException.java | 7 + .../ru/otus/spring/rest/PersonController.java | 43 ++++++ .../java/ru/otus/spring/rest/PersonDto.java | 54 ++++++++ .../spring/rest/PersonControllerTest.java | 84 ++++++++++++ .../spring-mvc-solution-4/.gitignore | 24 ++++ .../spring-mvc-solution-4/pom.xml | 52 ++++++++ .../src/main/java/ru/otus/spring/Main.java | 28 ++++ .../java/ru/otus/spring/domain/Person.java | 37 ++++++ .../spring/repostory/PersonRepository.java | 11 ++ .../ru/otus/spring/rest/ErrorHandler.java | 2 + .../otus/spring/rest/NotFoundException.java | 7 + .../ru/otus/spring/rest/PersonController.java | 62 +++++++++ .../java/ru/otus/spring/rest/PersonDto.java | 54 ++++++++ .../spring/rest/PersonControllerTest.java | 122 ++++++++++++++++++ 53 files changed, 1792 insertions(+) create mode 100644 2021-03/spring-15-mvc/.gitignore create mode 100644 2021-03/spring-15-mvc/pom.xml create mode 100644 2021-03/spring-15-mvc/requests.http create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/pom.xml create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/NotFoundException.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonDto.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/.gitignore create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/pom.xml create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/ErrorHandler.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/NotFoundException.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonDto.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/src/test/java/ru/otus/spring/rest/PersonControllerTest.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/.gitignore create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/pom.xml create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/ErrorHandler.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/NotFoundException.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonDto.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/src/test/java/ru/otus/spring/rest/PersonControllerTest.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/.gitignore create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/pom.xml create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/ErrorHandler.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/NotFoundException.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonDto.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/src/test/java/ru/otus/spring/rest/PersonControllerTest.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/.gitignore create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/pom.xml create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/ErrorHandler.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/NotFoundException.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonDto.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/src/test/java/ru/otus/spring/rest/PersonControllerTest.java diff --git a/2021-03/spring-15-mvc/.gitignore b/2021-03/spring-15-mvc/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-03/spring-15-mvc/.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-03/spring-15-mvc/pom.xml b/2021-03/spring-15-mvc/pom.xml new file mode 100644 index 00000000..9fe92a9b --- /dev/null +++ b/2021-03/spring-15-mvc/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + ru.otus + spring-mvc-class-work + 1.0 + + pom + + + spring-mvc-exercise + spring-mvc-solution-1 + spring-mvc-solution-2 + spring-mvc-solution-3 + spring-mvc-solution-4 + + diff --git a/2021-03/spring-15-mvc/requests.http b/2021-03/spring-15-mvc/requests.http new file mode 100644 index 00000000..e892d644 --- /dev/null +++ b/2021-03/spring-15-mvc/requests.http @@ -0,0 +1,11 @@ +POST http://localhost:8080/person +Content-Type: application/json + +{ + "id": "1", + "name": "Gogol'" +} + +### +GET http://localhost:8080/person + diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore b/2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore @@ -0,0 +1,24 @@ +target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/pom.xml b/2021-03/spring-15-mvc/spring-mvc-exercise/pom.xml new file mode 100644 index 00000000..963f4145 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + ru.otus + spring-mvc-exercise + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + + + + + org.springframework.data + spring-data-keyvalue + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..c8a54bea --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,28 @@ +package ru.otus.spring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.map.repository.config.EnableMapRepositories; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import javax.annotation.PostConstruct; + +@EnableMapRepositories +@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(1, "Pushkin")); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java new file mode 100644 index 00000000..97859832 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java @@ -0,0 +1,20 @@ +package ru.otus.spring.domain; + +public class Email { + + private long id; + + private String email; + + public Email(String email) { + this.email = email; + } + + public long getId() { + return id; + } + + public String getEmail() { + return email; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..4fea35dc --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,37 @@ +package ru.otus.spring.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.keyvalue.annotation.KeySpace; + +@KeySpace("person") +public class Person { + + @Id + private long id; + private String name; + + public Person(String name) { + this.name = name; + } + + public Person(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java new file mode 100644 index 00000000..dd8a6966 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/repostory/PersonRepository.java @@ -0,0 +1,11 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.repository.PagingAndSortingRepository; +import ru.otus.spring.domain.Person; + +import java.util.List; + +public interface PersonRepository extends PagingAndSortingRepository { + + List findAll(); +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/NotFoundException.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/NotFoundException.java new file mode 100644 index 00000000..3d98ee4a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.otus.spring.rest; + +public class NotFoundException extends RuntimeException{ + + public NotFoundException() { + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..344a34b7 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,13 @@ +package ru.otus.spring.rest; + +import ru.otus.spring.repostory.PersonRepository; + + +public class PersonController { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonDto.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonDto.java new file mode 100644 index 00000000..c561e56a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/rest/PersonDto.java @@ -0,0 +1,54 @@ +/* + * 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; + +import ru.otus.spring.domain.Person; + +/** + * DTO that represents Account + */ +@SuppressWarnings("all") +public class PersonDto { + + private long id; + private String name; + + public PersonDto() { + } + + public PersonDto(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static Person toDomainObject(PersonDto dto) { + return new Person(dto.getId(), dto.getName()); + } + + public static PersonDto toDto(Person account) { + return new PersonDto(account.getId(), account.getName()); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..086bf53c --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-exercise/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,84 @@ +package ru.otus.spring.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(PersonController.class) +class PersonControllerTest { + + public static final String ERROR_STRING = "Таких тут нет!"; + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @MockBean + private PersonRepository repository; + + @Test + void shouldReturnCorrectPersonsList() throws Exception { + List persons = List.of(new Person(1, "Person1"), new Person(2, "Person2")); + given(repository.findAll()).willReturn(persons); + + List expectedResult = persons.stream() + .map(PersonDto::toDto).collect(Collectors.toList()); + + mvc.perform(get("/persons/all")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInRequest() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInPath() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons/1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnExpectedErrorWhenPersonNotFound() throws Exception { + given(repository.findById(1L)).willReturn(Optional.empty()); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + + mvc.perform(get("/persons/1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + } + + +} \ No newline at end of file diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-1/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/.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-03/spring-15-mvc/spring-mvc-solution-1/pom.xml b/2021-03/spring-15-mvc/spring-mvc-solution-1/pom.xml new file mode 100644 index 00000000..bff84828 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + ru.otus + spring-mvc-solution-1 + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.data + spring-data-keyvalue + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..c8a54bea --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,28 @@ +package ru.otus.spring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.map.repository.config.EnableMapRepositories; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import javax.annotation.PostConstruct; + +@EnableMapRepositories +@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(1, "Pushkin")); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..4fea35dc --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,37 @@ +package ru.otus.spring.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.keyvalue.annotation.KeySpace; + +@KeySpace("person") +public class Person { + + @Id + private long id; + private String name; + + public Person(String name) { + this.name = name; + } + + public Person(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/repostory/PersonRepository.java new file mode 100644 index 00000000..dd8a6966 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/repostory/PersonRepository.java @@ -0,0 +1,11 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.repository.PagingAndSortingRepository; +import ru.otus.spring.domain.Person; + +import java.util.List; + +public interface PersonRepository extends PagingAndSortingRepository { + + List findAll(); +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/ErrorHandler.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/ErrorHandler.java new file mode 100644 index 00000000..b87f3ed6 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/ErrorHandler.java @@ -0,0 +1,4 @@ +package ru.otus.spring.rest; + +public class ErrorHandler { +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/NotFoundException.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/NotFoundException.java new file mode 100644 index 00000000..3d98ee4a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.otus.spring.rest; + +public class NotFoundException extends RuntimeException{ + + public NotFoundException() { + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..ca14e393 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,24 @@ +package ru.otus.spring.rest; + +import org.springframework.web.bind.annotation.*; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class PersonController { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @RequestMapping(value = "/persons/all", method = RequestMethod.GET) + public List getAllPersons() { + return repository.findAll().stream() + .map(PersonDto::toDto) + .collect(Collectors.toList()); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonDto.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonDto.java new file mode 100644 index 00000000..c561e56a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/main/java/ru/otus/spring/rest/PersonDto.java @@ -0,0 +1,54 @@ +/* + * 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; + +import ru.otus.spring.domain.Person; + +/** + * DTO that represents Account + */ +@SuppressWarnings("all") +public class PersonDto { + + private long id; + private String name; + + public PersonDto() { + } + + public PersonDto(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static Person toDomainObject(PersonDto dto) { + return new Person(dto.getId(), dto.getName()); + } + + public static PersonDto toDto(Person account) { + return new PersonDto(account.getId(), account.getName()); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-1/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..086bf53c --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-1/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,84 @@ +package ru.otus.spring.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(PersonController.class) +class PersonControllerTest { + + public static final String ERROR_STRING = "Таких тут нет!"; + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @MockBean + private PersonRepository repository; + + @Test + void shouldReturnCorrectPersonsList() throws Exception { + List persons = List.of(new Person(1, "Person1"), new Person(2, "Person2")); + given(repository.findAll()).willReturn(persons); + + List expectedResult = persons.stream() + .map(PersonDto::toDto).collect(Collectors.toList()); + + mvc.perform(get("/persons/all")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInRequest() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInPath() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons/1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnExpectedErrorWhenPersonNotFound() throws Exception { + given(repository.findById(1L)).willReturn(Optional.empty()); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + + mvc.perform(get("/persons/1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + } + + +} \ No newline at end of file diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-2/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/.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-03/spring-15-mvc/spring-mvc-solution-2/pom.xml b/2021-03/spring-15-mvc/spring-mvc-solution-2/pom.xml new file mode 100644 index 00000000..bb0ed71d --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + ru.otus + spring-mvc-solution-2 + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + + + + + org.springframework.data + spring-data-keyvalue + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..c8a54bea --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,28 @@ +package ru.otus.spring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.map.repository.config.EnableMapRepositories; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import javax.annotation.PostConstruct; + +@EnableMapRepositories +@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(1, "Pushkin")); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..4fea35dc --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,37 @@ +package ru.otus.spring.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.keyvalue.annotation.KeySpace; + +@KeySpace("person") +public class Person { + + @Id + private long id; + private String name; + + public Person(String name) { + this.name = name; + } + + public Person(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/repostory/PersonRepository.java new file mode 100644 index 00000000..dd8a6966 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/repostory/PersonRepository.java @@ -0,0 +1,11 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.repository.PagingAndSortingRepository; +import ru.otus.spring.domain.Person; + +import java.util.List; + +public interface PersonRepository extends PagingAndSortingRepository { + + List findAll(); +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/ErrorHandler.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/ErrorHandler.java new file mode 100644 index 00000000..07a077d4 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/ErrorHandler.java @@ -0,0 +1,2 @@ +package ru.otus.spring.rest;public class ErrorHandler { +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/NotFoundException.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/NotFoundException.java new file mode 100644 index 00000000..3d98ee4a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.otus.spring.rest; + +public class NotFoundException extends RuntimeException{ + + public NotFoundException() { + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..4f974cac --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,38 @@ +package ru.otus.spring.rest; + +import org.springframework.web.bind.annotation.*; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class PersonController { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @RequestMapping(value = "/persons/all", method = RequestMethod.GET, params = {}) + public List getAllPersons() { + return repository.findAll().stream() + .map(PersonDto::toDto) + .collect(Collectors.toList()); + } + + @RequestMapping(value = "/persons", method = RequestMethod.GET) + public PersonDto getPersonByIdInRequest(@RequestParam("id") long id) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + return PersonDto.toDto(person); + } + + @GetMapping("/persons/{id}") + public PersonDto getPersonByIdInPath(@PathVariable("id") long id) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + return PersonDto.toDto(person); + } + +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonDto.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonDto.java new file mode 100644 index 00000000..c561e56a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/main/java/ru/otus/spring/rest/PersonDto.java @@ -0,0 +1,54 @@ +/* + * 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; + +import ru.otus.spring.domain.Person; + +/** + * DTO that represents Account + */ +@SuppressWarnings("all") +public class PersonDto { + + private long id; + private String name; + + public PersonDto() { + } + + public PersonDto(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static Person toDomainObject(PersonDto dto) { + return new Person(dto.getId(), dto.getName()); + } + + public static PersonDto toDto(Person account) { + return new PersonDto(account.getId(), account.getName()); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-2/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..086bf53c --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-2/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,84 @@ +package ru.otus.spring.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(PersonController.class) +class PersonControllerTest { + + public static final String ERROR_STRING = "Таких тут нет!"; + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @MockBean + private PersonRepository repository; + + @Test + void shouldReturnCorrectPersonsList() throws Exception { + List persons = List.of(new Person(1, "Person1"), new Person(2, "Person2")); + given(repository.findAll()).willReturn(persons); + + List expectedResult = persons.stream() + .map(PersonDto::toDto).collect(Collectors.toList()); + + mvc.perform(get("/persons/all")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInRequest() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInPath() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons/1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnExpectedErrorWhenPersonNotFound() throws Exception { + given(repository.findById(1L)).willReturn(Optional.empty()); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + + mvc.perform(get("/persons/1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + } + + +} \ No newline at end of file diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-3/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/.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-03/spring-15-mvc/spring-mvc-solution-3/pom.xml b/2021-03/spring-15-mvc/spring-mvc-solution-3/pom.xml new file mode 100644 index 00000000..8e83469e --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + ru.otus + spring-mvc-solution-3 + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + + + + + org.springframework.data + spring-data-keyvalue + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..c8a54bea --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,28 @@ +package ru.otus.spring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.map.repository.config.EnableMapRepositories; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import javax.annotation.PostConstruct; + +@EnableMapRepositories +@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(1, "Pushkin")); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..4fea35dc --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,37 @@ +package ru.otus.spring.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.keyvalue.annotation.KeySpace; + +@KeySpace("person") +public class Person { + + @Id + private long id; + private String name; + + public Person(String name) { + this.name = name; + } + + public Person(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/repostory/PersonRepository.java new file mode 100644 index 00000000..dd8a6966 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/repostory/PersonRepository.java @@ -0,0 +1,11 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.repository.PagingAndSortingRepository; +import ru.otus.spring.domain.Person; + +import java.util.List; + +public interface PersonRepository extends PagingAndSortingRepository { + + List findAll(); +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/ErrorHandler.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/ErrorHandler.java new file mode 100644 index 00000000..07a077d4 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/ErrorHandler.java @@ -0,0 +1,2 @@ +package ru.otus.spring.rest;public class ErrorHandler { +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/NotFoundException.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/NotFoundException.java new file mode 100644 index 00000000..3d98ee4a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.otus.spring.rest; + +public class NotFoundException extends RuntimeException{ + + public NotFoundException() { + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..580fbff1 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,43 @@ +package ru.otus.spring.rest; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class PersonController { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @RequestMapping(value = "/persons/all", method = RequestMethod.GET, params = {}) + public List getAllPersons() { + return repository.findAll().stream() + .map(PersonDto::toDto) + .collect(Collectors.toList()); + } + + @RequestMapping(value = "/persons", method = RequestMethod.GET) + public PersonDto getPersonByIdInRequest(@RequestParam("id") long id) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + return PersonDto.toDto(person); + } + + @GetMapping("/persons/{id}") + public PersonDto getPersonByIdInPath(@PathVariable("id") long id) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + return PersonDto.toDto(person); + } + + @ExceptionHandler(NotFoundException.class) + public ResponseEntity handleNotFound(NotFoundException ex) { + return ResponseEntity.badRequest().body("Таких тут нет!"); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonDto.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonDto.java new file mode 100644 index 00000000..c561e56a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/main/java/ru/otus/spring/rest/PersonDto.java @@ -0,0 +1,54 @@ +/* + * 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; + +import ru.otus.spring.domain.Person; + +/** + * DTO that represents Account + */ +@SuppressWarnings("all") +public class PersonDto { + + private long id; + private String name; + + public PersonDto() { + } + + public PersonDto(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static Person toDomainObject(PersonDto dto) { + return new Person(dto.getId(), dto.getName()); + } + + public static PersonDto toDto(Person account) { + return new PersonDto(account.getId(), account.getName()); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-3/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..a913fb5a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-3/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,84 @@ +package ru.otus.spring.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(PersonController.class) +class PersonControllerTest { + + public static final String ERROR_STRING = "Таких тут нет!"; + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @MockBean + private PersonRepository repository; + + @Test + void shouldReturnCorrectPersonsList() throws Exception { + List persons = List.of(new Person(1, "Person1"), new Person(2, "Person2")); + given(repository.findAll()).willReturn(persons); + + List expectedResult = persons.stream() + .map(PersonDto::toDto).collect(Collectors.toList()); + + mvc.perform(get("/persons/all")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInRequest() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInPath() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons/1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnExpectedErrorWhenPersonNotFound() throws Exception { + given(repository.findById(1L)).willReturn(Optional.empty()); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + + mvc.perform(get("/persons/1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + } + + +} \ No newline at end of file diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-4/.gitignore new file mode 100644 index 00000000..4ea52072 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/.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-03/spring-15-mvc/spring-mvc-solution-4/pom.xml b/2021-03/spring-15-mvc/spring-mvc-solution-4/pom.xml new file mode 100644 index 00000000..3aa7eeb1 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + ru.otus + spring-mvc-solution-4 + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.5 + + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.data + spring-data-keyvalue + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..c8a54bea --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,28 @@ +package ru.otus.spring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.map.repository.config.EnableMapRepositories; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import javax.annotation.PostConstruct; + +@EnableMapRepositories +@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(1, "Pushkin")); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/domain/Person.java new file mode 100644 index 00000000..4fea35dc --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/domain/Person.java @@ -0,0 +1,37 @@ +package ru.otus.spring.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.keyvalue.annotation.KeySpace; + +@KeySpace("person") +public class Person { + + @Id + private long id; + private String name; + + public Person(String name) { + this.name = name; + } + + public Person(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/repostory/PersonRepository.java new file mode 100644 index 00000000..dd8a6966 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/repostory/PersonRepository.java @@ -0,0 +1,11 @@ +package ru.otus.spring.repostory; + +import org.springframework.data.repository.PagingAndSortingRepository; +import ru.otus.spring.domain.Person; + +import java.util.List; + +public interface PersonRepository extends PagingAndSortingRepository { + + List findAll(); +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/ErrorHandler.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/ErrorHandler.java new file mode 100644 index 00000000..07a077d4 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/ErrorHandler.java @@ -0,0 +1,2 @@ +package ru.otus.spring.rest;public class ErrorHandler { +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/NotFoundException.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/NotFoundException.java new file mode 100644 index 00000000..3d98ee4a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.otus.spring.rest; + +public class NotFoundException extends RuntimeException{ + + public NotFoundException() { + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..51ad268d --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,62 @@ +package ru.otus.spring.rest; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class PersonController { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @RequestMapping(value = "/persons/all", method = RequestMethod.GET, params = {}) + public List getAllPersons() { + return repository.findAll().stream() + .map(PersonDto::toDto) + .collect(Collectors.toList()); + } + + @RequestMapping(value = "/persons", method = RequestMethod.GET) + public PersonDto getPersonByIdInRequest(@RequestParam("id") long id) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + return PersonDto.toDto(person); + } + + @GetMapping("/persons/{id}") + public PersonDto getPersonByIdInPath(@PathVariable("id") long id) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + return PersonDto.toDto(person); + } + + @PostMapping("/persons") + public PersonDto createNewPerson(@RequestBody PersonDto dto) { + Person person = PersonDto.toDomainObject(dto); + Person savedPerson = repository.save(person); + return PersonDto.toDto(savedPerson); + } + + @PutMapping("/persons/{id}/name") + public PersonDto updateNameById(@PathVariable("id") long id, @RequestParam("name") String name) { + Person person = repository.findById(id).orElseThrow(NotFoundException::new); + person.setName(name); + return PersonDto.toDto(repository.save(person)); + } + + @DeleteMapping("/persons/{id}") + public void deleteById(@PathVariable("id") long id) { + repository.deleteById(id); + } + + @ExceptionHandler(NotFoundException.class) + public ResponseEntity handleNotFound(NotFoundException ex) { + return ResponseEntity.badRequest().body("Таких тут нет!"); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonDto.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonDto.java new file mode 100644 index 00000000..c561e56a --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonDto.java @@ -0,0 +1,54 @@ +/* + * 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; + +import ru.otus.spring.domain.Person; + +/** + * DTO that represents Account + */ +@SuppressWarnings("all") +public class PersonDto { + + private long id; + private String name; + + public PersonDto() { + } + + public PersonDto(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static Person toDomainObject(PersonDto dto) { + return new Person(dto.getId(), dto.getName()); + } + + public static PersonDto toDto(Person account) { + return new PersonDto(account.getId(), account.getName()); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/test/java/ru/otus/spring/rest/PersonControllerTest.java new file mode 100644 index 00000000..0a49473d --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-solution-4/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -0,0 +1,122 @@ +package ru.otus.spring.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import ru.otus.spring.domain.Person; +import ru.otus.spring.repostory.PersonRepository; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(PersonController.class) +class PersonControllerTest { + + public static final String ERROR_STRING = "Таких тут нет!"; + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @MockBean + private PersonRepository repository; + + @Test + void shouldReturnCorrectPersonsList() throws Exception { + List persons = List.of(new Person(1, "Person1"), new Person(2, "Person2")); + given(repository.findAll()).willReturn(persons); + + List expectedResult = persons.stream() + .map(PersonDto::toDto).collect(Collectors.toList()); + + mvc.perform(get("/persons/all")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInRequest() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnCorrectPersonByIdInPath() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + PersonDto expectedResult = PersonDto.toDto(person); + + mvc.perform(get("/persons/1")) + .andExpect(status().isOk()) + .andExpect(content().json(mapper.writeValueAsString(expectedResult))); + } + + @Test + void shouldReturnExpectedErrorWhenPersonNotFound() throws Exception { + given(repository.findById(1L)).willReturn(Optional.empty()); + + mvc.perform(get("/persons").param("id", "1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + + mvc.perform(get("/persons/1")) + .andExpect(status().isBadRequest()) + .andExpect(content().string(ERROR_STRING)); + } + + @Test + void shouldCorrectSaveNewPerson() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.save(any())).willReturn(person); + String expectedResult = mapper.writeValueAsString(PersonDto.toDto(person)); + + mvc.perform(post("/persons").contentType(APPLICATION_JSON) + .content(expectedResult)) + .andExpect(status().isOk()) + .andExpect(content().json(expectedResult)); + } + + @Test + void shouldCorrectUpdatePersonName() throws Exception { + Person person = new Person(1, "Person1"); + given(repository.findById(1L)).willReturn(Optional.of(person)); + given(repository.save(any())).willAnswer(invocation -> invocation.getArgument(0)); + + Person expectedPerson = new Person(1, "Person2"); + String expectedResult = mapper.writeValueAsString(PersonDto.toDto(expectedPerson)); + + mvc.perform(put("/persons/{id}/name", 1).param("name", expectedPerson.getName()) + .content(expectedResult)) + .andExpect(status().isOk()) + .andExpect(content().json(expectedResult)); + } + + @Test + void shouldCorrectDeletePerson() throws Exception { + mvc.perform(delete("/persons/1")) + .andExpect(status().isOk()); + verify(repository, times(1)).deleteById(1L); + } + + +} \ No newline at end of file From 2dd63c0defb2297719786bc9f758bb6eb2254aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9E=D1=80=D1=83=D0=B4=D0=B6=D0=B5=D0=B2?= Date: Mon, 24 May 2021 12:52:09 +0400 Subject: [PATCH 18/25] 2021-03 spring-15-mvc updated --- 2021-03/spring-15-mvc/.gitignore | 24 ------------- 2021-03/spring-15-mvc/requests.http | 11 ------ .../pom.xml | 2 +- .../src/main/java/ru/otus/spring/Main.java | 0 .../java/ru/otus/spring/config/WebConfig.java | 22 ++++++++++++ .../java/ru/otus/spring/domain/Person.java | 0 .../ru/otus/spring/domain/SystemInfo.java | 19 +++++++++++ .../spring/repostory/PersonRepository.java | 0 .../ru/otus/spring/rest/ErrorHandler.java | 0 .../otus/spring/rest/NotFoundException.java | 0 .../ru/otus/spring/rest/PersonController.java | 0 .../java/ru/otus/spring/rest/PersonDto.java | 0 .../spring/rest/SystemInfoController.java | 14 ++++++++ .../SystemInfoMethodArgumentResolver.java | 31 +++++++++++++++++ .../spring/service/SystemInfoService.java | 17 ++++++++++ .../spring/rest/PersonControllerTest.java | 0 .../spring/rest/SystemInfoControllerTest.java | 34 +++++++++++++++++++ .../spring-mvc-exercise/.gitignore | 24 ------------- .../spring-mvc-solution-1/.gitignore | 24 ------------- .../spring-mvc-solution-2/.gitignore | 24 ------------- .../spring-mvc-solution-3/.gitignore | 24 ------------- .../spring-mvc-solution-4/.gitignore | 24 ------------- 22 files changed, 138 insertions(+), 156 deletions(-) delete mode 100644 2021-03/spring-15-mvc/.gitignore delete mode 100644 2021-03/spring-15-mvc/requests.http rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/pom.xml (96%) rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/Main.java (100%) create mode 100644 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/domain/Person.java (100%) create mode 100644 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/repostory/PersonRepository.java (100%) rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/rest/ErrorHandler.java (100%) rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/rest/NotFoundException.java (100%) rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/rest/PersonController.java (100%) rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/main/java/ru/otus/spring/rest/PersonDto.java (100%) create mode 100644 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoController.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java create mode 100644 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java rename 2021-03/spring-15-mvc/{spring-mvc-solution-4 => spring-mvc-demo}/src/test/java/ru/otus/spring/rest/PersonControllerTest.java (100%) create mode 100644 2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/SystemInfoControllerTest.java delete mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore delete mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-1/.gitignore delete mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-2/.gitignore delete mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-3/.gitignore delete mode 100644 2021-03/spring-15-mvc/spring-mvc-solution-4/.gitignore diff --git a/2021-03/spring-15-mvc/.gitignore b/2021-03/spring-15-mvc/.gitignore deleted file mode 100644 index 4ea52072..00000000 --- a/2021-03/spring-15-mvc/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -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-03/spring-15-mvc/requests.http b/2021-03/spring-15-mvc/requests.http deleted file mode 100644 index e892d644..00000000 --- a/2021-03/spring-15-mvc/requests.http +++ /dev/null @@ -1,11 +0,0 @@ -POST http://localhost:8080/person -Content-Type: application/json - -{ - "id": "1", - "name": "Gogol'" -} - -### -GET http://localhost:8080/person - diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/pom.xml b/2021-03/spring-15-mvc/spring-mvc-demo/pom.xml similarity index 96% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/pom.xml rename to 2021-03/spring-15-mvc/spring-mvc-demo/pom.xml index 3aa7eeb1..0eee1791 100644 --- a/2021-03/spring-15-mvc/spring-mvc-solution-4/pom.xml +++ b/2021-03/spring-15-mvc/spring-mvc-demo/pom.xml @@ -5,7 +5,7 @@ 4.0.0 ru.otus - spring-mvc-solution-4 + spring-mvc-demo 1.0 diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/Main.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/Main.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/Main.java diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java new file mode 100644 index 00000000..0513c3d5 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java @@ -0,0 +1,22 @@ +package ru.otus.spring.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import ru.otus.spring.rest.SystemInfoMethodArgumentResolver; +import ru.otus.spring.service.SystemInfoService; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Autowired + private SystemInfoService systemInfoService; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new SystemInfoMethodArgumentResolver(systemInfoService)); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/Person.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/domain/Person.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/Person.java diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java new file mode 100644 index 00000000..40deaaee --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java @@ -0,0 +1,19 @@ +package ru.otus.spring.domain; + +public class SystemInfo { + private final String osName; + private final String timeZone; + private final String osArch; + private final int processorsCount; + + public SystemInfo(String osName, String timeZone, String osArch, int processorsCount) { + this.osName = osName; + this.timeZone = timeZone; + this.osArch = osArch; + this.processorsCount = processorsCount; + } + + public String getOsName() { + return osName; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/repostory/PersonRepository.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/repostory/PersonRepository.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/repostory/PersonRepository.java diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/ErrorHandler.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/ErrorHandler.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/ErrorHandler.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/ErrorHandler.java diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/NotFoundException.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/NotFoundException.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/NotFoundException.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/NotFoundException.java diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonController.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonController.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonController.java diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonDto.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonDto.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/main/java/ru/otus/spring/rest/PersonDto.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonDto.java diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoController.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoController.java new file mode 100644 index 00000000..88ccc448 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoController.java @@ -0,0 +1,14 @@ +package ru.otus.spring.rest; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.otus.spring.domain.SystemInfo; + +@RestController +public class SystemInfoController { + + @GetMapping("/server/system/info") + public SystemInfo getServerSystemInfo(SystemInfo systemInfo) { + return systemInfo; + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java new file mode 100644 index 00000000..601ab48c --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java @@ -0,0 +1,31 @@ +package ru.otus.spring.rest; + +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import ru.otus.spring.domain.SystemInfo; +import ru.otus.spring.service.SystemInfoService; + +public class SystemInfoMethodArgumentResolver implements HandlerMethodArgumentResolver { + + private final SystemInfoService systemInfoService; + + public SystemInfoMethodArgumentResolver(SystemInfoService systemInfoService) { + this.systemInfoService = systemInfoService; + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(SystemInfo.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) throws Exception { + return systemInfoService.getSystemInfo(); + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java new file mode 100644 index 00000000..3914948f --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java @@ -0,0 +1,17 @@ +package ru.otus.spring.service; + +import org.springframework.stereotype.Service; +import ru.otus.spring.domain.SystemInfo; + +@Service +public class SystemInfoService { +// + public SystemInfo getSystemInfo(){ + String osName = System.getProperty("os.name"); + String timeZone = System.getProperty("user.timezone"); + String osArch = System.getProperty("os.arch"); + int processorsCount = Runtime.getRuntime().availableProcessors();; + return new SystemInfo(osName, timeZone, osArch, processorsCount); + //HandlerMethodArgumentResolver + } +} diff --git a/2021-03/spring-15-mvc/spring-mvc-solution-4/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/PersonControllerTest.java similarity index 100% rename from 2021-03/spring-15-mvc/spring-mvc-solution-4/src/test/java/ru/otus/spring/rest/PersonControllerTest.java rename to 2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/PersonControllerTest.java diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/SystemInfoControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/SystemInfoControllerTest.java new file mode 100644 index 00000000..33f81220 --- /dev/null +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/SystemInfoControllerTest.java @@ -0,0 +1,34 @@ +package ru.otus.spring.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; +import ru.otus.spring.domain.SystemInfo; +import ru.otus.spring.service.SystemInfoService; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; + +@WebMvcTest(SystemInfoController.class) +@Import(SystemInfoService.class) +class SystemInfoControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @Autowired + private SystemInfoService systemInfoService; + + @Test + void shouldReturnCorrectServerSystemInfo() throws Exception { + SystemInfo expectedSystemInfo = systemInfoService.getSystemInfo(); + mvc.perform(get("/server/system/info")) + .andExpect(content().json(mapper.writeValueAsString(expectedSystemInfo))); + } +} \ No newline at end of file diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore b/2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore deleted file mode 100644 index 4ea52072..00000000 --- a/2021-03/spring-15-mvc/spring-mvc-exercise/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -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-03/spring-15-mvc/spring-mvc-solution-1/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-1/.gitignore deleted file mode 100644 index 4ea52072..00000000 --- a/2021-03/spring-15-mvc/spring-mvc-solution-1/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -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-03/spring-15-mvc/spring-mvc-solution-2/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-2/.gitignore deleted file mode 100644 index 4ea52072..00000000 --- a/2021-03/spring-15-mvc/spring-mvc-solution-2/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -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-03/spring-15-mvc/spring-mvc-solution-3/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-3/.gitignore deleted file mode 100644 index 4ea52072..00000000 --- a/2021-03/spring-15-mvc/spring-mvc-solution-3/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -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-03/spring-15-mvc/spring-mvc-solution-4/.gitignore b/2021-03/spring-15-mvc/spring-mvc-solution-4/.gitignore deleted file mode 100644 index 4ea52072..00000000 --- a/2021-03/spring-15-mvc/spring-mvc-solution-4/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -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/ From a5638699991a601b7c7d2e37e7d408db43fc895a Mon Sep 17 00:00:00 2001 From: stvort Date: Mon, 24 May 2021 12:54:10 +0400 Subject: [PATCH 19/25] 2021-03 spring-mvc updated --- 2021-03/spring-15-mvc/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2021-03/spring-15-mvc/pom.xml b/2021-03/spring-15-mvc/pom.xml index 9fe92a9b..c9bb3f66 100644 --- a/2021-03/spring-15-mvc/pom.xml +++ b/2021-03/spring-15-mvc/pom.xml @@ -15,6 +15,6 @@ spring-mvc-solution-1 spring-mvc-solution-2 spring-mvc-solution-3 - spring-mvc-solution-4 + spring-mvc-demo From 57f4c37117bd2279c8259337a88b759e51b135e6 Mon Sep 17 00:00:00 2001 From: stvort Date: Mon, 24 May 2021 12:59:05 +0400 Subject: [PATCH 20/25] 2021-03 spring-mvc updated --- .../java/ru/otus/spring/domain/Email.java | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java diff --git a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java b/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java deleted file mode 100644 index 97859832..00000000 --- a/2021-03/spring-15-mvc/spring-mvc-exercise/src/main/java/ru/otus/spring/domain/Email.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.otus.spring.domain; - -public class Email { - - private long id; - - private String email; - - public Email(String email) { - this.email = email; - } - - public long getId() { - return id; - } - - public String getEmail() { - return email; - } -} From 9fe137714c0de47f08c53315cf43040af268e89c Mon Sep 17 00:00:00 2001 From: stvort Date: Mon, 24 May 2021 14:37:49 +0400 Subject: [PATCH 21/25] 2021-03 spring-mvc updated --- .../main/java/ru/otus/spring/domain/SystemInfo.java | 12 ++++++++++++ .../java/ru/otus/spring/rest/PersonController.java | 4 ++-- .../ru/otus/spring/service/SystemInfoService.java | 6 +++--- .../ru/otus/spring/rest/PersonControllerTest.java | 6 +++++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java index 40deaaee..43c78151 100644 --- a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/domain/SystemInfo.java @@ -16,4 +16,16 @@ public class SystemInfo { public String getOsName() { return osName; } + + public String getTimeZone() { + return timeZone; + } + + public String getOsArch() { + return osArch; + } + + public int getProcessorsCount() { + return processorsCount; + } } diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonController.java index 51ad268d..882d61ba 100644 --- a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonController.java +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/PersonController.java @@ -17,7 +17,7 @@ public class PersonController { this.repository = repository; } - @RequestMapping(value = "/persons/all", method = RequestMethod.GET, params = {}) + @RequestMapping(value = "/persons/all", method = RequestMethod.GET) public List getAllPersons() { return repository.findAll().stream() .map(PersonDto::toDto) @@ -43,7 +43,7 @@ public class PersonController { return PersonDto.toDto(savedPerson); } - @PutMapping("/persons/{id}/name") + @PatchMapping("/persons/{id}/name") public PersonDto updateNameById(@PathVariable("id") long id, @RequestParam("name") String name) { Person person = repository.findById(id).orElseThrow(NotFoundException::new); person.setName(name); diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java index 3914948f..906eaed3 100644 --- a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/service/SystemInfoService.java @@ -5,13 +5,13 @@ import ru.otus.spring.domain.SystemInfo; @Service public class SystemInfoService { -// + public SystemInfo getSystemInfo(){ String osName = System.getProperty("os.name"); String timeZone = System.getProperty("user.timezone"); String osArch = System.getProperty("os.arch"); - int processorsCount = Runtime.getRuntime().availableProcessors();; + int processorsCount = Runtime.getRuntime().availableProcessors(); return new SystemInfo(osName, timeZone, osArch, processorsCount); - //HandlerMethodArgumentResolver + } } diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/PersonControllerTest.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/PersonControllerTest.java index 0a49473d..cf66b0d7 100644 --- a/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/PersonControllerTest.java +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/test/java/ru/otus/spring/rest/PersonControllerTest.java @@ -8,6 +8,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import ru.otus.spring.domain.Person; import ru.otus.spring.repostory.PersonRepository; +import ru.otus.spring.service.SystemInfoService; import java.util.List; import java.util.Optional; @@ -36,6 +37,9 @@ class PersonControllerTest { @MockBean private PersonRepository repository; + @MockBean + private SystemInfoService systemInfoService; + @Test void shouldReturnCorrectPersonsList() throws Exception { List persons = List.of(new Person(1, "Person1"), new Person(2, "Person2")); @@ -105,7 +109,7 @@ class PersonControllerTest { Person expectedPerson = new Person(1, "Person2"); String expectedResult = mapper.writeValueAsString(PersonDto.toDto(expectedPerson)); - mvc.perform(put("/persons/{id}/name", 1).param("name", expectedPerson.getName()) + mvc.perform(patch("/persons/{id}/name", 1).param("name", expectedPerson.getName()) .content(expectedResult)) .andExpect(status().isOk()) .andExpect(content().json(expectedResult)); From a60899a962327e355307f1c753b26742637b2215 Mon Sep 17 00:00:00 2001 From: stvort Date: Mon, 24 May 2021 23:01:37 +0400 Subject: [PATCH 22/25] 2021-03 spring-mvc updated --- .../src/main/java/ru/otus/spring/config/WebConfig.java | 4 ++-- .../ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java index 0513c3d5..433cd09a 100644 --- a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/config/WebConfig.java @@ -13,10 +13,10 @@ import java.util.List; public class WebConfig implements WebMvcConfigurer { @Autowired - private SystemInfoService systemInfoService; + private SystemInfoMethodArgumentResolver systemInfoMethodArgumentResolver; @Override public void addArgumentResolvers(List resolvers) { - resolvers.add(new SystemInfoMethodArgumentResolver(systemInfoService)); + resolvers.add(systemInfoMethodArgumentResolver); } } diff --git a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java index 601ab48c..c40c5416 100644 --- a/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java +++ b/2021-03/spring-15-mvc/spring-mvc-demo/src/main/java/ru/otus/spring/rest/SystemInfoMethodArgumentResolver.java @@ -1,6 +1,7 @@ package ru.otus.spring.rest; import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -8,6 +9,7 @@ import org.springframework.web.method.support.ModelAndViewContainer; import ru.otus.spring.domain.SystemInfo; import ru.otus.spring.service.SystemInfoService; +@Component public class SystemInfoMethodArgumentResolver implements HandlerMethodArgumentResolver { private final SystemInfoService systemInfoService; From ae541da4d895439e9e342f2201c712b6a3dc8423 Mon Sep 17 00:00:00 2001 From: stvort Date: Wed, 26 May 2021 01:41:58 +0400 Subject: [PATCH 23/25] 2021-03 spring-16-ajax added --- 2021-03/spring-16-ajax/ajax-demo.html | 84 +++++++++++++++++++ .../spring-16-ajax/spring-ajax-demo/pom.xml | 72 ++++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 13 +++ .../java/ru/otus/spring/domain/Person.java | 22 +++++ .../spring/page/PersonPagesController.java | 15 ++++ .../spring/repostory/PersonRepository.java | 11 +++ .../ru/otus/spring/rest/PersonController.java | 23 +++++ .../ru/otus/spring/rest/dto/PersonDto.java | 26 ++++++ .../src/main/resources/application.yml | 11 +++ .../src/main/resources/data.sql | 1 + .../src/main/resources/schema.sql | 8 ++ .../src/main/resources/templates/list.html | 49 +++++++++++ 12 files changed, 335 insertions(+) create mode 100644 2021-03/spring-16-ajax/ajax-demo.html create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/pom.xml create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/domain/Person.java create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/page/PersonPagesController.java create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/repostory/PersonRepository.java create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/dto/PersonDto.java create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/application.yml create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/data.sql create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/schema.sql create mode 100644 2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/templates/list.html diff --git a/2021-03/spring-16-ajax/ajax-demo.html b/2021-03/spring-16-ajax/ajax-demo.html new file mode 100644 index 00000000..b71de6c2 --- /dev/null +++ b/2021-03/spring-16-ajax/ajax-demo.html @@ -0,0 +1,84 @@ + + + + Технологии JS для отправки запросов + + + + + + + + + + + + + +

+

+

+

+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/pom.xml b/2021-03/spring-16-ajax/spring-ajax-demo/pom.xml
new file mode 100644
index 00000000..38e975ff
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/pom.xml
@@ -0,0 +1,72 @@
+
+
+    4.0.0
+
+    ru.otus
+    spring-ajax-demo
+    1.0
+
+    
+        org.springframework.boot
+        spring-boot-starter-parent
+        2.4.5
+    
+
+    
+        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.5.1
+        
+
+
+        
+            com.h2database
+            h2
+        
+
+        
+            org.springframework.boot
+            spring-boot-starter-data-jpa
+        
+
+        
+            org.projectlombok
+            lombok
+            1.18.20
+            provided
+        
+
+    
+
+    
+        
+            
+                org.springframework.boot
+                spring-boot-maven-plugin
+            
+        
+    
+
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/Main.java b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..e2779e96
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,13 @@
+package ru.otus.spring;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Main {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Main.class);
+    }
+
+}
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/domain/Person.java b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..56404ae3
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,22 @@
+package ru.otus.spring.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Entity
+@Table(name = "persons")
+public class Person {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    @Column(name = "name")
+    private String name;
+}
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/page/PersonPagesController.java b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/page/PersonPagesController.java
new file mode 100644
index 00000000..0f3a2e76
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/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-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/repostory/PersonRepository.java b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/repostory/PersonRepository.java
new file mode 100644
index 00000000..4fb88650
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/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-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/PersonController.java b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/PersonController.java
new file mode 100644
index 00000000..26fee19f
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/PersonController.java
@@ -0,0 +1,23 @@
+package ru.otus.spring.rest;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import ru.otus.spring.repostory.PersonRepository;
+import ru.otus.spring.rest.dto.PersonDto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RequiredArgsConstructor
+@RestController
+public class PersonController {
+
+    private final PersonRepository repository;
+
+    @GetMapping("/api/persons")
+    public List getAllPersons() {
+        return repository.findAll().stream().map(PersonDto::toDto)
+                .collect(Collectors.toList());
+    }
+}
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/dto/PersonDto.java b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/dto/PersonDto.java
new file mode 100644
index 00000000..ee966a8e
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/java/ru/otus/spring/rest/dto/PersonDto.java
@@ -0,0 +1,26 @@
+/*
+ * 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 lombok.AllArgsConstructor;
+import lombok.Data;
+import ru.otus.spring.domain.Person;
+
+@Data
+@AllArgsConstructor
+public class PersonDto {
+
+    private long id = -1;
+    private String name;
+
+    public static PersonDto toDto(Person person) {
+        return new PersonDto(person.getId(), person.getName());
+    }
+}
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/application.yml b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/application.yml
new file mode 100644
index 00000000..21e39b30
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/application.yml
@@ -0,0 +1,11 @@
+spring:
+  datasource:
+    url: jdbc:h2:mem:testdb
+    initialization-mode: always
+
+  jpa:
+    generate-ddl: false
+    hibernate:
+      ddl-auto: none
+
+    show-sql: true
\ No newline at end of file
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/data.sql b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/data.sql
new file mode 100644
index 00000000..e60c79ca
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/data.sql
@@ -0,0 +1 @@
+INSERT INTO persons (name) VALUES ('Pushkin'), ('Lermontov')
\ No newline at end of file
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/schema.sql b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/schema.sql
new file mode 100644
index 00000000..7771dc1b
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/schema.sql
@@ -0,0 +1,8 @@
+DROP TABLE IF EXISTS persons;
+
+CREATE TABLE persons (
+    id BIGSERIAL,
+    name VARCHAR(250),
+
+    PRIMARY KEY (id)
+);
\ No newline at end of file
diff --git a/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/templates/list.html b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/templates/list.html
new file mode 100644
index 00000000..3d37dce1
--- /dev/null
+++ b/2021-03/spring-16-ajax/spring-ajax-demo/src/main/resources/templates/list.html
@@ -0,0 +1,49 @@
+
+
+
+    
+    
+    List of all persons
+    
+    
+
+
+

Persons:

+ + + + + + + + + + +
IDName
+ + + + + From efb2ab6aa2c85d93b988022c185e6bbb10a4fce8 Mon Sep 17 00:00:00 2001 From: kataus Date: Wed, 26 May 2021 19:42:41 +0300 Subject: [PATCH 24/25] =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=BA=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D0=B8=D1=8E=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=B0=D1=83=D1=82=D0=B5=D0=BD=D1=82=D0=B8=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D0=B8=20Spring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2021-02/spring-23/pom.xml | 61 +++++++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 12 ++++ .../ru/otus/spring/rest/PagesController.java | 44 +++++++++++++ .../ru/otus/spring/security/AnonimusUD.java | 43 +++++++++++++ .../security/SecurityConfiguration.java | 53 ++++++++++++++++ .../spring/security/filter/MyOwnFilter.java | 17 ++++++ .../src/main/resources/application.yml | 3 + .../resources/templates/authenticated.html | 10 +++ .../src/main/resources/templates/error.html | 10 +++ .../src/main/resources/templates/index.html | 12 ++++ .../src/main/resources/templates/public.html | 10 +++ .../src/main/resources/templates/success.html | 10 +++ .../otus/spring/rest/PagesControllerTest.java | 30 +++++++++ 13 files changed, 315 insertions(+) create mode 100644 2021-02/spring-23/pom.xml create mode 100644 2021-02/spring-23/src/main/java/ru/otus/spring/Main.java create mode 100644 2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java create mode 100644 2021-02/spring-23/src/main/java/ru/otus/spring/security/AnonimusUD.java create mode 100644 2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java create mode 100644 2021-02/spring-23/src/main/java/ru/otus/spring/security/filter/MyOwnFilter.java create mode 100644 2021-02/spring-23/src/main/resources/application.yml create mode 100644 2021-02/spring-23/src/main/resources/templates/authenticated.html create mode 100644 2021-02/spring-23/src/main/resources/templates/error.html create mode 100644 2021-02/spring-23/src/main/resources/templates/index.html create mode 100644 2021-02/spring-23/src/main/resources/templates/public.html create mode 100644 2021-02/spring-23/src/main/resources/templates/success.html create mode 100644 2021-02/spring-23/src/test/java/ru/otus/spring/rest/PagesControllerTest.java diff --git a/2021-02/spring-23/pom.xml b/2021-02/spring-23/pom.xml new file mode 100644 index 00000000..396cc6f3 --- /dev/null +++ b/2021-02/spring-23/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + ru.otus + spring-framework-23-auth + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.3.RELEASE + + + + UTF-8 + UTF-8 + 11 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + org.springframework.boot + spring-boot-starter-security + + + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.security + spring-security-test + ${spring-security.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/Main.java b/2021-02/spring-23/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..5406a277 --- /dev/null +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,12 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + SpringApplication.run(Main.class); + } +} diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java b/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java new file mode 100644 index 00000000..37fad1d0 --- /dev/null +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java @@ -0,0 +1,44 @@ +package ru.otus.spring.rest; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class PagesController { + + @GetMapping("/") + public String indexPage() { + return "index"; + } + + @GetMapping("/public") + public String publicPage() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + System.out.println(authentication.getPrincipal()); + return "public"; + } + + @GetMapping("/authenticated") + public String authenticatedPage() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + UserDetails userDetails = (UserDetails) authentication.getDetails(); + System.out.println(userDetails.getUsername()); + return "authenticated"; + } + + @GetMapping("/success") + public String successPage() { + return "success"; + } + + @GetMapping("/error") + public String errorPage() { + return "error"; + } +} diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/security/AnonimusUD.java b/2021-02/spring-23/src/main/java/ru/otus/spring/security/AnonimusUD.java new file mode 100644 index 00000000..b7a31238 --- /dev/null +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/security/AnonimusUD.java @@ -0,0 +1,43 @@ +package ru.otus.spring.security; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; + +public class AnonimusUD implements UserDetails { + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return "anonymous"; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java b/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java new file mode 100644 index 00000000..307bb289 --- /dev/null +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java @@ -0,0 +1,53 @@ +package ru.otus.spring.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@EnableWebSecurity +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + public void configure( WebSecurity web ) { + web.ignoring().antMatchers( "/" ); + } + + @Override + public void configure( HttpSecurity http ) throws Exception { + http.csrf().disable() + // По умолчанию SecurityContext хранится в сессии + // Это необходимо, чтобы он нигде не хранился + // и данные приходили каждый раз с запросом + .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ) + .and() + .authorizeRequests().antMatchers( "/public" ).anonymous() + .and() + .authorizeRequests().antMatchers( "/authenticated", "/success" ).authenticated() + + .and() + // Включает Form-based аутентификацию +// + .formLogin(); + +// ; + } + + @SuppressWarnings("deprecation") + @Bean + public PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); + } + + @Autowired + public void configure( AuthenticationManagerBuilder auth ) throws Exception { + auth.inMemoryAuthentication() + .withUser( "admin" ).password( "password" ).roles( "ADMIN" ); + } +} diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/security/filter/MyOwnFilter.java b/2021-02/spring-23/src/main/java/ru/otus/spring/security/filter/MyOwnFilter.java new file mode 100644 index 00000000..010afbaa --- /dev/null +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/security/filter/MyOwnFilter.java @@ -0,0 +1,17 @@ +package ru.otus.spring.security.filter; + +import org.springframework.web.filter.GenericFilterBean; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +public class MyOwnFilter extends GenericFilterBean { + @Override + public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain ) throws IOException, ServletException { + servletRequest.getParameterMap().put( "SpecialValue", new String[]{ "My dirty secret" } ); + filterChain.doFilter( servletRequest, servletResponse ); + } +} diff --git a/2021-02/spring-23/src/main/resources/application.yml b/2021-02/spring-23/src/main/resources/application.yml new file mode 100644 index 00000000..e0afbd1f --- /dev/null +++ b/2021-02/spring-23/src/main/resources/application.yml @@ -0,0 +1,3 @@ +logging: + level: + root: error \ No newline at end of file diff --git a/2021-02/spring-23/src/main/resources/templates/authenticated.html b/2021-02/spring-23/src/main/resources/templates/authenticated.html new file mode 100644 index 00000000..0d2043f0 --- /dev/null +++ b/2021-02/spring-23/src/main/resources/templates/authenticated.html @@ -0,0 +1,10 @@ + + + + + Только для авторизованных + + +Только для авторизованных + + diff --git a/2021-02/spring-23/src/main/resources/templates/error.html b/2021-02/spring-23/src/main/resources/templates/error.html new file mode 100644 index 00000000..ba4e50a2 --- /dev/null +++ b/2021-02/spring-23/src/main/resources/templates/error.html @@ -0,0 +1,10 @@ + + + + + Упс... + + +Что-то пошло не так. Печалька + + diff --git a/2021-02/spring-23/src/main/resources/templates/index.html b/2021-02/spring-23/src/main/resources/templates/index.html new file mode 100644 index 00000000..f2d1d1ae --- /dev/null +++ b/2021-02/spring-23/src/main/resources/templates/index.html @@ -0,0 +1,12 @@ + + + + + Главная страница + + +/public +
+/authenticated + + diff --git a/2021-02/spring-23/src/main/resources/templates/public.html b/2021-02/spring-23/src/main/resources/templates/public.html new file mode 100644 index 00000000..b9f93817 --- /dev/null +++ b/2021-02/spring-23/src/main/resources/templates/public.html @@ -0,0 +1,10 @@ + + + + + Доступен всем + + +Доступен всем + + diff --git a/2021-02/spring-23/src/main/resources/templates/success.html b/2021-02/spring-23/src/main/resources/templates/success.html new file mode 100644 index 00000000..58414c01 --- /dev/null +++ b/2021-02/spring-23/src/main/resources/templates/success.html @@ -0,0 +1,10 @@ + + + + + Вы успешно вошли ! + + +Вы успешно вошли ! + + diff --git a/2021-02/spring-23/src/test/java/ru/otus/spring/rest/PagesControllerTest.java b/2021-02/spring-23/src/test/java/ru/otus/spring/rest/PagesControllerTest.java new file mode 100644 index 00000000..13a4e4f1 --- /dev/null +++ b/2021-02/spring-23/src/test/java/ru/otus/spring/rest/PagesControllerTest.java @@ -0,0 +1,30 @@ +package ru.otus.spring.rest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(PagesController.class) +public class PagesControllerTest { + + @Autowired + private MockMvc mockMvc; + + @WithMockUser( + username = "admin", + authorities = {"ROLE_ADMIN"} + ) + @Test + public void testAuthenticatedOnAdmin() throws Exception { + mockMvc.perform(get("/authenticated")) + .andExpect(status().isOk()); + } +} From 684e4937da64d64efc1dceb032012bf568d40789 Mon Sep 17 00:00:00 2001 From: kataus Date: Wed, 26 May 2021 20:33:46 +0300 Subject: [PATCH 25/25] =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=BA=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D0=B8=D1=8E=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=B0=D1=83=D1=82=D0=B5=D0=BD=D1=82=D0=B8=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D0=B8=20Spring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/otus/spring/rest/PagesController.java | 6 +++--- .../ru/otus/spring/security/SecurityConfiguration.java | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java b/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java index 37fad1d0..b7066f8e 100644 --- a/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/rest/PagesController.java @@ -26,9 +26,9 @@ public class PagesController { @GetMapping("/authenticated") public String authenticatedPage() { SecurityContext securityContext = SecurityContextHolder.getContext(); - Authentication authentication = securityContext.getAuthentication(); - UserDetails userDetails = (UserDetails) authentication.getDetails(); - System.out.println(userDetails.getUsername()); +// Authentication authentication = securityContext.getAuthentication(); +// UserDetails userDetails = (UserDetails) authentication.getDetails(); +// System.out.println(userDetails.getUsername()); return "authenticated"; } diff --git a/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java b/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java index 307bb289..4e5448d3 100644 --- a/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java +++ b/2021-02/spring-23/src/main/java/ru/otus/spring/security/SecurityConfiguration.java @@ -25,16 +25,17 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { // По умолчанию SecurityContext хранится в сессии // Это необходимо, чтобы он нигде не хранился // и данные приходили каждый раз с запросом - .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ) - .and() +// .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ) +// .and() .authorizeRequests().antMatchers( "/public" ).anonymous() .and() .authorizeRequests().antMatchers( "/authenticated", "/success" ).authenticated() .and() // Включает Form-based аутентификацию -// - .formLogin(); + .formLogin() + .passwordParameter( "vk_pass" ) + .successForwardUrl( "/success" ); // ; }