jpql-demo example has ben added

This commit is contained in:
stvort
2019-12-06 23:05:38 +04:00
parent 3c7ca8f9ab
commit a824cdcc00
19 changed files with 1004 additions and 1 deletions
+2 -1
View File
@@ -9,4 +9,5 @@
* *docker-test-containers* - пример настройки TestContainers для монги
* *spring-cloud-demo-stvort* - пример работы двух микросевисов с использованием Config server, Eureka, Zuul, Feign client
* *spring-mail-integration-demo* - пример работы с SpringMail через SpringIntegration
* *liquibase-demo* - пример работы с liquibase
* *liquibase-demo* - пример работы с liquibase
* *jpql-demo* - пример работы с JPQL
+29
View File
@@ -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/
+6
View File
@@ -0,0 +1,6 @@
## Пример работы с JPQL
В примере демонстрируется:
* *репозитории на Spring ORM с использованием JPA и JPQL*
* *использование JPQL для написания разного рода запросов (в т.ч. для выборки, агрегации, изменения и удаления данных)*
* *тестирование репозиториев на Spring ORM с использованием @DataJpaTest*
+71
View File
@@ -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.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ru.otus.example</groupId>
<artifactId>jpql-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jpql-demo</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>
</project>
@@ -0,0 +1,13 @@
package ru.otus.example.jpql_demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JpqlDemoApplication {
public static void main(String[] args) {
SpringApplication.run(JpqlDemoApplication.class, args);
}
}
@@ -0,0 +1,15 @@
package ru.otus.example.jpql_demo.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CitySalary {
private String city;
private Double salary;
}
@@ -0,0 +1,16 @@
package ru.otus.example.jpql_demo.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import ru.otus.example.jpql_demo.models.Employee;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmployeeProjects {
private Employee employee;
private long projectsCount;
}
@@ -0,0 +1,24 @@
package ru.otus.example.jpql_demo.models;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "city")
private String city;
}
@@ -0,0 +1,21 @@
package ru.otus.example.jpql_demo.models;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "departments")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column
private String name;
}
@@ -0,0 +1,53 @@
package ru.otus.example.jpql_demo.models;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "salary")
private BigDecimal salary;
@ManyToOne
@JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
@ManyToOne
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
@BatchSize(size = 100)
@ManyToMany
@JoinTable(name = "employees_projects",
joinColumns = @JoinColumn(name = "employee_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "project_id", referencedColumnName = "id"))
private List<Project> projects;
public Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
@@ -0,0 +1,21 @@
package ru.otus.example.jpql_demo.models;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "projects")
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "name")
private String name;
}
@@ -0,0 +1,49 @@
package ru.otus.example.jpql_demo.repositories;
import ru.otus.example.jpql_demo.dto.CitySalary;
import ru.otus.example.jpql_demo.dto.EmployeeProjects;
import ru.otus.example.jpql_demo.models.Employee;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
public interface EmployeeRepository {
List<Employee> findAll();
Optional<Employee> findEmployeeById(long id);
List<Employee> findAllEmployeesWithSalaryOver100000();
List<String> findEmployeesFirstNames();
List<Object[]> findEmployeesFirstAndLastNames();
long calcEmployeesCount();
BigDecimal findMaxEmployeeSalary();
Double calcAvgEmployeeSalary();
List<CitySalary> calcAvgSalaryByCities();
List<CitySalary> calcAvgSalaryByCitiesSorted();
List<CitySalary> calcAvgSalaryByCitiesHavingValueOver100000();
List<Employee> findEmployeesWithGivenProjects(String p1Name, String p2Name);
List<EmployeeProjects> findEmployeesProjectsCount();
List<Employee> findEmployeesWithGivenFirstNames(String name1, String name2);
List<Employee> findEmployeesWithFirstNamesFromGivenList(List<String> names);
List<Employee> findEmployeeNameSakes(Employee employee);
Employee findEmployeeNameSake(Employee employee);
List<Employee> findEmployeesWithSalaryLessThanGivenEmployee(Employee employee);
List<Employee> findEmployeeWithNameMatchingAnyOtherEmployeesNames();
List<Employee> findEmployeesWithSalaryLessThanAllEmployees();
int updateEmployeesSalary(BigDecimal oldSalary, BigDecimal newSalary);
int doubleEmployeesSalary(BigDecimal oldSalary);
int deleteEmployeesWithoutDepartment();
}
@@ -0,0 +1,237 @@
package ru.otus.example.jpql_demo.repositories;
import org.springframework.stereotype.Repository;
import ru.otus.example.jpql_demo.dto.CitySalary;
import ru.otus.example.jpql_demo.dto.EmployeeProjects;
import ru.otus.example.jpql_demo.models.Employee;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Repository
public class EmployeeRepositoryImpl implements EmployeeRepository {
@PersistenceContext
private EntityManager em;
@Override
public List<Employee> findAll() {
return em.createQuery("select e from Employee e", Employee.class).getResultList();
}
@Override
public Optional<Employee> findEmployeeById(long id) {
TypedQuery<Employee> query = em.createQuery(
"select e from Employee e where e.id = :id"
, Employee.class);
query.setParameter("id", id);
try {
return Optional.of(query.getSingleResult());
} catch (NoResultException e) {
return Optional.empty();
}
}
@Override
public List<Employee> findAllEmployeesWithSalaryOver100000() {
return em.createQuery(
"select e from Employee e where e.salary > 100000"
, Employee.class).getResultList();
}
@Override
public List<String> findEmployeesFirstNames() {
return em.createQuery(
"select e.firstName from Employee e"
, String.class).getResultList();
}
@SuppressWarnings("unchecked")
@Override
public List<Object[]> findEmployeesFirstAndLastNames() {
return em.createQuery(
"select e.firstName, e.lastName from Employee e"
).getResultList();
}
@Override
public long calcEmployeesCount() {
return em.createQuery(
"select count(e) from Employee e"
, Long.class).getSingleResult();
}
@Override
public BigDecimal findMaxEmployeeSalary() {
return em.createQuery(
"select max(e.salary) from Employee e"
, BigDecimal.class).getSingleResult();
}
@Override
public Double calcAvgEmployeeSalary() {
return em.createQuery(
"select avg(e.salary) from Employee e"
, Double.class).getSingleResult();
}
//-------------------------------------------------------------------------------------------------------
@Override
public List<CitySalary> calcAvgSalaryByCities() {
return em.createQuery(
"select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) " +
"from Employee e " +
"group by e.address.city"
, CitySalary.class).getResultList();
}
@Override
public List<CitySalary> calcAvgSalaryByCitiesSorted() {
return em.createQuery(
"select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) " +
"from Employee e " +
"group by e.address.city " +
"order by avg(e.salary)"
, CitySalary.class).getResultList();
}
@Override
public List<CitySalary> calcAvgSalaryByCitiesHavingValueOver100000() {
return em.createQuery(
"select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary)) " +
"from Employee e " +
"group by e.address.city " +
"having avg(e.salary) > 100000" +
"order by avg(e.salary) "
, CitySalary.class).getResultList();
}
//-------------------------------------------------------------------------------------------------------
@Override
public List<Employee> findEmployeesWithGivenProjects(String p1Name, String p2Name) {
TypedQuery<Employee> query = em.createQuery(
"select e " +
"from Employee e join e.projects p1 join e.projects p2 " +
"where p1.name = :p1 and p2.name = :p2"
, Employee.class);
query.setParameter("p1", p1Name);
query.setParameter("p2", p2Name);
return query.getResultList();
}
@Override
public List<EmployeeProjects> findEmployeesProjectsCount() {
return em.createQuery(
"select new ru.otus.example.jpql_demo.dto.EmployeeProjects(e, count(p)) " +
"from Employee e left join e.projects p " +
"group by e " +
"order by count(p) desc "
, EmployeeProjects.class).getResultList();
}
//-------------------------------------------------------------------------------------------------------
@Override
public List<Employee> findEmployeesWithGivenFirstNames(String name1, String name2) {
TypedQuery<Employee> query = em.createQuery(
"select e " +
"from Employee e " +
"where e.firstName in (:name1, :name2) "
, Employee.class);
query.setParameter("name1", name1);
query.setParameter("name2", name2);
return query.getResultList();
}
@Override
public List<Employee> findEmployeesWithFirstNamesFromGivenList(List<String> names) {
TypedQuery<Employee> query = em.createQuery(
"select e " +
"from Employee e " +
"where e.firstName in :names "
, Employee.class);
query.setParameter("names", names);
return query.getResultList();
}
@Override
public List<Employee> findEmployeeNameSakes(Employee employee) {
TypedQuery<Employee> query = em.createQuery(
"select e " +
"from Employee e " +
"where e.firstName in (select e2.firstName from Employee e2 where e2.lastName = :lastName and e2.id <> :id) "
, Employee.class);
query.setParameter("lastName", employee.getLastName());
query.setParameter("id", employee.getId());
return query.getResultList();
}
//-------------------------------------------------------------------------------------------------------
@Override
public Employee findEmployeeNameSake(Employee employee) {
TypedQuery<Employee> query = em.createQuery(
"select e " +
"from Employee e " +
"where e.firstName = (select e2.firstName from Employee e2 where e2.id = :id) and e.id <> :id "
, Employee.class);
query.setParameter("id", employee.getId());
return query.getSingleResult();
}
@Override
public List<Employee> findEmployeesWithSalaryLessThanGivenEmployee(Employee employee) {
TypedQuery<Employee> query = em.createQuery(
"select e " +
"from Employee e " +
"where e.salary < (select e2.salary from Employee e2 where e2.id = :id) "
, Employee.class);
query.setParameter("id", employee.getId());
return query.getResultList();
}
@Override
public List<Employee> findEmployeeWithNameMatchingAnyOtherEmployeesNames() {
return em.createQuery(
"select e " +
"from Employee e " +
"where e.firstName = any(select e2.firstName from Employee e2 where e2.id <> e.id) "
, Employee.class).getResultList();
}
@Override
public List<Employee> findEmployeesWithSalaryLessThanAllEmployees() {
return em.createQuery(
"select e " +
"from Employee e " +
"where e.salary <= all(select e2.salary from Employee e2) "
, Employee.class).getResultList();
}
//-------------------------------------------------------------------------------------------------------
@Override
public int updateEmployeesSalary(BigDecimal oldSalary, BigDecimal newSalary) {
Query query = em.createQuery("update Employee e set e.salary = :newSalary where e.salary = :oldSalary");
query.setParameter("newSalary", newSalary);
query.setParameter("oldSalary", oldSalary);
return query.executeUpdate();
}
@Override
public int doubleEmployeesSalary(BigDecimal oldSalary) {
Query query = em.createQuery("update Employee e set e.salary = e.salary * 2 where e.salary = :oldSalary");
query.setParameter("oldSalary", oldSalary);
return query.executeUpdate();
}
@Override
public int deleteEmployeesWithoutDepartment() {
return em.createQuery("delete from Employee e where e.department is null")
.executeUpdate();
}
}
@@ -0,0 +1,15 @@
spring:
datasource:
url: jdbc:h2:mem:testdb
initialization-mode: always
jpa:
generate-ddl: false
hibernate:
ddl-auto: none
show-sql: true
properties:
hibernate:
#format_sql: true
@@ -0,0 +1,74 @@
select e from Employee e where e.id = :id
----------------------------------------------------------------------------------
select e from Employee e where e.salary > 100000
----------------------------------------------------------------------------------
select e.firstName from Employee e
----------------------------------------------------------------------------------
select e.firstName, e.lastName from Employee e
----------------------------------------------------------------------------------
select count(e) from Employee e
----------------------------------------------------------------------------------
select max(e.salary) from Employee e
----------------------------------------------------------------------------------
select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary))
from Employee e
group by e.address.city
----------------------------------------------------------------------------------
select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary))
from Employee e
group by e.address.city
order by avg(e.salary)
----------------------------------------------------------------------------------
select new ru.otus.example.jpql_demo.dto.CitySalary(e.address.city, avg(e.salary))
from Employee e
group by e.address.city
having avg(e.salary) > 100000
order by avg(e.salary)
----------------------------------------------------------------------------------
select e
from Employee e join e.projects p1 join e.projects p2
where p1.name = :p1 and p2.name = :p2
----------------------------------------------------------------------------------
select new ru.otus.example.jpql_demo.dto.EmployeeProjects(e, count(p))
from Employee e left join e.projects p
group by e
order by count(p) desc
----------------------------------------------------------------------------------
select e
from Employee e
where e.firstName in (:name1, :name2)
----------------------------------------------------------------------------------
select e
from Employee e
where e.firstName in :names
----------------------------------------------------------------------------------
select e
from Employee e
where e.firstName in (select e2.firstName
from Employee e2
where e2.lastName = :lastName and
e2.id <> :id)
----------------------------------------------------------------------------------
select e
from Employee e
where e.firstName = (select e2.firstName
from Employee e2
where e2.id = :id) and e.id <> :id
----------------------------------------------------------------------------------
select e
from Employee e
where e.salary < (select e2.salary
from Employee e2
where e2.id = :id)
----------------------------------------------------------------------------------
select e
from Employee e
where e.firstName = any(select e2.firstName
from Employee e2
where e2.id <> e.id)
----------------------------------------------------------------------------------
select e
from Employee e
where e.salary <= all(select e2.salary
from Employee e2)
----------------------------------------------------------------------------------
@@ -0,0 +1,39 @@
DROP TABLE IF EXISTS employees_projects;
DROP TABLE IF EXISTS addresses;
DROP TABLE IF EXISTS departments;
DROP TABLE IF EXISTS projects;
DROP TABLE IF EXISTS employees;
CREATE TABLE addresses (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
city VARCHAR(255)
);
CREATE TABLE departments (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE projects (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE employees (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(255),
last_name VARCHAR(255),
salary BIGINT,
address_id BIGINT,
department_id BIGINT,
FOREIGN KEY(address_id) REFERENCES addresses(id) ON DELETE CASCADE,
FOREIGN KEY(department_id) REFERENCES departments(id) ON DELETE CASCADE
);
CREATE TABLE employees_projects (
employee_id BIGINT,
project_id BIGINT,
FOREIGN KEY(employee_id) REFERENCES employees(id) ON DELETE CASCADE,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
);
@@ -0,0 +1,282 @@
package ru.otus.example.jpql_demo.repositories;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.context.annotation.Import;
import ru.otus.example.jpql_demo.dto.CitySalary;
import ru.otus.example.jpql_demo.dto.EmployeeProjects;
import ru.otus.example.jpql_demo.models.Employee;
import javax.persistence.NonUniqueResultException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import static org.assertj.core.api.Assertions.*;
@DisplayName("Репозиторий Employee должен")
@DataJpaTest
@Import(EmployeeRepositoryImpl.class)
class EmployeeRepositoryImplTest {
private static final long FIRST_EMPLOYEE_ID = 1L;
private static final long SECOND_EMPLOYEE_ID = 2L;
private static final long THIRD_EMPLOYEE_ID = 3L;
private static final long FOURTH_EMPLOYEE_ID = 4L;
private static final long SEVENTH_EMPLOYEE_ID = 7L;
private static final long EIGTH_EMPLOYEE_ID = 8L;
private static final int EMPLOYEES_COUNT = 8;
private static final String FIRST_EMPLOYEE_FIRST_NAME = "fn1";
private static final String PROJECT_3 = "Project #3";
private static final String PROJECT_4 = "Project #4";
private static final CitySalary SARATOV_SALARY = new CitySalary("Saratov", 66666.0);
private static final CitySalary OMSK_SALARY = new CitySalary("Omsk", 170000.0);
private static final CitySalary MOSCOW_SALARY = new CitySalary("Moscow", 330100.0);
private static final int MAX_SALARY = 1000000;
private static final double AVG_SALARY = 211299.75d;
private static final int EMPLOYEES_WITH_SALARY_OVER_100000_COUNT = 4;
private static final int FOURTH_EMPLOYEE_PROJECTS_COUNT = 4;
private static final String NAME_SAKE_NAME_1 = "NameSake1";
private static final String NAME_SAKE_NAME_2 = "NameSake2";
@Autowired
private TestEntityManager em;
@Autowired
private EmployeeRepositoryImpl employeeRepository;
@DisplayName("возвращать список всех сотрудников")
@Test
void shouldFindAllEmployees() {
List<Employee> employees = employeeRepository.findAll();
assertThat(employees).hasSize(EMPLOYEES_COUNT);
}
@DisplayName("возвращать сотрудника по его id")
@Test
void shouldFindEmployeeById() {
Optional<Employee> employee = employeeRepository.findEmployeeById(FIRST_EMPLOYEE_ID);
assertThat(employee).isNotEmpty().get()
.hasFieldOrPropertyWithValue("firstName", FIRST_EMPLOYEE_FIRST_NAME);
}
@DisplayName("возвращать список всех сотрудников c окладом более 100000")
@Test
void shouldFindAllEmployeesWithSalaryOver100000() {
List<Employee> allEmployeesWithSalaryOver100000 = employeeRepository.findAllEmployeesWithSalaryOver100000();
assertThat(allEmployeesWithSalaryOver100000).size().isEqualTo(EMPLOYEES_WITH_SALARY_OVER_100000_COUNT);
}
@DisplayName("возвращать список имен всех сотрудников")
@Test
void shouldFindEmployeesFirstNames() {
List<String> employeesFirstNames = employeeRepository.findEmployeesFirstNames();
assertThat(employeesFirstNames)
.containsExactlyInAnyOrder("fn1", "fn2", "fn3", "fn4", "fn5", "fn6", "fn7", "fn8");
}
@DisplayName("возвращать список имен и фамилий всех сотрудников")
@Test
void shouldFindEmployeesFirstAndLastNames() {
List<Object[]> employeesFirstAndLastNames = employeeRepository.findEmployeesFirstAndLastNames();
String[][] expectedFirstAndLastNames = new String[EMPLOYEES_COUNT][2];
IntStream.range(1, EMPLOYEES_COUNT + 1)
.forEachOrdered(i -> expectedFirstAndLastNames[i - 1] = new String[]{"fn" + i, "ln" + i});
assertThat(employeesFirstAndLastNames).containsExactlyInAnyOrder(expectedFirstAndLastNames);
}
@DisplayName("считать общее количество сотрудников")
@Test
void shouldCalcEmployeesCount() {
long employeesCount = employeeRepository.calcEmployeesCount();
assertThat(employeesCount).isEqualTo(EMPLOYEES_COUNT);
}
@DisplayName("находить максимальный оклад сотрудников")
@Test
void shouldFindMaxEmployeeSalary() {
BigDecimal maxSalary = employeeRepository.findMaxEmployeeSalary();
assertThat(maxSalary).isEqualTo(new BigDecimal(MAX_SALARY));
}
@DisplayName("считать средний оклад всех сотрудников")
@Test
void shouldCalcAvgEmployeeSalary() {
Double avgSalary = employeeRepository.calcAvgEmployeeSalary();
assertThat(avgSalary).isEqualTo(AVG_SALARY, offset(0.01d));
}
//-------------------------------------------------------------------------------------------------------
@DisplayName("возвращать список окладов по городам")
@Test
void shouldCalcAvgEmployeeSalaryByCities() {
List<CitySalary> avgSalaryByCities = employeeRepository.calcAvgSalaryByCities();
assertThat(avgSalaryByCities)
.containsExactlyInAnyOrder(SARATOV_SALARY, MOSCOW_SALARY, OMSK_SALARY);
}
@DisplayName("возвращать сортированный список окладов по городам")
@Test
void shouldCalcAvgEmployeeSalaryByCitiesSorted() {
List<CitySalary> avgSalaryByCities = employeeRepository.calcAvgSalaryByCitiesSorted();
assertThat(avgSalaryByCities)
.containsExactly(SARATOV_SALARY, OMSK_SALARY, MOSCOW_SALARY);
}
@DisplayName("возвращать список окладов по городам, где средний доход сотрудников более 100000")
@Test
void shouldCalcAvgEmployeeSalaryByCitiesHavingValueOver100000() {
List<CitySalary> avgSalaryByCities = employeeRepository.calcAvgSalaryByCitiesHavingValueOver100000();
assertThat(avgSalaryByCities).containsExactly(OMSK_SALARY, MOSCOW_SALARY);
}
//-------------------------------------------------------------------------------------------------------
@DisplayName("возвращать список всех сотрудников работающих над заданными проектами")
@Test
void shouldFindEmployeesWithGivenProjects() {
Employee employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID);
Employee employee4 = em.find(Employee.class, FOURTH_EMPLOYEE_ID);
List<Employee> employees = employeeRepository.findEmployeesWithGivenProjects(PROJECT_3, PROJECT_4);
assertThat(employees).hasSize(2).containsExactlyInAnyOrder(employee2, employee4);
}
@DisplayName("возвращать количество проектов по сотрудникам")
@Test
void shouldFindEmployeesProjectsCount() {
Employee employee4 = em.find(Employee.class, FOURTH_EMPLOYEE_ID);
List<EmployeeProjects> employeeProjects = employeeRepository.findEmployeesProjectsCount();
assertThat(employeeProjects).hasSize(EMPLOYEES_COUNT)
.contains(new EmployeeProjects(employee4, FOURTH_EMPLOYEE_PROJECTS_COUNT));
}
//-------------------------------------------------------------------------------------------------------
@DisplayName("возвращать список всех сотрудников имеющих одно из двух заданных имен")
@Test
void shouldFindEmployeesWithGivenFirstNames() {
Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID);
Employee employee7 = em.find(Employee.class, SEVENTH_EMPLOYEE_ID);
List<Employee> employees = employeeRepository.findEmployeesWithGivenFirstNames("fn1", "fn7");
assertThat(employees).hasSize(2).containsExactlyInAnyOrder(employee1, employee7);
}
@DisplayName("возвращать список всех сотрудников имеющих имя, совпадающиее с одним из заданного списка")
@Test
void shouldFindEmployeesWithFirstNamesFromGivenList() {
Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID);
Employee employee7 = em.find(Employee.class, SEVENTH_EMPLOYEE_ID);
List<Employee> employees = employeeRepository.findEmployeesWithFirstNamesFromGivenList(List.of("fn1", "fn7"));
assertThat(employees).hasSize(2).containsExactlyInAnyOrder(employee1, employee7);
}
@DisplayName("возвращать список всех однофамильцев заданного сотрудника")
@Test
void shouldFindEmployeesNameSakes() {
Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID);
Employee employee9 = em.persistAndFlush(new Employee(NAME_SAKE_NAME_1, employee1.getLastName()));
Employee employee10 = em.persistAndFlush(new Employee(NAME_SAKE_NAME_2, employee1.getLastName()));
List<Employee> nameSakes = employeeRepository.findEmployeeNameSakes(employee1);
assertThat(nameSakes).hasSize(2).containsExactlyInAnyOrder(employee9, employee10);
}
//-------------------------------------------------------------------------------------------------------
@DisplayName("возвращать список всех тезок заданного сотрудника")
@Test
void shouldFindEmployeeNameSake() {
Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID);
Employee employee9 = em.persistAndFlush(new Employee(employee1.getFirstName(), NAME_SAKE_NAME_1));
Employee nameSake = employeeRepository.findEmployeeNameSake(employee1);
assertThat(nameSake).isEqualToComparingFieldByField(employee9);
em.persistAndFlush(new Employee(employee1.getFirstName(), NAME_SAKE_NAME_2));
assertThatCode(() -> employeeRepository.findEmployeeNameSake(employee1))
.isInstanceOf(NonUniqueResultException.class);
}
@DisplayName("возвращать список всех сотрудников имеющих оклад меньше, чем у заданного сотрудника")
@Test
void shouldFindEmployeesWithSalaryLessThanGivenEmployee() {
Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID);
Employee employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID);
Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
Employee employee7 = em.find(Employee.class, SEVENTH_EMPLOYEE_ID);
List<Employee> employees = employeeRepository.findEmployeesWithSalaryLessThanGivenEmployee(employee7);
assertThat(employees).hasSize(3).containsExactlyInAnyOrder(employee1, employee2, employee3);
}
@DisplayName("возвращать сотрудника, являющегося тезкой любому другому сотруднику")
@Test
void shouldFindEmployeeWithNameMatchingAnyOtherEmployeesNames() {
Employee employee1 = em.find(Employee.class, FIRST_EMPLOYEE_ID);
Employee employee9 = em.persistAndFlush(new Employee(employee1.getFirstName(), NAME_SAKE_NAME_1));
List<Employee> nameSakes = employeeRepository.findEmployeeWithNameMatchingAnyOtherEmployeesNames();
assertThat(nameSakes).hasSize(2).containsExactlyInAnyOrder(employee9, employee1);
}
@DisplayName("возвращать сотрудника имеющго оклад меньше, чем у всех")
@Test
void shouldFindEmployeesWithSalaryLessThanAllEmployees() {
Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
List<Employee> employees = employeeRepository.findEmployeesWithSalaryLessThanAllEmployees();
assertThat(employees).hasSize(1).containsOnly(employee3);
}
//-------------------------------------------------------------------------------------------------------
@DisplayName("изменять значение оклада сотрудника имеющго заданный оклад")
@Test
void shouldUpdateEmployeesSalary() {
Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
BigDecimal oldSalary = employee3.getSalary();
BigDecimal newSalary = oldSalary.multiply(new BigDecimal(2));
em.detach(employee3);
employeeRepository.updateEmployeesSalary(oldSalary, newSalary);
employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
assertThat(employee3.getSalary()).isEqualTo(newSalary);
}
@DisplayName("изменять значение оклада в два раза, у сотрудника имеющго заданный оклад")
@Test
void shouldDoubleEmployeesSalary() {
Employee employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
BigDecimal oldSalary = employee3.getSalary();
BigDecimal newSalary = oldSalary.multiply(new BigDecimal(2));
em.detach(employee3);
employeeRepository.doubleEmployeesSalary(oldSalary);
employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
assertThat(employee3.getSalary()).isEqualTo(newSalary);
}
@DisplayName("удалять сотрудников не относящихся ни к одному отделу")
@Test
void shouldDeleteEmployeesWithoutDepartment() {
Employee employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID);
Employee employee8 = em.find(Employee.class, EIGTH_EMPLOYEE_ID);
assertThat(employee2).isNotNull();
assertThat(employee8).isNotNull();
employeeRepository.deleteEmployeesWithoutDepartment();
em.clear();
employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID);
employee8 = em.find(Employee.class, EIGTH_EMPLOYEE_ID);
assertThat(employee2).isNull();
assertThat(employee8).isNull();
}
}
@@ -0,0 +1,16 @@
spring:
datasource:
url: jdbc:h2:mem:testdb
initialization-mode: always
data: test-data.sql
jpa:
generate-ddl: false
hibernate:
ddl-auto: none
show-sql: true
properties:
hibernate:
#format_sql: true
@@ -0,0 +1,21 @@
INSERT INTO addresses (city) VALUES ('Saratov'), ('Omsk'), ('Moscow');
INSERT INTO departments (name) VALUES ('IT'), ('AHO');
INSERT INTO projects (name) VALUES ('Project #1'), ('Project #2'), ('Project #3'), ('Project #4');
INSERT INTO employees (first_name, last_name, salary, address_id, department_id)
VALUES ('fn1', 'ln1', 70000, 1, 1),
('fn2', 'ln2', 99998, 1, null),
('fn3', 'ln3', 30000, 1, 2),
('fn4', 'ln4', 170000, 2, 1),
('fn5', 'ln5', 120000, 3, 1),
('fn6', 'ln6', 100400, 3, 1),
('fn7', 'ln7', 100000, 3, 1),
('fn8', 'ln8', 1000000, 3, null);
INSERT INTO employees_projects (employee_id, project_id)
VALUES (1, 1), (1, 2), (1, 3),
(2, 3), (2, 4),
(4, 1), (4, 2), (4, 3), (4, 4);