mirror of
https://github.com/OtusTeam/Spring.git
synced 2026-05-30 10:50:42 +00:00
deleted old examples
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### 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/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
@@ -1,79 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.4.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>db-problems-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>db-problems-demo</name>
|
||||
<description>Demo project for Db usage problems </description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.200</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.17</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1 +0,0 @@
|
||||
Проект с миграцией для демонстрации того, как выглядит джойн нескольких таблиц при связи "многие-ко-многим"
|
||||
@@ -1,60 +0,0 @@
|
||||
package db.migration;
|
||||
|
||||
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
||||
import org.flywaydb.core.api.migration.Context;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class R__0002_Add_books extends BaseJavaMigration {
|
||||
|
||||
public static final int BOOKS_COUNT = 100_000;
|
||||
|
||||
@Override
|
||||
public void migrate(Context context) throws Exception {
|
||||
DataSource ds = new SingleConnectionDataSource(context.getConnection(), true);
|
||||
NamedParameterJdbcOperations jdbc = new NamedParameterJdbcTemplate(ds);
|
||||
|
||||
insertBooks(jdbc);
|
||||
insertBooksRelations(jdbc);
|
||||
}
|
||||
|
||||
private void insertBooks(NamedParameterJdbcOperations jdbc) {
|
||||
List<Map<String, Object>> params = new ArrayList<>(BOOKS_COUNT);
|
||||
for (int i = 0; i < BOOKS_COUNT; i++) {
|
||||
String bookTitle = String.format("Заголовок книги #%d", i);
|
||||
String bookTextPart = String.format("Это текст книги #%d", i);
|
||||
String bookText = bookTextPart.repeat( 8000 / bookTextPart.length());
|
||||
params.add(new MapSqlParameterSource("title", bookTitle)
|
||||
.addValue("bookText", bookText)
|
||||
.getValues()
|
||||
);
|
||||
}
|
||||
jdbc.batchUpdate("insert into books (title, book_text) values (:title, :bookText)",
|
||||
params.toArray(new Map[0]));
|
||||
}
|
||||
|
||||
private void insertBooksRelations(NamedParameterJdbcOperations jdbc) {
|
||||
List<Map<String, Object>> paramsForAuthors = new ArrayList<>(BOOKS_COUNT);
|
||||
List<Map<String, Object>> paramsForGenres = new ArrayList<>(BOOKS_COUNT);
|
||||
for (int i = 1; i <= BOOKS_COUNT; i++) {
|
||||
for (int j = 1; j <= 3; j++) {
|
||||
paramsForAuthors.add(new MapSqlParameterSource("bookId", i).addValue("authorId", j).getValues());
|
||||
paramsForGenres.add(new MapSqlParameterSource("bookId", i).addValue("genreId", j).getValues());
|
||||
}
|
||||
}
|
||||
jdbc.batchUpdate("insert into books_authors (book_id, author_id) values (:bookId, :authorId)",
|
||||
paramsForAuthors.toArray(new Map[0]));
|
||||
|
||||
jdbc.batchUpdate("insert into books_genres (book_id, genre_id) values (:bookId, :genreId)",
|
||||
paramsForGenres.toArray(new Map[0]));
|
||||
}
|
||||
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package ru.otus.dbproblemsdemo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/*
|
||||
SELECT * FROM AUTHORS;
|
||||
SELECT * FROM GENRES;
|
||||
SELECT * FROM BOOKS_AUTHORS ;
|
||||
SELECT * FROM BOOKS_GENRES;
|
||||
SELECT * FROM BOOKS;
|
||||
SELECT * FROM BOOKS_ALL_INFO ;
|
||||
*/
|
||||
|
||||
@SpringBootApplication
|
||||
public class DbProblemsDemoApplication {
|
||||
|
||||
public static void main(String[] args) throws SQLException {
|
||||
SpringApplication.run(DbProblemsDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
spring:
|
||||
datasource:
|
||||
#url: jdbc:h2:mem:testdb
|
||||
url: jdbc:postgresql://localhost:5432/problems
|
||||
username: postgres
|
||||
password: postgres
|
||||
driver-class-name: org.postgresql.Driver
|
||||
initialization-mode: never
|
||||
h2:
|
||||
console:
|
||||
enabled: true
|
||||
flyway:
|
||||
enabled: true
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
--date: 2002-10-10
|
||||
--author: stvort
|
||||
|
||||
create table authors (id bigserial, name varchar(50), primary key (id));
|
||||
create table genres (id bigserial, name varchar(50), primary key (id));
|
||||
create table books (id bigserial, title varchar(50), book_text varchar(8000), primary key (id));
|
||||
create table books_authors (book_id bigint not null references books (id) on delete cascade , author_id bigint not null references authors (id) on delete cascade);
|
||||
create table books_genres (book_id bigint not null references books (id) on delete cascade , genre_id bigint not null references genres (id) on delete cascade);
|
||||
|
||||
|
||||
create view books_all_info as
|
||||
select b.id as "ID книги",
|
||||
b.title as "Заголовок книги",
|
||||
b.book_text as "Текст книги",
|
||||
a.id as "ID автора",
|
||||
a.name as "Имя автора",
|
||||
g.id as "ID жанра",
|
||||
g.name as "Название жанра"
|
||||
from books b left join
|
||||
books_authors ba on b.id = ba.book_id left join
|
||||
books_genres bg on b.id = bg.book_id left join
|
||||
authors a on ba.author_id = a.id left join
|
||||
genres g on bg.genre_id = g.id;
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
insert into authors(id, name)
|
||||
values (1, 'Админ Супервайзерович Рут'),
|
||||
(2, 'Валентина Игоревна Априори'),
|
||||
(3, 'Питуний Дельфинович Автостопов'),
|
||||
(4, 'Инокентий Купертинович Жужа');
|
||||
|
||||
insert into genres(id, name)
|
||||
values (1, 'Детектив'),
|
||||
(2, 'Фантастика'),
|
||||
(3, 'Фэнтези'),
|
||||
(4, 'Техническая литература');
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
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/
|
||||
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.1.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>ru.otus.example</groupId>
|
||||
<artifactId>hibernate-fetch-mode-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>HibernateFetchModeDemo</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.shell</groupId>
|
||||
<artifactId>spring-shell-starter</artifactId>
|
||||
<version>2.0.1.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class HibernateFetchModeDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HibernateFetchModeDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "knowledge")
|
||||
public class Knowledge {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_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 = "mentors")
|
||||
public class Mentor {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@ManyToMany(targetEntity = Knowledge.class, cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "mentors_experience",
|
||||
joinColumns = @JoinColumn(name = "mentor_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "knowledge_id", referencedColumnName = "id"))
|
||||
private List<Knowledge> experience;
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_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 = "students")
|
||||
public class Student {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
//@BatchSize(size = 5)
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@ManyToMany(targetEntity = Knowledge.class, cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "students_experience",
|
||||
joinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "knowledge_id", referencedColumnName = "id"))
|
||||
private List<Knowledge> experience;
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "teachers")
|
||||
public class Teacher {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@ManyToMany(targetEntity = Knowledge.class, cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "teachers_experience",
|
||||
joinColumns = @JoinColumn(name = "teacher_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "knowledge_id", referencedColumnName = "id"))
|
||||
private List<Knowledge> experience;
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.repositories;
|
||||
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Mentor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MentorRepository {
|
||||
List<Mentor> findAll();
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.repositories;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Mentor;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public class MentorRepositoryImpl implements MentorRepository {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public List<Mentor> findAll() {
|
||||
return em.createQuery("select m from Mentor m join fetch m.experience", Mentor.class).getResultList();
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.repositories;
|
||||
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Student;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StudentRepository {
|
||||
List<Student> findAll();
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.repositories;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Student;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public class StudentRepositoryImpl implements StudentRepository {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public List<Student> findAll() {
|
||||
return em.createQuery("select s from Student s", Student.class).getResultList();
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.repositories;
|
||||
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Teacher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TeacherRepository {
|
||||
List<Teacher> findAll();
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.repositories;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Teacher;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public class TeacherRepositoryImpl implements TeacherRepository {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public List<Teacher> findAll() {
|
||||
return em.createQuery("select t from Teacher t", Teacher.class).getResultList();
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package ru.otus.example.hibernate_fetch_mode_demo.shell;
|
||||
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Mentor;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Student;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.models.Teacher;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.repositories.MentorRepository;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.repositories.StudentRepository;
|
||||
import ru.otus.example.hibernate_fetch_mode_demo.repositories.TeacherRepository;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@ShellComponent
|
||||
public class ShellCommands {
|
||||
|
||||
private static final String OFFSET = "\n\n----------------------------\n\n";
|
||||
|
||||
private final StudentRepository studentRepository;
|
||||
private final MentorRepository mentorRepository;
|
||||
private final TeacherRepository teacherRepository;
|
||||
|
||||
public ShellCommands(StudentRepository studentRepository, MentorRepository mentorRepository, TeacherRepository teacherRepository) {
|
||||
this.studentRepository = studentRepository;
|
||||
this.mentorRepository = mentorRepository;
|
||||
this.teacherRepository = teacherRepository;
|
||||
}
|
||||
|
||||
|
||||
@ShellMethod(value = "Show all students", key = "find-all-students")
|
||||
public String findAllStudents(){
|
||||
System.out.println();
|
||||
return OFFSET + studentRepository.findAll().stream().map(Student::toString).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
@ShellMethod(value = "Show all mentors", key = "find-all-mentors")
|
||||
public String findAllMentors(){
|
||||
System.out.println();
|
||||
return OFFSET + mentorRepository.findAll().stream().map(Mentor::toString).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
@ShellMethod(value = "Show all teachers", key = "find-all-teachers")
|
||||
public String findAllTeachers(){
|
||||
System.out.println();
|
||||
return OFFSET + teacherRepository.findAll().stream().map(Teacher::toString).collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
INSERT INTO knowledge (name) VALUES ('Spring Core'), ('Spring Data'), ('Spring MVC'), ('Spring Batch'), ('Spring Integration'), ('Spring WebFlux');
|
||||
|
||||
INSERT INTO students (name) VALUES ('Student #01'), ('Student #02'), ('Student #03'), ('Student #04'), ('Student #05'), ('Student #06'), ('Student #07'), ('Student #08'), ('Student #09');
|
||||
INSERT INTO mentors (name) VALUES ('Mentor #01'), ('Mentor #02'), ('Mentor #03'), ('Mentor #04'), ('Mentor #05'), ('Mentor #06'), ('Mentor #07'), ('Mentor #08'), ('Mentor #09');
|
||||
INSERT INTO teachers (name) VALUES ('Teacher #01'), ('Teacher #02'), ('Teacher #03'), ('Teacher #04'), ('Teacher #05'), ('Teacher #06'), ('Teacher #07'), ('Teacher #08'), ('Teacher #09');
|
||||
|
||||
INSERT INTO students_experience (student_id, knowledge_id) VALUES (1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (2, 5);
|
||||
INSERT INTO mentors_experience (mentor_id, knowledge_id) VALUES (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6);
|
||||
INSERT INTO teachers_experience (teacher_id, knowledge_id) VALUES(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6);
|
||||
@@ -1,48 +0,0 @@
|
||||
DROP TABLE IF EXISTS students_experience;
|
||||
DROP TABLE IF EXISTS mentors_experience;
|
||||
DROP TABLE IF EXISTS teachers_experience;
|
||||
DROP TABLE IF EXISTS students;
|
||||
DROP TABLE IF EXISTS mentors;
|
||||
DROP TABLE IF EXISTS teachers;
|
||||
DROP TABLE IF EXISTS knowledge;
|
||||
|
||||
CREATE TABLE knowledge (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE TABLE students (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE TABLE mentors (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE TABLE teachers (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE TABLE students_experience (
|
||||
student_id BIGINT,
|
||||
knowledge_id BIGINT,
|
||||
FOREIGN KEY(student_id) REFERENCES students(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(knowledge_id) REFERENCES knowledge(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE mentors_experience (
|
||||
mentor_id BIGINT,
|
||||
knowledge_id BIGINT,
|
||||
FOREIGN KEY(mentor_id) REFERENCES mentors(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(knowledge_id) REFERENCES knowledge(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE teachers_experience (
|
||||
teacher_id BIGINT,
|
||||
knowledge_id BIGINT,
|
||||
FOREIGN KEY(teacher_id) REFERENCES teachers(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(knowledge_id) REFERENCES knowledge(id) ON DELETE CASCADE
|
||||
);
|
||||
@@ -1,21 +0,0 @@
|
||||
version: "3"
|
||||
services:
|
||||
keycloak-container:
|
||||
image: quay.io/keycloak/keycloak:11.0.0
|
||||
|
||||
command:
|
||||
|
||||
-Djboss.socket.binding.port-offset=2
|
||||
|
||||
restart: always
|
||||
|
||||
environment:
|
||||
KEYCLOAK_USER: admin
|
||||
KEYCLOAK_PASSWORD: admin
|
||||
KEYCLOAK_IMPORT: /tmp/realm.json
|
||||
|
||||
ports:
|
||||
- 8082:8082
|
||||
|
||||
volumes:
|
||||
- ./realm.json:/tmp/realm.json
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>resource-app</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>resource-app</name>
|
||||
<description>KeyCloack resource app example</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.bom</groupId>
|
||||
<artifactId>keycloak-adapter-bom</artifactId>
|
||||
<version>11.0.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package ru.otus.resourceapp;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
//https://www.baeldung.com/spring-boot-keycloak
|
||||
//https://www.baeldung.com/keycloak-custom-user-attributes
|
||||
//https://github.com/keycloak/keycloak-quickstarts/tree/latest/app-authz-springboot
|
||||
@SpringBootApplication
|
||||
public class ResourceAppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ResourceAppApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package ru.otus.resourceapp.config;
|
||||
|
||||
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
|
||||
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
|
||||
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
|
||||
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
var keycloakAuthenticationProvider = keycloakAuthenticationProvider();
|
||||
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
|
||||
auth.authenticationProvider(keycloakAuthenticationProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
|
||||
return new KeycloakSpringBootConfigResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
|
||||
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
super.configure(http);
|
||||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/secret*").hasRole("user")
|
||||
.anyRequest().permitAll();
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package ru.otus.resourceapp.controllers;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
@RestController
|
||||
public class CommonController {
|
||||
|
||||
@GetMapping(path = "/")
|
||||
public String commonData() {
|
||||
return "Абсолютно свободные данные";
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@GetMapping(path = "/secret")
|
||||
public String secretData(Principal principal) {
|
||||
var authenticationToken = (KeycloakAuthenticationToken) principal;
|
||||
var kp = (KeycloakPrincipal) authenticationToken.getPrincipal();
|
||||
var token = kp.getKeycloakSecurityContext().getToken();
|
||||
|
||||
return "Жутко секретные данные для пользователя: " + token.getPreferredUsername() +
|
||||
", \n остальные данные: " + token.getOtherClaims();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
keycloak:
|
||||
auth-server-url: http://localhost:8082/auth
|
||||
realm: KCExample
|
||||
resource: resource-app
|
||||
public-client: true
|
||||
@@ -1,29 +0,0 @@
|
||||
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/
|
||||
@@ -1,7 +0,0 @@
|
||||
## Пример работы с MongoDB
|
||||
|
||||
В примере демонстрируется:
|
||||
* *два подхода к хранению вложенных сущностей*
|
||||
* *инициализация базы данными с помощью инструмента миграций Mongock*
|
||||
* *использование AbstractMongoEventListener для выполнения каскадных операций*
|
||||
* *работа с массивами с помощью агрегаций и Criteria api*
|
||||
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>ru.otus.example</groupId>
|
||||
<artifactId>mongo-db-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>mongo-db-demo</name>
|
||||
<description>Demo project for MongoDB</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<maven.compiler.sourcre>11</maven.compiler.sourcre>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<mongock.version>3.2.8</mongock.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.cloudyrock.mongock</groupId>
|
||||
<artifactId>mongock-spring</artifactId>
|
||||
<version>${mongock.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Тестирование-->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableConfigurationProperties
|
||||
public class MongoDbDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MongoDbDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.changelogs;
|
||||
|
||||
import com.github.cloudyrock.mongock.ChangeLog;
|
||||
import com.github.cloudyrock.mongock.ChangeSet;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import lombok.val;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
import ru.otus.example.mongodbdemo.model.Teacher;
|
||||
|
||||
@ChangeLog(order = "001")
|
||||
public class InitMongoDBDataChangeLog {
|
||||
|
||||
private Knowledge springDataKnowledge;
|
||||
private Knowledge mongockKnowledge;
|
||||
private Knowledge aggregationApiKnowledge;
|
||||
|
||||
@ChangeSet(order = "000", id = "dropDB", author = "stvort", runAlways = true)
|
||||
public void dropDB(MongoDatabase database){
|
||||
database.drop();
|
||||
}
|
||||
|
||||
@ChangeSet(order = "001", id = "initKnowledges", author = "stvort", runAlways = true)
|
||||
public void initKnowledges(MongoTemplate template){
|
||||
springDataKnowledge = template.save(new Knowledge("Spring Data"));
|
||||
mongockKnowledge = template.save(new Knowledge("Mongock"));
|
||||
aggregationApiKnowledge = template.save(new Knowledge("AggregationApi"));
|
||||
}
|
||||
|
||||
@ChangeSet(order = "002", id = "initStudents", author = "stvort", runAlways = true)
|
||||
public void initStudents(MongoTemplate template){
|
||||
template.save(new Student("Student #1", springDataKnowledge, mongockKnowledge));
|
||||
}
|
||||
|
||||
@ChangeSet(order = "003", id = "Teacher", author = "stvort", runAlways = true)
|
||||
public void initTeachers(MongoTemplate template){
|
||||
val teacher = new Teacher("Teacher #1", springDataKnowledge, mongockKnowledge, aggregationApiKnowledge);
|
||||
template.save(teacher);
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.config;
|
||||
|
||||
import com.github.cloudyrock.mongock.Mongock;
|
||||
import com.github.cloudyrock.mongock.SpringMongockBuilder;
|
||||
import com.mongodb.MongoClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class ApplicationConfig {
|
||||
|
||||
private static final String CHANGELOGS_PACKAGE = "ru.otus.example.mongodbdemo.changelogs";
|
||||
|
||||
@Bean
|
||||
public Mongock mongock(MongoProps mongoProps, MongoClient mongoClient) {
|
||||
return new SpringMongockBuilder(mongoClient, mongoProps.getDatabase(), CHANGELOGS_PACKAGE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties("spring.data.mongodb")
|
||||
public class MongoProps {
|
||||
private int port;
|
||||
private String database;
|
||||
private String uri;
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.events;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MongoKnowledgeCascadeDeleteEventsListener extends AbstractMongoEventListener<Knowledge> {
|
||||
|
||||
private final StudentRepository studentRepository;
|
||||
|
||||
@Override
|
||||
public void onAfterDelete(AfterDeleteEvent<Knowledge> event) {
|
||||
super.onAfterDelete(event);
|
||||
val source = event.getSource();
|
||||
val id = source.get("_id").toString();
|
||||
studentRepository.removeExperienceArrayElementsById(id);
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.events;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
|
||||
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MongoStudentCascadeSaveEventsListener extends AbstractMongoEventListener<Student> {
|
||||
|
||||
private final KnowledgeRepository knowledgeRepository;
|
||||
|
||||
@Override
|
||||
public void onBeforeConvert(BeforeConvertEvent<Student> event) {
|
||||
super.onBeforeConvert(event);
|
||||
val student = event.getSource();
|
||||
if (student.getExperience() != null) {
|
||||
student.getExperience().stream().filter(e -> Objects.isNull(e.getId())).forEach(knowledgeRepository::save);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
public class Knowledge {
|
||||
@Id
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
public Knowledge(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
public class Student {
|
||||
@Id
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
@DBRef
|
||||
private List<Knowledge> experience;
|
||||
|
||||
public Student(String name, Knowledge... experience) {
|
||||
this.name = name;
|
||||
this.experience = Arrays.asList(experience);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
public class Teacher {
|
||||
@Id
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
private List<Knowledge> experience;
|
||||
|
||||
public Teacher(String name, Knowledge... experience) {
|
||||
this.name = name;
|
||||
this.experience = Arrays.asList(experience);
|
||||
|
||||
}
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
|
||||
public interface KnowledgeRepository extends MongoRepository<Knowledge, String> {
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
|
||||
|
||||
public interface StudentRepository extends MongoRepository<Student, String>, StudentRepositoryCustom {
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StudentRepositoryCustom {
|
||||
List<Knowledge> getStudentExperienceById(String studentId);
|
||||
|
||||
long getExperienceArrayLengthByStudentId(String id);
|
||||
void removeExperienceArrayElementsById(String id);
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
import ru.otus.example.mongodbdemo.utils.RawResultPrinter;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.ObjectOperators.ObjectToArray.valueOfToArray;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class StudentRepositoryCustomImpl implements StudentRepositoryCustom {
|
||||
|
||||
@Data
|
||||
private class ArraySizeProjection {
|
||||
private int size;
|
||||
}
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
private final RawResultPrinter rawResultPrinter;
|
||||
|
||||
@Override
|
||||
public List<Knowledge> getStudentExperienceById(String studentId) {
|
||||
Aggregation aggregation = newAggregation(
|
||||
match(Criteria.where("id").is(studentId))
|
||||
, unwind("experience")
|
||||
, project().andExclude("_id").and(valueOfToArray("experience")).as("experience_map")
|
||||
, project().and("experience_map").arrayElementAt(1).as("experience_id_map")
|
||||
, project().and("experience_id_map.v").as("experience_id")
|
||||
, lookup("knowledge", "experience_id", "_id", "experience")
|
||||
, project().and("experience._id").as("_id").and("experience.name").as("name")
|
||||
);
|
||||
|
||||
Document rawResults = mongoTemplate.aggregate(aggregation, Student.class, Knowledge.class).getRawResults();
|
||||
rawResultPrinter.prettyPrintRawResult(rawResults);
|
||||
return mongoTemplate.aggregate(aggregation, Student.class, Knowledge.class).getMappedResults();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExperienceArrayLengthByStudentId(String studentId) {
|
||||
val aggregation = Aggregation.newAggregation(
|
||||
match(where("id").is(studentId)),
|
||||
project().andExclude("_id").and("experience").size().as("size"));
|
||||
|
||||
val arraySizeProjection = mongoTemplate.aggregate(aggregation, Student.class, ArraySizeProjection.class).getUniqueMappedResult();
|
||||
return arraySizeProjection == null ? 0 : arraySizeProjection.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExperienceArrayElementsById(String id) {
|
||||
val query = Query.query(Criteria.where("$id").is(new ObjectId(id)));
|
||||
val update = new Update().pull("experience", query);
|
||||
mongoTemplate.updateMulti(new Query(), update, Student.class);
|
||||
}
|
||||
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import ru.otus.example.mongodbdemo.model.Teacher;
|
||||
|
||||
public interface TeacherRepository extends MongoRepository<Teacher, String>, TeacherRepositoryCustom {
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TeacherRepositoryCustom {
|
||||
List<Knowledge> getTeacherExperienceById(String teacherId);
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.model.Teacher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class TeacherRepositoryCustomImpl implements TeacherRepositoryCustom {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Override
|
||||
public List<Knowledge> getTeacherExperienceById(String teacherId) {
|
||||
val aggregation = newAggregation(
|
||||
match(Criteria.where("id").is(teacherId))
|
||||
, unwind("experience")
|
||||
, project().andExclude("_id").and("experience.id").as("_id").and("experience.name").as("name")
|
||||
);
|
||||
return mongoTemplate.aggregate(aggregation, Teacher.class, Knowledge.class).getMappedResults();
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.utils;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
public interface RawResultPrinter {
|
||||
@SuppressWarnings("unchecked")
|
||||
void prettyPrintRawResult(Document document);
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.utils;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class RawResultPrinterImpl implements RawResultPrinter {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void prettyPrintRawResult(Document document) {
|
||||
List<Document> results = (List<Document>) document.get("results");
|
||||
String resultsAsString = results.stream().map(Document::toString).collect(Collectors.joining("\n"));
|
||||
System.out.println(resultsAsString);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
spring:
|
||||
data:
|
||||
mongodb:
|
||||
uri: mongodb://localhost
|
||||
port: 27017
|
||||
database: awesomeMongo
|
||||
logging:
|
||||
level:
|
||||
root: ERROR
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import ru.otus.example.mongodbdemo.utils.RawResultPrinterImpl;
|
||||
|
||||
@DataMongoTest
|
||||
@EnableConfigurationProperties
|
||||
@ComponentScan({"ru.otus.example.mongodbdemo.config", "ru.otus.example.mongodbdemo.repositories"})
|
||||
@Import(RawResultPrinterImpl.class)
|
||||
public abstract class AbstractRepositoryTest {
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories;
|
||||
|
||||
import lombok.val;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("TeacherRepository должен ")
|
||||
class TeacherRepositoryTest extends AbstractRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private TeacherRepository teacherRepository;
|
||||
|
||||
@DisplayName(" возвращать корректный список знаний преподавателя")
|
||||
@Test
|
||||
void shouldReturnCorrectKnowledgeList(){
|
||||
val teachers = teacherRepository.findAll();
|
||||
val teacher = teachers.get(0);
|
||||
val experience = teacher.getExperience();
|
||||
assertThat(experience).isNotNull().hasSize(3);
|
||||
|
||||
val actualExperience = teacherRepository.getTeacherExperienceById(teacher.getId());
|
||||
assertThat(actualExperience).containsExactlyInAnyOrder(experience.toArray(new Knowledge[0]));
|
||||
|
||||
}
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories.plain;
|
||||
|
||||
import lombok.val;
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
import org.assertj.core.api.AbstractOptionalAssert;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
|
||||
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
|
||||
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
@DisplayName("KnowledgeRepository при отсутствии listener-ов в контексте ")
|
||||
class KnowledgeRepositoryWithoutListenerTest extends AbstractRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private KnowledgeRepository knowledgeRepository;
|
||||
|
||||
@Autowired
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
@DisplayName("при удалении Knowledge не должен удалять его из опыта студента")
|
||||
@Test
|
||||
void shouldLeaveKnowledgeInStudentExperienceWhenRemovingKnowledge() {
|
||||
|
||||
// Загрузка студента и его первого знания
|
||||
val students = studentRepository.findAll();
|
||||
val student = students.get(0);
|
||||
val experience = student.getExperience();
|
||||
val firstKnowledge = experience.get(0);
|
||||
|
||||
// Удаление знания из коллекции знаний
|
||||
knowledgeRepository.delete(firstKnowledge);
|
||||
|
||||
// Загружаем студента заново и проверяем, что знание действительно удалено (размер стал меньше на 1)
|
||||
val expectedExperienceArrayLength = experience.size() - 1;
|
||||
val actualStudentOptional = studentRepository.findById(student.getId());
|
||||
assertThat(actualStudentOptional)
|
||||
.isNotEmpty().get()
|
||||
.matches(s -> s.getExperience() != null && s.getExperience().size() == expectedExperienceArrayLength);
|
||||
|
||||
|
||||
// Загружаем размер массива с помощью аггрегаций и проверяем, что на самом деле размер массива в БД не изменился
|
||||
val actualExperienceArrayLength = studentRepository.getExperienceArrayLengthByStudentId(student.getId());
|
||||
assertThat(actualExperienceArrayLength).isNotEqualTo(expectedExperienceArrayLength);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories.plain;
|
||||
|
||||
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.data.mapping.MappingException;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
|
||||
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
@DisplayName("StudentRepository при отсутствии listener-ов в контексте ")
|
||||
class StudentRepositoryWithoutListenerTest extends AbstractRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
@DisplayName("должен кидать MappingException во время сохранения студента с отсутствующими в БД знаниями")
|
||||
@Test
|
||||
void shouldThrowMappingExceptionWhenSaveStudentWithNewKnowledge(){
|
||||
val student = new Student("Student #2", new Knowledge("Knowledge #3"));
|
||||
assertThatThrownBy(() -> studentRepository.save(student)).isInstanceOf(MappingException.class);
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories.withlisteners;
|
||||
|
||||
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.context.annotation.ComponentScan;
|
||||
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
|
||||
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
|
||||
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("KnowledgeRepository при наличии listener-ов в контексте ")
|
||||
@ComponentScan("ru.otus.example.mongodbdemo.events")
|
||||
class KnowledgeRepositoryWithListenerTest extends AbstractRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private KnowledgeRepository knowledgeRepository;
|
||||
|
||||
@Autowired
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
@DisplayName("при удалении Knowledge должен удалить его из опыта студента")
|
||||
@Test
|
||||
void shouldRemoveKnowledgeFromStudentExperienceWhenRemovingKnowledge() {
|
||||
|
||||
// Загрузка студента и его первого знания
|
||||
val students = studentRepository.findAll();
|
||||
val student = students.get(0);
|
||||
val experience = student.getExperience();
|
||||
val firstKnowledge = experience.get(0);
|
||||
|
||||
// Удаление знания из коллекции знаний
|
||||
knowledgeRepository.delete(firstKnowledge);
|
||||
|
||||
// Загружаем студента заново и проверяем, что знание действительно удалено (размер стал меньше на 1)
|
||||
val expectedExperienceArrayLength = experience.size() - 1;
|
||||
val actualStudentOptional = studentRepository.findById(student.getId());
|
||||
assertThat(actualStudentOptional)
|
||||
.isNotEmpty().get()
|
||||
.matches(s -> s.getExperience() != null && s.getExperience().size() == expectedExperienceArrayLength);
|
||||
|
||||
// Загружаем размер массива с помощью аггрегаций и проверяем, что размер массива в БД тоже изменился
|
||||
val actualExperienceArrayLength = studentRepository.getExperienceArrayLengthByStudentId(student.getId());
|
||||
assertThat(actualExperienceArrayLength).isEqualTo(expectedExperienceArrayLength);
|
||||
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package ru.otus.example.mongodbdemo.repositories.withlisteners;
|
||||
|
||||
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.context.annotation.ComponentScan;
|
||||
import ru.otus.example.mongodbdemo.model.Knowledge;
|
||||
import ru.otus.example.mongodbdemo.model.Student;
|
||||
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
|
||||
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("StudentRepository при наличии listener-ов в контексте ")
|
||||
@ComponentScan("ru.otus.example.mongodbdemo.events")
|
||||
class StudentRepositoryWithListenersTest extends AbstractRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
@DisplayName("должен корректно сохранять студента с отсутствующими в БД знаниями")
|
||||
@Test
|
||||
void shouldCorrectSaveStudentWithNewKnowledge(){
|
||||
val student = new Student("Student #2", new Knowledge("Knowledge #3"));
|
||||
val actual = studentRepository.save(student);
|
||||
assertThat(actual.getId()).isNotNull();
|
||||
assertThat(actual.getName()).isEqualTo(student.getName());
|
||||
}
|
||||
|
||||
@DisplayName("должен возвращать корректный список знаний студента")
|
||||
@Test
|
||||
void shouldReturnCorrectStudentKnowledgeList(){
|
||||
val student = studentRepository.findAll().get(0);
|
||||
List<Knowledge> knowledgeList = studentRepository.getStudentExperienceById(student.getId());
|
||||
assertThat(knowledgeList).containsExactlyInAnyOrderElementsOf(student.getExperience());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
spring:
|
||||
data:
|
||||
mongodb:
|
||||
port: 0
|
||||
database: test
|
||||
#uri: mongodb://localhost
|
||||
#port: 27017
|
||||
#database: awesomeMongo
|
||||
logging:
|
||||
level:
|
||||
root: ERROR
|
||||
@@ -1,4 +0,0 @@
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
target/
|
||||
@@ -1,6 +0,0 @@
|
||||
## Пример аутентификации/авторизации с помощью OAuth2 сервера и JWT
|
||||
|
||||
* *В примере есть проект authorization-server, призванный выдавать jwt токены в ответ на корректный POST запрос по http://localhost:8090/oauth/token *
|
||||
* *В теле такого запроса нужно передать client_id, grant_type, имя и пароль пользователя пользователя пытающегося выполнить вход*
|
||||
* *Далее, полученный токен XXX нужно прикладывать к каждому запросу к приложению из проекта resource-server в виде загловка "Authorization Bearer XXX"*
|
||||
* *Для демонстрации работы примера нужно запустить приложение authorization-server и следом изучить работу E2E теста в проекте resource-server*
|
||||
@@ -1,31 +0,0 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
### 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/
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>authorization-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>authorization-server</name>
|
||||
<description>Authorization server demo</description>
|
||||
|
||||
<properties>
|
||||
<java.version>13</java.version>
|
||||
<maven.compiler.source>13</maven.compiler.source>
|
||||
<maven.compiler.target>13</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
<version>2.5.0.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-jwt</artifactId>
|
||||
<version>1.1.1.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package ru.otus.authorizationserver;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
@EnableConfigurationProperties
|
||||
@SpringBootApplication
|
||||
public class AuthorizationServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthorizationServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package ru.otus.authorizationserver.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import ru.otus.authorizationserver.services.CustomTokenEnhancer;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration
|
||||
@EnableAuthorizationServer
|
||||
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||
|
||||
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 60 * 60 * 3;
|
||||
private static final int REFRESH_TOKEN_VALIDITY_SECONDS = -1;
|
||||
|
||||
@Autowired
|
||||
private OAuthProps oAuthProps;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authManager;
|
||||
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
|
||||
@Autowired
|
||||
private JwtAccessTokenConverter jwtAccessTokenConverter;
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
|
||||
tokenEnhancerChain.setTokenEnhancers(List.of(new CustomTokenEnhancer(), jwtAccessTokenConverter));
|
||||
endpoints.authenticationManager(authManager)
|
||||
.tokenEnhancer(tokenEnhancerChain)
|
||||
.tokenStore(tokenStore)
|
||||
.reuseRefreshTokens(false)
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
clients
|
||||
.inMemory()
|
||||
.withClient(oAuthProps.getClientId())
|
||||
.secret(oAuthProps.getClientSecret())
|
||||
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
|
||||
.scopes("read", "write")
|
||||
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
|
||||
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package ru.otus.authorizationserver.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "app.oauth2")
|
||||
public class OAuthProps {
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private String signingKey;
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package ru.otus.authorizationserver.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService);
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package ru.otus.authorizationserver.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration
|
||||
public class TokenStoreConfig {
|
||||
|
||||
@Autowired
|
||||
private OAuthProps oAuthProps;
|
||||
|
||||
@Bean
|
||||
public TokenStore tokenStore() {
|
||||
return new JwtTokenStore(jwtAccessTokenConverter());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAccessTokenConverter jwtAccessTokenConverter() {
|
||||
var converter = new JwtAccessTokenConverter();
|
||||
converter.setSigningKey(oAuthProps.getSigningKey());
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package ru.otus.authorizationserver.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class CustomUser extends User {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String firstName;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String fatherName;
|
||||
|
||||
public CustomUser(String firstName, String fatherName,
|
||||
String username, String password,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
super(username, password, authorities);
|
||||
this.firstName = firstName;
|
||||
this.fatherName = fatherName;
|
||||
}
|
||||
|
||||
public CustomUser(CustomUser user) {
|
||||
super(user.getUsername(), user.getPassword(), user.getAuthorities());
|
||||
this.firstName = user.getFirstName();
|
||||
this.fatherName = user.getFatherName();
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package ru.otus.authorizationserver.services;
|
||||
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import ru.otus.authorizationserver.model.CustomUser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CustomTokenEnhancer implements TokenEnhancer {
|
||||
public static final String KEY_FIRST_NAME = "firstName";
|
||||
public static final String KEY_FATHER_NAME = "fatherName";
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
|
||||
CustomUser user = (CustomUser) authentication.getPrincipal();
|
||||
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(Map.of(
|
||||
KEY_FIRST_NAME, user.getFirstName(),
|
||||
KEY_FATHER_NAME, user.getFatherName()
|
||||
));
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package ru.otus.authorizationserver.services;
|
||||
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.otus.authorizationserver.model.CustomUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class InMemoryUserDetailsService implements UserDetailsService {
|
||||
|
||||
private final Map<String, CustomUser> userMap;
|
||||
|
||||
public InMemoryUserDetailsService(PasswordEncoder passwordEncoder) {
|
||||
userMap = Map.of(
|
||||
"user1", new CustomUser("Василий", "Григорьевич",
|
||||
"user1",
|
||||
passwordEncoder.encode("user1"),
|
||||
List.of(new SimpleGrantedAuthority("ADMIN"))),
|
||||
|
||||
"user2", new CustomUser("Ираклий", "Спиридонович",
|
||||
"user2",
|
||||
passwordEncoder.encode("user2"),
|
||||
List.of(new SimpleGrantedAuthority("USER")))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
return Optional.ofNullable(userMap.get(username)).map(CustomUser::new)
|
||||
.orElseThrow(() -> new UsernameNotFoundException(String.format("User %s not found", username)));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
server:
|
||||
port: 8090
|
||||
|
||||
app:
|
||||
oauth2:
|
||||
client-id: "any_id"
|
||||
client-secret: "any_secret"
|
||||
signing-key: 987654321
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>oauth2-example</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>authorization-server</module>
|
||||
<module>resource-server</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -1,31 +0,0 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
### 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/
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>resource-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>resource-server</name>
|
||||
<description>Resource server demo</description>
|
||||
|
||||
<properties>
|
||||
<java.version>13</java.version>
|
||||
<maven.compiler.source>13</maven.compiler.source>
|
||||
<maven.compiler.target>13</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
<version>2.5.0.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-jwt</artifactId>
|
||||
<version>1.1.1.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package ru.otus.resourceserver;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
@EnableConfigurationProperties
|
||||
@SpringBootApplication
|
||||
public class ResourceServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ResourceServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package ru.otus.resourceserver.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "app.oauth2")
|
||||
public class OAuthProps {
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private String signingKey;
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package ru.otus.resourceserver.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@EnableResourceServer
|
||||
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private OAuthProps oAuthProps;
|
||||
|
||||
@Bean
|
||||
public TokenStore tokenStore() {
|
||||
return new JwtTokenStore(jwtAccessTokenConverter());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAccessTokenConverter jwtAccessTokenConverter() {
|
||||
var converter = new JwtAccessTokenConverter();
|
||||
converter.setSigningKey(oAuthProps.getSigningKey());
|
||||
return converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
|
||||
resources.tokenStore(tokenStore())
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
http.anonymous().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/api/**").authenticated()
|
||||
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
|
||||
}
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package ru.otus.resourceserver.controllers;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.otus.resourceserver.models.CurrentUserRequestResult;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
public class ApiController {
|
||||
|
||||
public static final String KEY_FIRST_NAME = "firstName";
|
||||
public static final String KEY_FATHER_NAME = "fatherName";
|
||||
|
||||
private final TokenStore tokenStore;
|
||||
|
||||
@GetMapping("api/current-user")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PreAuthorize("hasAuthority('ADMIN')")
|
||||
public CurrentUserRequestResult getCurrentUser(Authentication authentication) {
|
||||
|
||||
Map<String, Object> info = getAdditionalInfo(authentication);
|
||||
return new CurrentUserRequestResult((String) authentication.getPrincipal(),
|
||||
(String) info.get(KEY_FIRST_NAME),
|
||||
(String) info.get(KEY_FATHER_NAME));
|
||||
}
|
||||
|
||||
private Map<String, Object> getAdditionalInfo(Authentication authentication) {
|
||||
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
|
||||
OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());
|
||||
return accessToken.getAdditionalInformation();
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package ru.otus.resourceserver.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CurrentUserRequestResult {
|
||||
private String userName;
|
||||
private String firstName;
|
||||
private String fatherName;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
app:
|
||||
oauth2:
|
||||
client-id: "any_id"
|
||||
client-secret: "any_secret"
|
||||
signing-key: 987654321
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package ru.otus.resourceserver;
|
||||
|
||||
import lombok.Data;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.*;
|
||||
import ru.otus.resourceserver.config.OAuthProps;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
class ResourceServerApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private OAuthProps oAuthProps;
|
||||
|
||||
@Data
|
||||
static class TokenResponse {
|
||||
private String access_token;
|
||||
private String token_type;
|
||||
private String refresh_token;
|
||||
private String expires_in;
|
||||
private String scope;
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSuccessfullyAccessTheEndpointAfterLoginOnAuthorizationServer() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
headers.setBasicAuth(oAuthProps.getClientId(), oAuthProps.getClientSecret());
|
||||
|
||||
ResponseEntity<TokenResponse> tokenResponse = restTemplate.postForEntity(
|
||||
"http://localhost:8090/oauth/token?client_id=" + oAuthProps.getClientId() +
|
||||
"&grant_type=password&username=user1&password=user1",
|
||||
new HttpEntity<>(headers),
|
||||
TokenResponse.class);
|
||||
|
||||
|
||||
/*
|
||||
System.out.println(tokenResponse.getStatusCode());
|
||||
System.out.println(tokenResponse.getHeaders());
|
||||
System.out.println(tokenResponse.getBody());
|
||||
*/
|
||||
assertThat(tokenResponse.getBody()).isNotNull();
|
||||
|
||||
headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(tokenResponse.getBody().getAccess_token());
|
||||
|
||||
|
||||
ResponseEntity<String> currentUserResponse = restTemplate.exchange(
|
||||
"http://localhost:8080/api/current-user",
|
||||
HttpMethod.GET,
|
||||
new HttpEntity<>(headers),
|
||||
String.class);
|
||||
|
||||
System.out.println(currentUserResponse.getStatusCode());
|
||||
System.out.println(currentUserResponse.getHeaders());
|
||||
System.out.println(currentUserResponse.getBody());
|
||||
assertThat(currentUserResponse.getBody())
|
||||
.isNotNull().isEqualTo("{\"userName\":\"user1\",\"firstName\":\"Василий\",\"fatherName\":\"Григорьевич\"}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>readonly-transaction-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>readonly-transaction-demo</name>
|
||||
<description>Readonly transaction demo</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package ru.otus.demo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ReadonlyTransactionDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ReadonlyTransactionDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package ru.otus.demo.model;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package ru.otus.demo.model;
|
||||
|
||||
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(cascade = CascadeType.ALL)
|
||||
private Email email;
|
||||
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package ru.otus.demo.repository;
|
||||
|
||||
import ru.otus.demo.model.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PersonRepository {
|
||||
|
||||
Person save(Person person);
|
||||
|
||||
List<Person> findAll();
|
||||
Person findById(long id);
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package ru.otus.demo.repository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.hibernate.Session;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.demo.model.Person;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PersonRepositoryJpa implements PersonRepository {
|
||||
|
||||
@PersistenceContext
|
||||
private final EntityManager em;
|
||||
|
||||
@Override
|
||||
public Person save(Person person) {
|
||||
if (person.getId() == 0) {
|
||||
Person savedPerson = new Person(person.getId(), person.getName(), person.getEmail());
|
||||
em.persist(savedPerson);
|
||||
return savedPerson;
|
||||
|
||||
}
|
||||
return em.merge(person);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Person> findAll() {
|
||||
TypedQuery<Person> query = em.createQuery("select p from Person p", Person.class);
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Person findById(long id) {
|
||||
System.out.println("FlushMode: " + em.unwrap(Session.class).getHibernateFlushMode());
|
||||
return em.find(Person.class, id);
|
||||
/*
|
||||
TypedQuery<Person> query = em.createQuery("select p from Person p where p.id = :id", Person.class);
|
||||
query.setParameter("id", id);
|
||||
return query.getSingleResult();
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package ru.otus.demo.services;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.otus.demo.model.Person;
|
||||
import ru.otus.demo.repository.PersonRepository;
|
||||
|
||||
@Service
|
||||
public class PersonService {
|
||||
private final PersonRepository personRepository;
|
||||
|
||||
public PersonService(PersonRepository personRepository) {
|
||||
this.personRepository = personRepository;
|
||||
}
|
||||
|
||||
public Person findById(long id) {
|
||||
return personRepository.findById(id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void save(Person person){
|
||||
personRepository.save(person);
|
||||
}
|
||||
|
||||
public void updateWithoutTran(long id, String name) {
|
||||
Person person = personRepository.findById(id);
|
||||
person.setName(name);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateWithNormalTran(long id, String name) {
|
||||
Person person = personRepository.findById(id);
|
||||
person.setName(name);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public void updateWithReadonlyTran(long id, String name) {
|
||||
Person person = personRepository.findById(id);
|
||||
person.setName(name);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
spring:
|
||||
datasource:
|
||||
#url: jdbc:postgresql://localhost:5432/persons
|
||||
#username: postgres
|
||||
#password: postgres
|
||||
|
||||
url: jdbc:h2:mem:testdb
|
||||
|
||||
initialization-mode: always
|
||||
|
||||
jpa:
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
#format_sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
org.hibernate.engine.transaction.internal: DEBUG
|
||||
@@ -1,17 +0,0 @@
|
||||
drop table if exists person;
|
||||
drop table if exists email;
|
||||
|
||||
|
||||
create table email (
|
||||
id bigserial,
|
||||
address varchar(200),
|
||||
primary key(id)
|
||||
);
|
||||
|
||||
create table person (
|
||||
id bigserial,
|
||||
email_id bigint references email(id),
|
||||
name varchar(200),
|
||||
primary key(id)
|
||||
);
|
||||
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
package ru.otus.demo.services;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.otus.demo.model.Email;
|
||||
import ru.otus.demo.model.Person;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
|
||||
class PersonServiceTest {
|
||||
|
||||
@Autowired
|
||||
private PersonService personService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
personService.save(new Person(0, "Igor", new Email(0, "noname@nomail.ru")));
|
||||
}
|
||||
|
||||
/*
|
||||
// Изменение имени пёрсона внутри updateWithNormalTran ПОПАДАЮТ в БД после закрытия сессии/транзакции
|
||||
// Работаем в обычной транзакции (в логах есть ее начало и коммит, а так же update)
|
||||
// FlushMode: AUTO (есть в логах)
|
||||
|
||||
2021-07-24 19:21:16.310 DEBUG 8636 --- [ main] o.h.e.t.internal.TransactionImpl : begin
|
||||
Hibernate: select person0_.id as id1_1_, person0_.email_id as email_id3_1_, person0_.name as name2_1_ from person person0_ where person0_.id=?
|
||||
Hibernate: select email0_.id as id1_0_0_, email0_.address as address2_0_0_ from email email0_ where email0_.id=?
|
||||
2021-07-24 19:21:16.390 DEBUG 8636 --- [ main] o.h.e.t.internal.TransactionImpl : committing
|
||||
Hibernate: update person set email_id=?, name=? where id=?
|
||||
*/
|
||||
@Test
|
||||
void updateWithNormalTran() {
|
||||
personService.updateWithNormalTran(1, "Vasya");
|
||||
Person actualPerson = personService.findById(1);
|
||||
assertThat(actualPerson).extracting(Person::getName).isEqualTo("Vasya");
|
||||
}
|
||||
|
||||
/*
|
||||
// Работаем в readonly транзакции (в логах есть ее начало и коммит)
|
||||
// Изменение имени пёрсона внутри updateWithReadonlyTran НЕ попадают в БД после закрытия сессии/транзакции
|
||||
// FlushMode: MANUAL (есть в логах), поэтому изменения и не попадают
|
||||
|
||||
2021-07-24 19:15:22.889 DEBUG 11024 --- [ main] o.h.e.t.internal.TransactionImpl : begin
|
||||
Hibernate: select person0_.id as id1_1_, person0_.email_id as email_id3_1_, person0_.name as name2_1_ from person person0_ where person0_.id=?
|
||||
Hibernate: select email0_.id as id1_0_0_, email0_.address as address2_0_0_ from email email0_ where email0_.id=?
|
||||
2021-07-24 19:15:22.967 DEBUG 11024 --- [ main] o.h.e.t.internal.TransactionImpl : committing
|
||||
|
||||
*/
|
||||
@Test
|
||||
void updateWithReadonlyTran() {
|
||||
personService.updateWithReadonlyTran(1, "Vasya");
|
||||
Person actualPerson = personService.findById(1);
|
||||
assertThat(actualPerson).extracting(Person::getName).isEqualTo("Igor");
|
||||
}
|
||||
|
||||
/*
|
||||
// Работаем без транзакции (в логах нет ее начала и коммита)
|
||||
// Изменение имени пёрсона внутри updateWithoutTran НЕ попадают в БД после закрытия сессии/транзакции
|
||||
// FlushMode: AUTO (есть в логах)
|
||||
|
||||
Hibernate: select person0_.id as id1_1_, person0_.email_id as email_id3_1_, person0_.name as name2_1_ from person person0_ where person0_.id=?
|
||||
Hibernate: select email0_.id as id1_0_0_, email0_.address as address2_0_0_ from email email0_ where email0_.id=?
|
||||
*/
|
||||
@Test
|
||||
void updateWithoutTran() {
|
||||
personService.updateWithoutTran(1, "Vasya");
|
||||
Person actualPerson = personService.findById(1);
|
||||
assertThat(actualPerson).extracting(Person::getName).isEqualTo("Igor");
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
package ru.otus.demo.services;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import ru.otus.demo.model.Email;
|
||||
import ru.otus.demo.model.Person;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
|
||||
class TransactionsTest {
|
||||
|
||||
private static final Person expectedPerson = new Person(1L, "Igor",
|
||||
new Email(1L, "noname@nomail.ru"));
|
||||
|
||||
@Autowired
|
||||
private SessionFactory sf;
|
||||
|
||||
@Autowired
|
||||
private Session s;
|
||||
|
||||
@Autowired
|
||||
private TransactionTemplate tm;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
tm.setReadOnly(false);
|
||||
tm.execute(status -> {
|
||||
s.persist(new Person(0, expectedPerson.getName(),
|
||||
new Email(0, expectedPerson.getEmail().getAddress())));
|
||||
return null;
|
||||
});
|
||||
|
||||
sf.getStatistics().setStatisticsEnabled(true);
|
||||
sf.getStatistics().clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findWithNormalTran() {
|
||||
tm.setReadOnly(false);
|
||||
Person actualPerson = tm.execute(status -> {
|
||||
assertThat(s.getHibernateFlushMode()).isEqualTo(FlushMode.AUTO);
|
||||
return s.find(Person.class, 1L);
|
||||
});
|
||||
assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
|
||||
assertThat(sf.getStatistics().getTransactionCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findWithReadOnlyTran() {
|
||||
tm.setReadOnly(true);
|
||||
Person actualPerson = tm.execute(status -> {
|
||||
assertThat(s.getHibernateFlushMode()).isEqualTo(FlushMode.MANUAL);
|
||||
return s.find(Person.class, 1L);
|
||||
});
|
||||
assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
|
||||
assertThat(sf.getStatistics().getTransactionCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findWithoutTran() {
|
||||
assertThat(s.getHibernateFlushMode()).isEqualTo(FlushMode.AUTO);
|
||||
Person actualPerson = s.find(Person.class, 1L);
|
||||
assertThat(actualPerson).usingRecursiveComparison().isEqualTo(expectedPerson);
|
||||
assertThat(sf.getStatistics().getTransactionCount()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user