mirror of
https://github.com/OtusTeam/Spring.git
synced 2026-06-20 11:14:34 +00:00
jpql-demo example has ben added
This commit is contained in:
+2
-1
@@ -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
|
||||
@@ -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,6 @@
|
||||
## Пример работы с JPQL
|
||||
|
||||
В примере демонстрируется:
|
||||
* *репозитории на Spring ORM с использованием JPA и JPQL*
|
||||
* *использование JPQL для написания разного рода запросов (в т.ч. для выборки, агрегации, изменения и удаления данных)*
|
||||
* *тестирование репозиториев на Spring ORM с использованием @DataJpaTest*
|
||||
@@ -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;
|
||||
}
|
||||
+49
@@ -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();
|
||||
|
||||
|
||||
|
||||
}
|
||||
+237
@@ -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
|
||||
);
|
||||
+282
@@ -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);
|
||||
Reference in New Issue
Block a user