From 79fd67719602be5fe3f79c2b4964b719d6a50e79 Mon Sep 17 00:00:00 2001 From: Kirill Vasilev Date: Tue, 10 Jan 2023 16:16:23 +0700 Subject: [PATCH 1/2] change embeddedpg to testcontainers --- .gitignore | 3 ++- pom.xml | 6 ++--- src/main/java/ru/hh/school/DbFactory.java | 5 ++++- src/main/resources/hibernate.properties | 1 - src/test/java/ru/hh/school/BaseTest.java | 22 +++++++++++++++---- .../ru/hh/school/batching/BatchingTest.java | 4 ++-- .../school/service/EmployerServiceTest.java | 2 +- .../hh/school/service/VacancyServiceTest.java | 4 ++-- 8 files changed, 32 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 744289d..5004c48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # Project exclude paths -/target/ \ No newline at end of file +/target/ +.idea diff --git a/pom.xml b/pom.xml index 595ab88..691d681 100644 --- a/pom.xml +++ b/pom.xml @@ -30,9 +30,9 @@ test - com.opentable.components - otj-pg-embedded - 0.13.0 + org.testcontainers + postgresql + 1.17.6 test diff --git a/src/main/java/ru/hh/school/DbFactory.java b/src/main/java/ru/hh/school/DbFactory.java index 81c94f8..d46579e 100644 --- a/src/main/java/ru/hh/school/DbFactory.java +++ b/src/main/java/ru/hh/school/DbFactory.java @@ -1,9 +1,11 @@ package ru.hh.school; +import javax.sql.DataSource; import org.hibernate.SessionFactory; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Environment; import ru.hh.school.entity.Resume; import ru.hh.school.entity.Area; import ru.hh.school.entity.Employer; @@ -19,9 +21,10 @@ public class DbFactory { Area.class ); - public static SessionFactory createSessionFactory() { + public static SessionFactory createSessionFactory(DataSource dataSource) { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .loadProperties("hibernate.properties") + .applySetting(Environment.DATASOURCE, dataSource) .build(); MetadataSources metadataSources = new MetadataSources(serviceRegistry); diff --git a/src/main/resources/hibernate.properties b/src/main/resources/hibernate.properties index e330710..e64b3e2 100644 --- a/src/main/resources/hibernate.properties +++ b/src/main/resources/hibernate.properties @@ -1,7 +1,6 @@ # datasource hibernate.connection.driver_class = org.postgresql.Driver hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect -hibernate.connection.url = jdbc:postgresql://localhost:5433/postgres hibernate.connection.username = postgres hibernate.connection.password = postgres hibernate.current_session_context_class=thread diff --git a/src/test/java/ru/hh/school/BaseTest.java b/src/test/java/ru/hh/school/BaseTest.java index 0ef00d6..197b22d 100644 --- a/src/test/java/ru/hh/school/BaseTest.java +++ b/src/test/java/ru/hh/school/BaseTest.java @@ -1,11 +1,13 @@ package ru.hh.school; -import com.opentable.db.postgres.embedded.EmbeddedPostgres; +import javax.sql.DataSource; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.junit.Before; import org.junit.BeforeClass; +import org.postgresql.ds.PGSimpleDataSource; +import org.testcontainers.containers.PostgreSQLContainer; import ru.hh.school.util.QueryInfoHolder; import java.io.IOException; import java.util.Optional; @@ -13,13 +15,25 @@ public abstract class BaseTest { - protected static EmbeddedPostgres pg; + protected static PostgreSQLContainer pgContainer; + protected static DataSource dataSource; protected static SessionFactory sessionFactory; @BeforeClass public static void setupSessionFactory() throws IOException { - pg = EmbeddedPostgres.builder().setPort(5433).start(); - sessionFactory = DbFactory.createSessionFactory(); + pgContainer = new PostgreSQLContainer<>("postgres") + .withUsername("postgres") + .withPassword("postgres"); + pgContainer.start(); + + PGSimpleDataSource pgDataSource = new PGSimpleDataSource(); + String jdbcUrl = pgContainer.getJdbcUrl(); + pgDataSource.setUrl(jdbcUrl); + pgDataSource.setUser(pgContainer.getUsername()); + pgDataSource.setPassword(pgContainer.getPassword()); + dataSource = pgDataSource; + + sessionFactory = DbFactory.createSessionFactory(dataSource); } @Before diff --git a/src/test/java/ru/hh/school/batching/BatchingTest.java b/src/test/java/ru/hh/school/batching/BatchingTest.java index a329e60..714a893 100644 --- a/src/test/java/ru/hh/school/batching/BatchingTest.java +++ b/src/test/java/ru/hh/school/batching/BatchingTest.java @@ -14,12 +14,12 @@ public class BatchingTest extends BaseTest { @BeforeClass public static void createTable() { - TestHelper.executeScript(pg.getPostgresDatabase(), "create_resume.sql"); + TestHelper.executeScript(dataSource, "create_resume.sql"); } @Before public void clearTable() { - TestHelper.execute(pg.getPostgresDatabase(), "delete from resume"); + TestHelper.execute(dataSource, "delete from resume"); } /** diff --git a/src/test/java/ru/hh/school/service/EmployerServiceTest.java b/src/test/java/ru/hh/school/service/EmployerServiceTest.java index b55d2e3..db0bb10 100644 --- a/src/test/java/ru/hh/school/service/EmployerServiceTest.java +++ b/src/test/java/ru/hh/school/service/EmployerServiceTest.java @@ -28,7 +28,7 @@ public static void setupService() { new EmployerDao(sessionFactory), new TransactionHelper(sessionFactory) ); - TestHelper.executeScript(pg.getPostgresDatabase(), "create_employers.sql"); + TestHelper.executeScript(dataSource, "create_employers.sql"); } @Test diff --git a/src/test/java/ru/hh/school/service/VacancyServiceTest.java b/src/test/java/ru/hh/school/service/VacancyServiceTest.java index b9ebc5e..c89bb4e 100644 --- a/src/test/java/ru/hh/school/service/VacancyServiceTest.java +++ b/src/test/java/ru/hh/school/service/VacancyServiceTest.java @@ -25,12 +25,12 @@ public static void setupService() { new VacancyDao(sessionFactory), new TransactionHelper(sessionFactory) ); - TestHelper.executeScript(pg.getPostgresDatabase(), "create_employers.sql"); + TestHelper.executeScript(dataSource, "create_employers.sql"); } @Test public void getSalaryStatistics() { - TestHelper.executeScript(pg.getPostgresDatabase(), "clear_data.sql"); + TestHelper.executeScript(dataSource, "clear_data.sql"); Employer employer = new Employer(); employer.setCompanyName("HH"); From 0d0cb6728a87c699e7cf96a48fab945d4b530b5b Mon Sep 17 00:00:00 2001 From: NaAN Date: Sat, 14 Jan 2023 20:27:36 +0300 Subject: [PATCH 2/2] =?UTF-8?q?1.=20=D0=9E=D1=84=D0=BE=D1=80=D0=BC=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20Area,=20Employer,=20Resume,=20Vacancy;=202.=20=D0=9D?= =?UTF-8?q?=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D1=8B=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D1=8B=20=D0=B2=20EmployerDao,=20VacancyDao;?= =?UTF-8?q?=203.=20save=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=20?= =?UTF-8?q?=D0=BD=D0=B0=20saveOrUpdate=20=D0=B2=20GenericDao;=204.=20C?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D0=BE=20=D1=81=D0=BE=D1=85=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=B8=20=D0=B5?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=B2=D0=B0=D0=BA=D0=B0=D0=BD=D1=81=D0=B8=D0=B9?= =?UTF-8?q?=20=D0=B2=20EmployerService.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/hh/school/dao/EmployerDao.java | 6 +++- .../java/ru/hh/school/dao/GenericDao.java | 5 +-- .../java/ru/hh/school/dao/VacancyDao.java | 9 ++--- src/main/java/ru/hh/school/entity/Area.java | 34 +++++++++++++++++- .../java/ru/hh/school/entity/Employer.java | 36 ++++++++++++++++--- src/main/java/ru/hh/school/entity/Resume.java | 13 +++++-- .../java/ru/hh/school/entity/Vacancy.java | 30 ++++++++++++---- .../ru/hh/school/service/EmployerService.java | 1 + 8 files changed, 111 insertions(+), 23 deletions(-) diff --git a/src/main/java/ru/hh/school/dao/EmployerDao.java b/src/main/java/ru/hh/school/dao/EmployerDao.java index 0a0d46d..eb44a70 100644 --- a/src/main/java/ru/hh/school/dao/EmployerDao.java +++ b/src/main/java/ru/hh/school/dao/EmployerDao.java @@ -18,7 +18,11 @@ public EmployerDao(SessionFactory sessionFactory) { */ public Employer getEager(int employerId) { return getSession() - .createQuery("from Employer employer", Employer.class) + .createQuery( + "SELECT e FROM Employer AS e " + + " LEFT JOIN FETCH e.vacancies" + + " WHERE e.id = :eid", Employer.class) + .setParameter("eid", employerId) .getSingleResult(); } diff --git a/src/main/java/ru/hh/school/dao/GenericDao.java b/src/main/java/ru/hh/school/dao/GenericDao.java index 70aa8bc..cd9f504 100644 --- a/src/main/java/ru/hh/school/dao/GenericDao.java +++ b/src/main/java/ru/hh/school/dao/GenericDao.java @@ -2,10 +2,7 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; - import java.io.Serializable; -import java.util.Collection; -import java.util.Objects; public class GenericDao { private final SessionFactory sessionFactory; @@ -25,7 +22,7 @@ public void save(Object object) { if (object == null) { return; } - getSession().save(object); + getSession().saveOrUpdate(object); } protected Session getSession() { diff --git a/src/main/java/ru/hh/school/dao/VacancyDao.java b/src/main/java/ru/hh/school/dao/VacancyDao.java index 195d045..d8e6a78 100644 --- a/src/main/java/ru/hh/school/dao/VacancyDao.java +++ b/src/main/java/ru/hh/school/dao/VacancyDao.java @@ -4,17 +4,18 @@ import ru.hh.school.employers.StatisticsDto; import ru.hh.school.entity.Area; -public class VacancyDao extends GenericDao{ +public class VacancyDao extends GenericDao { public VacancyDao(SessionFactory sessionFactory) { super(sessionFactory); } - public StatisticsDto getSalaryStatistics(Area area){ + public StatisticsDto getSalaryStatistics(Area area) { // ToDo дополните запрос, чтобы возвращался ru.hh.school.employers.StatisticsDto // https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/ return getSession().createQuery( - "SELECT count(v.id), min(v.compensationFrom), max(v.compensationTo) " + - "FROM Vacancy v WHERE v.area = :area", StatisticsDto.class) + "SELECT new ru.hh.school.employers.StatisticsDto(COUNT(v.id), MIN(v.compensationFrom), MAX(v.compensationTo))" + + "FROM Vacancy AS v " + + "WHERE v.area = :area", StatisticsDto.class) .setParameter("area", area) .getSingleResult(); } diff --git a/src/main/java/ru/hh/school/entity/Area.java b/src/main/java/ru/hh/school/entity/Area.java index 2288dd9..065d179 100644 --- a/src/main/java/ru/hh/school/entity/Area.java +++ b/src/main/java/ru/hh/school/entity/Area.java @@ -1,11 +1,26 @@ package ru.hh.school.entity; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + //TODO: оформите entity +@Entity +@Table(name = "area") public class Area { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "area_id") private Integer id; - + @Column(name = "name", nullable = false) private String name; + public Area() { + } + public String getName() { return name; } @@ -13,4 +28,21 @@ public String getName() { public void setName(String name) { this.name = name; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Area area = (Area) o; + + return name.equals(area.getName()); + } + + @Override + public int hashCode() { + int hash = id * id; + hash = 31 * hash + (name == null ? 0 : name.hashCode()); + return 31 * hash; + } } diff --git a/src/main/java/ru/hh/school/entity/Employer.java b/src/main/java/ru/hh/school/entity/Employer.java index 152a66c..8bdca20 100644 --- a/src/main/java/ru/hh/school/entity/Employer.java +++ b/src/main/java/ru/hh/school/entity/Employer.java @@ -1,23 +1,45 @@ package ru.hh.school.entity; +import com.sun.istack.NotNull; +import org.hibernate.annotations.CreationTimestamp; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import java.util.Objects; //TODO: оформите entity +@Entity +@Table(name = "employer") public class Employer { - + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "employer_id") private Integer id; + @Column(name = "company_name") + @NotNull private String companyName; // не используйте java.util.Date // https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#basic-datetime-java8 + @CreationTimestamp + @Column(name = "creation_time", nullable = false, updatable = false) private LocalDateTime creationTime; + @OneToMany(mappedBy = "employer", + cascade = {CascadeType.ALL}, + orphanRemoval = true) private List vacancies = new ArrayList<>(); + @Column(name = "block_time") private LocalDateTime blockTime; public List getVacancies() { @@ -48,17 +70,21 @@ public void setBlockTime(LocalDateTime blockTime) { // // https://vladmihalcea.com/hibernate-facts-equals-and-hashcode/ // https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + Employer employer = (Employer) o; - return Objects.equals(companyName, employer.companyName); + + return id.equals(employer.getId()) && companyName.equals(employer.getCompanyName()); } @Override public int hashCode() { - return Objects.hash(companyName); + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (companyName != null ? companyName.hashCode() : 0); + return result; } - } diff --git a/src/main/java/ru/hh/school/entity/Resume.java b/src/main/java/ru/hh/school/entity/Resume.java index 7310dda..1b5691a 100644 --- a/src/main/java/ru/hh/school/entity/Resume.java +++ b/src/main/java/ru/hh/school/entity/Resume.java @@ -1,9 +1,16 @@ package ru.hh.school.entity; +import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.GenerationType; +import javax.persistence.Column; +import javax.persistence.SequenceGenerator; //TODO: оформите entity +@Entity +@Table(name = "resume") public class Resume { // TODO: сделать так, чтобы id брался из sequence-а // таким образом, мы сможем отправлять в бд запросы батчами. @@ -15,12 +22,14 @@ public class Resume { // https://vladmihalcea.com/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/ @Id - @GeneratedValue(/* здесь место для вашего кода */) + @GeneratedValue(generator = "sequence", strategy = GenerationType.SEQUENCE) + @SequenceGenerator(name = "sequence", sequenceName = "resume_id_seq", allocationSize = 10) private Integer id; + @Column(name = "description") private String description; - Resume() {} + public Resume() {} public Resume(String description) { this.description = description; diff --git a/src/main/java/ru/hh/school/entity/Vacancy.java b/src/main/java/ru/hh/school/entity/Vacancy.java index 8d017b4..5221043 100644 --- a/src/main/java/ru/hh/school/entity/Vacancy.java +++ b/src/main/java/ru/hh/school/entity/Vacancy.java @@ -10,29 +10,43 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; import java.time.LocalDateTime; -import java.util.Objects; //TODO: оформите entity +@Entity +@Table(name = "vacancy") public class Vacancy { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "vacancy_id") private Integer id; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "employer_id") private Employer employer; - + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "area_id") private Area area; + @Column(name = "title") private String title; + @Column(name = "description") private String description; + @Column(name = "compensation_from") private Integer compensationFrom; - private Integer compensationTo; + @Column(name = "compensation_to") + private Integer compensationTo; + @Column(name = "compensation_gross") private Boolean compensationGross; + @Column(name = "creation_time") private LocalDateTime creationTime; + @Column(name = "archiving_time") private LocalDateTime archivingTime; public Vacancy() { @@ -94,13 +108,17 @@ public void setArchivingTime(LocalDateTime archivingTime) { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + Vacancy vacancy = (Vacancy) o; - return Objects.equals(id, vacancy.id); + + return id.equals(vacancy.getId()) && title.equals(vacancy.getTitle()); } @Override public int hashCode() { - return 17; + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + (description != null ? description.hashCode() : 0); + return result; } - } diff --git a/src/main/java/ru/hh/school/service/EmployerService.java b/src/main/java/ru/hh/school/service/EmployerService.java index 4822f0d..99b592c 100644 --- a/src/main/java/ru/hh/school/service/EmployerService.java +++ b/src/main/java/ru/hh/school/service/EmployerService.java @@ -69,6 +69,7 @@ public void blockIfEmployerUseBadWords(int employerId) { transactionHelper.inTransaction(() -> { employer.setBlockTime(LocalDateTime.now()); employer.getVacancies().forEach(v -> v.setArchivingTime(LocalDateTime.now())); + genericDao.save(employer); }); }