mirror of
https://github.com/OtusTeam/Spring.git
synced 2026-05-30 10:50:42 +00:00
spring-07 examples added
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# application-events-demo
|
||||
Пример работы с событиями
|
||||
@@ -0,0 +1,2 @@
|
||||
# beans-lifecycle-demo
|
||||
Пример жизненного цикла бинов
|
||||
@@ -0,0 +1,2 @@
|
||||
# beans-scopes-demo
|
||||
Пример работы со областью действия (Scope) бинов
|
||||
@@ -0,0 +1,2 @@
|
||||
# conditional-and-profiles-demo
|
||||
Пример работы с профилями и автоконфигурациями
|
||||
@@ -0,0 +1,2 @@
|
||||
# test-configuration-demo
|
||||
Пример конфигурирования тестов
|
||||
@@ -0,0 +1,29 @@
|
||||
HELP.md
|
||||
/target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
@@ -0,0 +1,2 @@
|
||||
# mybatis-demo
|
||||
Пример работы с БД через MyBatis
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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.1.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>ru.otus.example</groupId>
|
||||
<artifactId>mybatis-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>mybatis-demo</name>
|
||||
<description>MyBatis demo</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>2.0.1</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>
|
||||
</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>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package ru.otus.example.mybatisdemo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MyBatisDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MyBatisDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.mybatisdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Avatar {
|
||||
private long id;
|
||||
private String photoUrl;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.mybatisdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Course {
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.mybatisdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EMail {
|
||||
private long id;
|
||||
private String email;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ru.otus.example.mybatisdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class OtusStudent {
|
||||
private long id;
|
||||
private String name;
|
||||
private Avatar avatar;
|
||||
private List<EMail> emails;
|
||||
private List<Course> courses;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ru.otus.example.mybatisdemo.repositories;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Result;
|
||||
import org.apache.ibatis.annotations.Results;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import ru.otus.example.mybatisdemo.models.Avatar;
|
||||
|
||||
@Mapper
|
||||
public interface AvatarRepository {
|
||||
@Select("select * from avatars where id = #{id}")
|
||||
@Results(value = {
|
||||
@Result(property = "id", column = "id"),
|
||||
@Result(property = "photoUrl", column = "photo_url")
|
||||
})
|
||||
Avatar getAvatarById(long id);
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package ru.otus.example.mybatisdemo.repositories;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import ru.otus.example.mybatisdemo.models.Course;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface CourseRepository {
|
||||
|
||||
@Select("select * from student_courses sc left join courses c on sc.course_id = c.id where sc.student_id = #{studentId}")
|
||||
List<Course> getCoursesByStudentId(long studentId);
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package ru.otus.example.mybatisdemo.repositories;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import ru.otus.example.mybatisdemo.models.EMail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface EmailRepository {
|
||||
|
||||
@Select("select * from emails where student_id = #{studentId}")
|
||||
List<EMail> getEmailsByStudentId(long studentId);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package ru.otus.example.mybatisdemo.repositories;
|
||||
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.apache.ibatis.mapping.FetchType;
|
||||
import ru.otus.example.mybatisdemo.models.Avatar;
|
||||
import ru.otus.example.mybatisdemo.models.OtusStudent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface OtusStudentRepository {
|
||||
|
||||
@Select("select * from otus_students")
|
||||
@Results(id = "studentAllMap", value = {
|
||||
@Result(property = "id", column = "id"),
|
||||
@Result(property = "name", column = "name"),
|
||||
@Result(property = "avatar", column = "avatar_id", javaType = Avatar.class,
|
||||
one = @One(select = "ru.otus.example.mybatisdemo.repositories.AvatarRepository.getAvatarById", fetchType = FetchType.EAGER)),
|
||||
@Result(property = "emails", column = "id", javaType = List.class,
|
||||
many = @Many(select = "ru.otus.example.mybatisdemo.repositories.EmailRepository.getEmailsByStudentId", fetchType = FetchType.EAGER)),
|
||||
@Result(property = "courses", column = "id", javaType = List.class,
|
||||
many = @Many(select = "ru.otus.example.mybatisdemo.repositories.CourseRepository.getCoursesByStudentId", fetchType = FetchType.EAGER))
|
||||
})
|
||||
List<OtusStudent> findAllWithAllInfo();
|
||||
|
||||
@Select("select * from otus_students where id = #{id}")
|
||||
@ResultMap("studentAllMap")
|
||||
OtusStudent findById(long id);
|
||||
|
||||
@Select("select count(*) as students_count from otus_students")
|
||||
long getStudentsCount();
|
||||
|
||||
@Insert("insert into otus_students(name, avatar_id) values (#{name}, #{avatar.id})")
|
||||
void insert(OtusStudent student);
|
||||
|
||||
@Update("update otus_students set name = #{name} where id = #{id}")
|
||||
void updateName(OtusStudent student);
|
||||
|
||||
@Delete("delete from otus_students where id = #{id}")
|
||||
void deleteById(long id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
create table avatars(
|
||||
id bigserial,
|
||||
photo_url varchar(8000),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table courses(
|
||||
id bigserial,
|
||||
name varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table otus_students(
|
||||
id bigserial,
|
||||
name varchar(255),
|
||||
avatar_id bigint references avatars (id),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table emails(
|
||||
id bigserial,
|
||||
student_id bigint references otus_students(id) on delete cascade,
|
||||
email varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table student_courses(
|
||||
student_id bigint references otus_students(id) on delete cascade,
|
||||
course_id bigint references courses(id),
|
||||
primary key (student_id, course_id)
|
||||
);
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package ru.otus.example.mybatisdemo.repositories;
|
||||
|
||||
import lombok.val;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.otus.example.mybatisdemo.models.Avatar;
|
||||
import ru.otus.example.mybatisdemo.models.OtusStudent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("Репозиторий на основе MyBatis для работы со студентами ")
|
||||
@SpringBootTest
|
||||
@Transactional
|
||||
public class OtusStudentRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private OtusStudentRepository studentRepositoryMyBatis;
|
||||
|
||||
@DisplayName("должен загружать список всех студентов с полной информацией о них")
|
||||
@Test
|
||||
void shouldReturnCorrectStudentsListWithAllInfo() {
|
||||
val students = studentRepositoryMyBatis.findAllWithAllInfo();
|
||||
assertThat(students).isNotNull().hasSize(10).allMatch(s -> !s.getName().equals(""))
|
||||
.allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
|
||||
.allMatch(s -> s.getAvatar() != null)
|
||||
.allMatch(s -> s.getEmails() != null && s.getEmails().size() > 0);
|
||||
}
|
||||
|
||||
@DisplayName("должен загружать число студентов в БД")
|
||||
@Test
|
||||
void shouldReturnCorrectStudentsCount() {
|
||||
long studentsCount = studentRepositoryMyBatis.getStudentsCount();
|
||||
assertThat(studentsCount).isEqualTo(10);
|
||||
}
|
||||
|
||||
@DisplayName(" должен загружать информацию о нужном студенте")
|
||||
@Test
|
||||
void shouldFindExpectedStudentById(){
|
||||
val actualStudent = studentRepositoryMyBatis.findById(1L);
|
||||
|
||||
assertThat(actualStudent).isNotNull();
|
||||
assertThat(actualStudent.getName()).isEqualTo("student_01");
|
||||
assertThat(actualStudent.getAvatar()).isNotNull()
|
||||
.hasFieldOrPropertyWithValue("id", 1L)
|
||||
.hasFieldOrPropertyWithValue("photoUrl", "photoUrl_01");
|
||||
assertThat(actualStudent.getEmails()).isNotNull().hasSize(2);
|
||||
assertThat(actualStudent.getCourses()).isNotNull().hasSize(3);
|
||||
}
|
||||
|
||||
@DisplayName(" должен сохранить, а потом загрузить информацию о нужном студенте")
|
||||
@Test
|
||||
void shouldSaveAndLoadCorrectStudent() {
|
||||
val expectedStudent = new OtusStudent();
|
||||
expectedStudent.setName("Vasya");
|
||||
expectedStudent.setAvatar(new Avatar(1L, "photoUrl_01"));
|
||||
studentRepositoryMyBatis.insert(expectedStudent);
|
||||
val actualStudent = studentRepositoryMyBatis.findById(11L);
|
||||
|
||||
assertThat(actualStudent).isNotNull().isEqualToComparingOnlyGivenFields(expectedStudent, "name");
|
||||
}
|
||||
|
||||
|
||||
@DisplayName(" должен обновлять имя студента в БД")
|
||||
@Test
|
||||
void shouldUpdateStudentName() {
|
||||
val student = studentRepositoryMyBatis.findById(1L);
|
||||
student.setName("Висусуалий");
|
||||
studentRepositoryMyBatis.updateName(student);
|
||||
val actualStudent = studentRepositoryMyBatis.findById(1L);
|
||||
|
||||
assertThat(actualStudent).isNotNull().hasFieldOrPropertyWithValue("name", student.getName());
|
||||
}
|
||||
|
||||
@DisplayName("должен удалять студента из БД по id")
|
||||
@Test
|
||||
void shouldDeleteStudentFromDbById() {
|
||||
val studentsCountBefore = studentRepositoryMyBatis.getStudentsCount();
|
||||
studentRepositoryMyBatis.deleteById(1L);
|
||||
val studentsCountAfter = studentRepositoryMyBatis.getStudentsCount();
|
||||
|
||||
assertThat(studentsCountBefore - studentsCountAfter).isEqualTo(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
initialization-mode: always
|
||||
|
||||
jpa:
|
||||
generate-ddl: false
|
||||
#generate-ddl: true
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
#ddl-auto: create-drop
|
||||
|
||||
show-sql: true
|
||||
|
||||
|
||||
logging:
|
||||
level:
|
||||
ru.otus.example.mybatisdemo.repositories: TRACE
|
||||
@@ -0,0 +1,29 @@
|
||||
insert into avatars(photo_url)
|
||||
values ('photoUrl_01'), ('photoUrl_02'), ('photoUrl_03'), ('photoUrl_04'), ('photoUrl_05'),
|
||||
('photoUrl_06'), ('photoUrl_07'), ('photoUrl_08'), ('photoUrl_09'), ('photoUrl_10');
|
||||
|
||||
insert into courses(name)
|
||||
values ('course_name_01'), ('course_name_02'), ('course_name_03'), ('course_name_04'), ('course_name_05'),
|
||||
('course_name_06'), ('course_name_07'), ('course_name_08'), ('course_name_09'), ('course_name_10'), ('not_used_11');
|
||||
|
||||
insert into otus_students(name, avatar_id)
|
||||
values ('student_01', 1), ('student_02', 2), ('student_03', 3), ('student_04', 4), ('student_05', 5),
|
||||
('student_06', 6), ('student_07', 7), ('student_08', 8), ('student_09', 9), ('student_10', 10);
|
||||
|
||||
|
||||
insert into emails(email, student_id)
|
||||
values ('email_01', 1), ('email_02', 1), ('email_03', 2), ('email_04', 2), ('email_05', 3), ('email_06', 4),
|
||||
('email_07', 5), ('email_08', 6), ('email_09', 7), ('email_10', 8), ('email_11', 9), ('email_12', 10);
|
||||
|
||||
|
||||
insert into student_courses(student_id, course_id)
|
||||
values (1, 1), (1, 2), (1, 3),
|
||||
(2, 2), (2, 4), (2, 5),
|
||||
(3, 3), (3, 6), (3, 7),
|
||||
(4, 4), (4, 8), (4, 9),
|
||||
(5, 5), (5, 10), (5, 1),
|
||||
(6, 6), (6, 2), (6, 3),
|
||||
(7, 7), (7, 4), (7, 5),
|
||||
(8, 8), (8, 6), (8, 7),
|
||||
(9, 9), (9, 8), (9, 10),
|
||||
(10, 10), (10, 1), (10, 2);
|
||||
@@ -0,0 +1,29 @@
|
||||
HELP.md
|
||||
/target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
@@ -0,0 +1,2 @@
|
||||
# orm-demo
|
||||
Пример работы с БД через ORM
|
||||
@@ -0,0 +1,88 @@
|
||||
<?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.1.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>ru.otus.example</groupId>
|
||||
<artifactId>orm-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>orm-demo</name>
|
||||
<description>Orm demo</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
<!--
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
-->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>2.7.4</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>
|
||||
</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>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package ru.otus.example.ormdemo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import ru.otus.example.ormdemo.config.EclipseLinkJpaConfiguration;
|
||||
|
||||
@SpringBootApplication
|
||||
//@Import(EclipseLinkJpaConfiguration.class)
|
||||
public class OrmDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OrmDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package ru.otus.example.ormdemo.config;
|
||||
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class EclipseLinkJpaConfiguration extends JpaBaseConfiguration {
|
||||
@Autowired
|
||||
protected EclipseLinkJpaConfiguration(DataSource dataSource, JpaProperties properties,
|
||||
ObjectProvider<JtaTransactionManager> jtaTransactionManager,
|
||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
super(dataSource, properties, jtaTransactionManager, transactionManagerCustomizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||
return new EclipseLinkJpaVendorAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getVendorProperties() {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
map.put(PersistenceUnitProperties.WEAVING, "false");
|
||||
return map;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package ru.otus.example.ormdemo.models;
|
||||
|
||||
import lombok.*;
|
||||
import ru.otus.example.ormdemo.models.common.Avatar;
|
||||
import ru.otus.example.ormdemo.models.common.Course;
|
||||
import ru.otus.example.ormdemo.models.common.EMail;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity // Указывает, что данный класс является сущностью
|
||||
@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность
|
||||
public class OtusStudent {
|
||||
@Id // Позволяет указать какое поле является идентификатором
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов
|
||||
private long id;
|
||||
|
||||
// Задает имя и некоторые свойства поля таблицы, на которое будет отображаться поле сущности
|
||||
@Column(name = "name", nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
// Указывает на связь между таблицами "один к одному"
|
||||
@OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL)
|
||||
// Задает поле, по которому происходит объединение с таблицей для хранения связанной сущности
|
||||
@JoinColumn(name = "avatar_id")
|
||||
private Avatar avatar;
|
||||
|
||||
// Указывает на связь между таблицами "один ко многим"
|
||||
@OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "student_id")
|
||||
private List<EMail> emails;
|
||||
|
||||
// Указывает на связь между таблицами "многие ко многим"
|
||||
@ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY /*, cascade = CascadeType.ALL*/)
|
||||
// Задает таблицу связей между таблицами для хранения родительской и связанной сущностью
|
||||
@JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "course_id"))
|
||||
private List<Course> courses;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package ru.otus.example.ormdemo.models;
|
||||
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.BatchSize;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import ru.otus.example.ormdemo.models.common.Avatar;
|
||||
import ru.otus.example.ormdemo.models.common.Course;
|
||||
import ru.otus.example.ormdemo.models.common.EMail;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "otus_students")
|
||||
@NamedEntityGraph(name = "OtusStudentWithAvatarAndEmails",
|
||||
attributeNodes = {@NamedAttributeNode(value = "avatar"),
|
||||
@NamedAttributeNode(value = "emails")})
|
||||
public class OtusStudentV2 {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(name = "name", nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
@OneToOne(targetEntity = Avatar.class, cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "avatar_id")
|
||||
private Avatar avatar;
|
||||
|
||||
@OneToMany(targetEntity = EMail.class, cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "student_id")
|
||||
private List<EMail> emails;
|
||||
|
||||
//@BatchSize(size = 5)
|
||||
//@Fetch(FetchMode.SUBSELECT)
|
||||
@ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "student_courses", joinColumns = @JoinColumn(name = "student_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "course_id"))
|
||||
private List<Course> courses;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package ru.otus.example.ormdemo.models.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "avatars")
|
||||
public class Avatar {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(name = "photo_url", nullable = false, unique = true)
|
||||
private String photoUrl;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package ru.otus.example.ormdemo.models.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "courses")
|
||||
public class Course {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(name = "name", nullable = false, unique = true)
|
||||
private String name;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ru.otus.example.ormdemo.models.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "emails")
|
||||
public class EMail {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(name = "email", nullable = false, unique = true)
|
||||
private String email;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
import ru.otus.example.ormdemo.models.common.Course;
|
||||
|
||||
public interface CourseRepositoryJpa {
|
||||
Course save(Course course);
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
import ru.otus.example.ormdemo.models.common.Course;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
public class CourseRepositoryJpaImpl implements CourseRepositoryJpa {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public Course save(Course course) {
|
||||
em.persist(course);
|
||||
return course;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
|
||||
import ru.otus.example.ormdemo.models.OtusStudent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface OtusStudentRepositoryJpa {
|
||||
Optional<OtusStudent> findById(long id);
|
||||
List<OtusStudent> findAll();
|
||||
OtusStudent save(OtusStudent student);
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.ormdemo.models.OtusStudent;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public Optional<OtusStudent> findById(long id) {
|
||||
return Optional.ofNullable(em.find(OtusStudent.class, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OtusStudent> findAll() {
|
||||
return em.createQuery("select s from OtusStudent s", OtusStudent.class).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OtusStudent save(OtusStudent student) {
|
||||
if (student.getId() <= 0) {
|
||||
em.persist(student);
|
||||
//em.flush();
|
||||
return student;
|
||||
} else {
|
||||
return em.merge(student);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
|
||||
import ru.otus.example.ormdemo.models.OtusStudentV2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OtusStudentV2RepositoryJpa {
|
||||
List<OtusStudentV2> findAllWithEntityGraph();
|
||||
List<OtusStudentV2> findAllWithJoinFetch();
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.ormdemo.models.OtusStudentV2;
|
||||
|
||||
import javax.persistence.EntityGraph;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public class OtusStudentV2RepositoryJpaImpl implements OtusStudentV2RepositoryJpa {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public List<OtusStudentV2> findAllWithEntityGraph() {
|
||||
EntityGraph<?> entityGraph = em.getEntityGraph("OtusStudentWithAvatarAndEmails");
|
||||
TypedQuery<OtusStudentV2> query = em.createQuery("select s from OtusStudentV2 s", OtusStudentV2.class);
|
||||
query.setHint("javax.persistence.fetchgraph", entityGraph);
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OtusStudentV2> findAllWithJoinFetch() {
|
||||
return em.createQuery("select distinct s from OtusStudentV2 s join fetch s.avatar join fetch s.emails", OtusStudentV2.class).getResultList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
create table avatars(
|
||||
id bigserial,
|
||||
photo_url varchar(8000),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table courses(
|
||||
id bigserial,
|
||||
name varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table otus_students(
|
||||
id bigserial,
|
||||
name varchar(255),
|
||||
avatar_id bigint references avatars (id),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table emails(
|
||||
id bigserial,
|
||||
student_id bigint references otus_students(id) on delete cascade,
|
||||
email varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table student_courses(
|
||||
student_id bigint references otus_students(id) on delete cascade,
|
||||
course_id bigint references courses(id),
|
||||
primary key (student_id, course_id)
|
||||
);
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
import lombok.val;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import ru.otus.example.ormdemo.models.OtusStudent;
|
||||
import ru.otus.example.ormdemo.models.common.Avatar;
|
||||
import ru.otus.example.ormdemo.models.common.Course;
|
||||
import ru.otus.example.ormdemo.models.common.EMail;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("Репозиторий на основе Jpa для работы со студентами ")
|
||||
@DataJpaTest
|
||||
@Import({OtusStudentRepositoryJpaImpl.class, CourseRepositoryJpaImpl.class})
|
||||
class OtusStudentRepositoryJpaImplTest {
|
||||
|
||||
private static final int EXPECTED_QUERIES_COUNT = 31;
|
||||
|
||||
@Autowired
|
||||
private OtusStudentRepositoryJpaImpl repositoryJpa;
|
||||
|
||||
@Autowired
|
||||
private CourseRepositoryJpaImpl courseRepositoryJpa;
|
||||
|
||||
@Autowired
|
||||
private TestEntityManager em;
|
||||
|
||||
@DisplayName(" должен загружать информацию о нужном студенте")
|
||||
@Test
|
||||
void shouldFindExpectedStudentById() {
|
||||
val optionalActualStudent = repositoryJpa.findById(1L);
|
||||
val expectedStudent = em.find(OtusStudent.class, 1L);
|
||||
assertThat(optionalActualStudent).isPresent().get().isEqualToComparingFieldByFieldRecursively(expectedStudent);
|
||||
}
|
||||
|
||||
@DisplayName("должен загружать список всех студентов с полной информацией о них")
|
||||
@Test
|
||||
void shouldReturnCorrectStudentsListWithAllInfo() {
|
||||
SessionFactory sessionFactory = em.getEntityManager().getEntityManagerFactory().unwrap(SessionFactory.class);
|
||||
sessionFactory.getStatistics().setStatisticsEnabled(true);
|
||||
|
||||
|
||||
System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------");
|
||||
val students = repositoryJpa.findAll();
|
||||
assertThat(students).isNotNull().hasSize(10).allMatch(s -> !s.getName().equals(""))
|
||||
.allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
|
||||
.allMatch(s -> s.getAvatar() != null)
|
||||
.allMatch(s -> s.getEmails() != null && s.getEmails().size() > 0);
|
||||
System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n");
|
||||
assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT);
|
||||
}
|
||||
|
||||
@DisplayName(" должен корректно сохранять всю информацию о студенте")
|
||||
@Test
|
||||
void shouldSaveAllStudentInfo() {
|
||||
val avatar = new Avatar(0, "где-то там");
|
||||
val email = new EMail(0, "any@mail.com");
|
||||
val emails = Collections.singletonList(email);
|
||||
|
||||
val course = new Course(0, "Spring");
|
||||
val courses = Collections.singletonList(course);
|
||||
//courseRepositoryJpa.save(course);
|
||||
|
||||
|
||||
val vasya = new OtusStudent(- 1, "Vasya", avatar, emails, courses);
|
||||
repositoryJpa.save(vasya);
|
||||
assertThat(vasya.getId()).isGreaterThan(0);
|
||||
|
||||
val actualStudent = em.find(OtusStudent.class, vasya.getId());
|
||||
assertThat(actualStudent).isNotNull().matches(s -> !s.getName().equals(""))
|
||||
.matches(s -> s.getCourses() != null && s.getCourses().size() > 0 && s.getCourses().get(0).getId() > 0)
|
||||
.matches(s -> s.getAvatar() != null)
|
||||
.matches(s -> s.getEmails() != null && s.getEmails().size() > 0);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package ru.otus.example.ormdemo.repositories;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import ru.otus.example.ormdemo.models.OtusStudentV2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("Репозиторий v2 на основе Jpa для работы со студентами ")
|
||||
@DataJpaTest
|
||||
@Import({OtusStudentV2RepositoryJpaImpl.class})
|
||||
class OtusStudentV2RepositoryJpaImplTest {
|
||||
|
||||
private static final int EXPECTED_QUERIES_COUNT = 11;
|
||||
//private static final int EXPECTED_QUERIES_COUNT = 2;
|
||||
//private static final int EXPECTED_QUERIES_COUNT = 3;
|
||||
|
||||
@Autowired
|
||||
private OtusStudentV2RepositoryJpaImpl repositoryJpa;
|
||||
|
||||
@Autowired
|
||||
private TestEntityManager em;
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
sessionFactory = em.getEntityManager().getEntityManagerFactory().unwrap(SessionFactory.class);
|
||||
sessionFactory.getStatistics().setStatisticsEnabled(true);
|
||||
sessionFactory.getStatistics().clear();
|
||||
}
|
||||
|
||||
@DisplayName(" с помощью EntityGraph должен загружать список всех студентов с полной информацией о них")
|
||||
@Test
|
||||
void usingEntityGraphShouldReturnCorrectStudentsListWithWithAllInfo() {
|
||||
System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------");
|
||||
List<OtusStudentV2> students = repositoryJpa.findAllWithEntityGraph();
|
||||
assertThat(students).isNotNull().hasSize(10).allMatch(s -> !s.getName().equals(""))
|
||||
.allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
|
||||
.allMatch(s -> s.getAvatar() != null)
|
||||
.allMatch(s -> s.getEmails() != null && s.getEmails().size() > 0);
|
||||
System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n");
|
||||
assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT);
|
||||
|
||||
}
|
||||
|
||||
@DisplayName(" с помощью 'join fetch' должен загружать список всех студентов с полной информацией о них")
|
||||
@Test
|
||||
void usingJoinFetchShouldReturnCorrectStudentsListWithWithAllInfo() {
|
||||
System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------");
|
||||
List<OtusStudentV2> students = repositoryJpa.findAllWithJoinFetch();
|
||||
assertThat(students).isNotNull().hasSize(10).allMatch(s -> !s.getName().equals(""))
|
||||
.allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
|
||||
.allMatch(s -> s.getAvatar() != null)
|
||||
.allMatch(s -> s.getEmails() != null && s.getEmails().size() > 0);
|
||||
System.out.println("----------------------------------------------------------------------------------------------------------\n\n\n\n");
|
||||
assertThat(sessionFactory.getStatistics().getPrepareStatementCount()).isEqualTo(EXPECTED_QUERIES_COUNT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
initialization-mode: always
|
||||
|
||||
jpa:
|
||||
show-sql: false
|
||||
generate-ddl: false
|
||||
#generate-ddl: true
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
#ddl-auto: create-drop
|
||||
|
||||
#show-sql: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
ROOT: ERROR
|
||||
@@ -0,0 +1,29 @@
|
||||
insert into avatars(photo_url)
|
||||
values ('photoUrl_01'), ('photoUrl_02'), ('photoUrl_03'), ('photoUrl_04'), ('photoUrl_05'),
|
||||
('photoUrl_06'), ('photoUrl_07'), ('photoUrl_08'), ('photoUrl_09'), ('photoUrl_10');
|
||||
|
||||
insert into courses(name)
|
||||
values ('course_name_01'), ('course_name_02'), ('course_name_03'), ('course_name_04'), ('course_name_05'),
|
||||
('course_name_06'), ('course_name_07'), ('course_name_08'), ('course_name_09'), ('course_name_10'), ('not_used_11');
|
||||
|
||||
insert into otus_students(name, avatar_id)
|
||||
values ('student_01', 1), ('student_02', 2), ('student_03', 3), ('student_04', 4), ('student_05', 5),
|
||||
('student_06', 6), ('student_07', 7), ('student_08', 8), ('student_09', 9), ('student_10', 10);
|
||||
|
||||
|
||||
insert into emails(email, student_id)
|
||||
values ('email_01', 1), ('email_02', 1), ('email_03', 2), ('email_04', 2), ('email_05', 3), ('email_06', 4),
|
||||
('email_07', 5), ('email_08', 6), ('email_09', 7), ('email_10', 8), ('email_11', 9), ('email_12', 10);
|
||||
|
||||
|
||||
insert into student_courses(student_id, course_id)
|
||||
values (1, 1), (1, 2), (1, 3),
|
||||
(2, 2), (2, 4), (2, 5),
|
||||
(3, 3), (3, 6), (3, 7),
|
||||
(4, 4), (4, 8), (4, 9),
|
||||
(5, 5), (5, 10), (5, 1),
|
||||
(6, 6), (6, 2), (6, 3),
|
||||
(7, 7), (7, 4), (7, 5),
|
||||
(8, 8), (8, 6), (8, 7),
|
||||
(9, 9), (9, 8), (9, 10),
|
||||
(10, 10), (10, 1), (10, 2);
|
||||
@@ -0,0 +1,29 @@
|
||||
HELP.md
|
||||
/target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
@@ -0,0 +1,2 @@
|
||||
# spring-jdbc-demo
|
||||
Пример работы с БД через jdbc
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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.1.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>ru.otus.example</groupId>
|
||||
<artifactId>spring-jdbc-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>spring-jdbc-demo</name>
|
||||
<description>Spring jdbc 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>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>
|
||||
|
||||
<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>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.springjdbcdemo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringJdbcDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringJdbcDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.springjdbcdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Avatar {
|
||||
private long id;
|
||||
private String photoUrl;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.springjdbcdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Course {
|
||||
private long id;
|
||||
private String name;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ru.otus.example.springjdbcdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EMail {
|
||||
private long id;
|
||||
private String email;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ru.otus.example.springjdbcdemo.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class OtusStudent {
|
||||
private long id;
|
||||
private String name;
|
||||
private Avatar avatar;
|
||||
private List<EMail> emails;
|
||||
private List<Course> courses;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories;
|
||||
|
||||
import ru.otus.example.springjdbcdemo.models.Course;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CourseRepositoryJdbc {
|
||||
List<Course> findAllUsed();
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.springjdbcdemo.models.Course;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class CourseRepositoryJdbcImpl implements CourseRepositoryJdbc {
|
||||
|
||||
@Autowired
|
||||
private final JdbcOperations op;
|
||||
|
||||
@Override
|
||||
public List<Course> findAllUsed() {
|
||||
return op.query("select c.id, c.name " +
|
||||
"from courses c inner join student_courses sc on c.id = sc.course_id " +
|
||||
"group by c.id, c.name " +
|
||||
"order by c.name", new CourseRowMapper());
|
||||
}
|
||||
|
||||
private class CourseRowMapper implements RowMapper<Course> {
|
||||
@Override
|
||||
public Course mapRow(ResultSet rs, int i) throws SQLException {
|
||||
return new Course(rs.getLong(1), rs.getString(2));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories;
|
||||
|
||||
import ru.otus.example.springjdbcdemo.models.OtusStudent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OtusStudentRepositoryJdbc {
|
||||
List<OtusStudent> findAllWithAllInfo();
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.otus.example.springjdbcdemo.models.Course;
|
||||
import ru.otus.example.springjdbcdemo.models.OtusStudent;
|
||||
import ru.otus.example.springjdbcdemo.repositories.ext.OtusStudentResultSetExtractor;
|
||||
import ru.otus.example.springjdbcdemo.repositories.ext.StudentCourseRelation;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class OtusStudentRepositoryJdbcImpl implements OtusStudentRepositoryJdbc {
|
||||
|
||||
private final CourseRepositoryJdbc courseRepository;
|
||||
private final JdbcOperations op;
|
||||
|
||||
@Override
|
||||
public List<OtusStudent> findAllWithAllInfo() {
|
||||
List<Course> courses = courseRepository.findAllUsed();
|
||||
List<StudentCourseRelation> relations = getAllRelations();
|
||||
Map<Long, OtusStudent> students = op.query("select os.id, os.name, a.id avatar_id, a.photo_url, e.id email_id, e.email " +
|
||||
"from (otus_students os left join avatars a on " +
|
||||
"os.avatar_id = a.id) left join emails e on os.id = e.student_id",
|
||||
new OtusStudentResultSetExtractor());
|
||||
|
||||
mergeStudentsInfo(students, courses, relations);
|
||||
return new ArrayList<>(Objects.requireNonNull(students).values());
|
||||
}
|
||||
|
||||
private List<StudentCourseRelation> getAllRelations() {
|
||||
return op.query("select student_id, course_id from student_courses sc order by student_id, course_id",
|
||||
(rs, i) -> new StudentCourseRelation(rs.getLong(1), rs.getLong(2)));
|
||||
}
|
||||
|
||||
private void mergeStudentsInfo(Map<Long, OtusStudent> students, List<Course> courses, List<StudentCourseRelation> relations) {
|
||||
Map<Long, Course> coursesMap = courses.stream().collect(Collectors.toMap(Course::getId, c -> c));
|
||||
relations.forEach(r -> {
|
||||
if (students.containsKey(r.getStudentId()) && coursesMap.containsKey(r.getCourseId())) {
|
||||
students.get(r.getStudentId()).getCourses().add(coursesMap.get(r.getCourseId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories.ext;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import ru.otus.example.springjdbcdemo.models.Avatar;
|
||||
import ru.otus.example.springjdbcdemo.models.EMail;
|
||||
import ru.otus.example.springjdbcdemo.models.OtusStudent;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class OtusStudentResultSetExtractor implements
|
||||
ResultSetExtractor<Map<Long, OtusStudent>> {
|
||||
@Override
|
||||
public Map<Long, OtusStudent> extractData(ResultSet rs) throws SQLException,
|
||||
DataAccessException {
|
||||
|
||||
Map<Long, OtusStudent> students = new HashMap<>();
|
||||
while (rs.next()) {
|
||||
long id = rs.getLong("id");
|
||||
OtusStudent student = students.get(id);
|
||||
if (student == null) {
|
||||
student = new OtusStudent(id, rs.getString("name"),
|
||||
new Avatar(rs.getLong("avatar_id"), rs.getString("photo_url")),
|
||||
new ArrayList<>(), new ArrayList<>());
|
||||
students.put(student.getId(), student);
|
||||
}
|
||||
|
||||
student.getEmails().add(new EMail(rs.getLong("email_id"),
|
||||
rs.getString("email")));
|
||||
}
|
||||
return students;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories.ext;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
public class StudentCourseRelation {
|
||||
private final long studentId;
|
||||
private final long courseId;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
create table avatars(
|
||||
id bigserial,
|
||||
photo_url varchar(8000),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table courses(
|
||||
id bigserial,
|
||||
name varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table otus_students(
|
||||
id bigserial,
|
||||
name varchar(255),
|
||||
avatar_id bigint references avatars (id),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table emails(
|
||||
id bigserial,
|
||||
student_id bigint references otus_students(id) on delete cascade,
|
||||
email varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table student_courses(
|
||||
student_id bigint references otus_students(id) on delete cascade,
|
||||
course_id bigint references courses(id),
|
||||
primary key (student_id, course_id)
|
||||
);
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ru.otus.example.springjdbcdemo.repositories;
|
||||
|
||||
import lombok.val;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("Репозиторий на основе Jdbc для работы со студентами ")
|
||||
@JdbcTest
|
||||
@Import({OtusStudentRepositoryJdbcImpl.class, CourseRepositoryJdbcImpl.class})
|
||||
class OtusStudentRepositoryJdbcImplTest {
|
||||
|
||||
@Autowired
|
||||
private OtusStudentRepositoryJdbcImpl repositoryJdbc;
|
||||
|
||||
@DisplayName("должен загружать список всех студентов с полной информацией о них")
|
||||
@Test
|
||||
void shouldReturnCorrectStudentsListWithAllInfo() {
|
||||
val students = repositoryJdbc.findAllWithAllInfo();
|
||||
assertThat(students).isNotNull().hasSize(10).allMatch(s -> !s.getName().equals(""))
|
||||
.allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
|
||||
.allMatch(s -> s.getAvatar() != null)
|
||||
.allMatch(s -> s.getEmails() != null && s.getEmails().size() > 0);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
initialization-mode: always
|
||||
|
||||
jpa:
|
||||
generate-ddl: false
|
||||
#generate-ddl: true
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
#ddl-auto: create-drop
|
||||
|
||||
show-sql: true
|
||||
|
||||
|
||||
logging:
|
||||
level:
|
||||
ru.otus.example.ormbasicsdemo.repositories.mybatis: TRACE
|
||||
@@ -0,0 +1,29 @@
|
||||
insert into avatars(photo_url)
|
||||
values ('photoUrl_01'), ('photoUrl_02'), ('photoUrl_03'), ('photoUrl_04'), ('photoUrl_05'),
|
||||
('photoUrl_06'), ('photoUrl_07'), ('photoUrl_08'), ('photoUrl_09'), ('photoUrl_10');
|
||||
|
||||
insert into courses(name)
|
||||
values ('course_name_01'), ('course_name_02'), ('course_name_03'), ('course_name_04'), ('course_name_05'),
|
||||
('course_name_06'), ('course_name_07'), ('course_name_08'), ('course_name_09'), ('course_name_10'), ('not_used_11');
|
||||
|
||||
insert into otus_students(name, avatar_id)
|
||||
values ('student_01', 1), ('student_02', 2), ('student_03', 3), ('student_04', 4), ('student_05', 5),
|
||||
('student_06', 6), ('student_07', 7), ('student_08', 8), ('student_09', 9), ('student_10', 10);
|
||||
|
||||
|
||||
insert into emails(email, student_id)
|
||||
values ('email_01', 1), ('email_02', 1), ('email_03', 2), ('email_04', 2), ('email_05', 3), ('email_06', 4),
|
||||
('email_07', 5), ('email_08', 6), ('email_09', 7), ('email_10', 8), ('email_11', 9), ('email_12', 10);
|
||||
|
||||
|
||||
insert into student_courses(student_id, course_id)
|
||||
values (1, 1), (1, 2), (1, 3),
|
||||
(2, 2), (2, 4), (2, 5),
|
||||
(3, 3), (3, 6), (3, 7),
|
||||
(4, 4), (4, 8), (4, 9),
|
||||
(5, 5), (5, 10), (5, 1),
|
||||
(6, 6), (6, 2), (6, 3),
|
||||
(7, 7), (7, 4), (7, 5),
|
||||
(8, 8), (8, 6), (8, 7),
|
||||
(9, 9), (9, 8), (9, 10),
|
||||
(10, 10), (10, 1), (10, 2);
|
||||
Reference in New Issue
Block a user