From cd8b931995e6659614832c5a22954c49cf12913b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=81=D0=B8=D0=BB=D1=8C=D0=B5=D0=B2=20=D0=90?= =?UTF-8?q?=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=93=D0=B5=D0=BE=D1=80=D0=B3?= =?UTF-8?q?=D0=B8=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 4 Jun 2025 13:58:59 +0500 Subject: [PATCH 1/2] =?UTF-8?q?=D0=94=D0=97-28:=20springBoot=20-=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- L28-springDataJdbc/build.gradle.kts | 19 ++ L28-springDataJdbc/docker/runDb.sh | 6 + .../src/main/java/ru/otus/ActionDemo.java | 167 ++++++++++++++++++ .../src/main/java/ru/otus/DbServiceDemo.java | 11 ++ .../main/java/ru/otus/crm/model/Client.java | 15 ++ .../java/ru/otus/crm/model/ClientDetails.java | 8 + .../main/java/ru/otus/crm/model/Manager.java | 65 +++++++ .../java/ru/otus/crm/model/TableWithPk.java | 59 +++++++ .../otus/crm/repository/ClientRepository.java | 20 +++ .../crm/repository/ManagerRepository.java | 29 +++ .../ManagerResultSetExtractorClass.java | 42 +++++ .../crm/repository/TableWithPkRepository.java | 21 +++ .../ru/otus/crm/service/DBServiceClient.java | 14 ++ .../ru/otus/crm/service/DBServiceManager.java | 14 ++ .../otus/crm/service/DbServiceClientImpl.java | 48 +++++ .../crm/service/DbServiceManagerImpl.java | 47 +++++ .../sessionmanager/TransactionAction.java | 5 + .../sessionmanager/TransactionManager.java | 6 + .../TransactionManagerSpring.java | 14 ++ .../src/main/resources/application.yml | 11 ++ .../db/migration/V1__initial_schema.sql | 28 +++ settings.gradle.kts | 1 + 22 files changed, 650 insertions(+) create mode 100644 L28-springDataJdbc/build.gradle.kts create mode 100755 L28-springDataJdbc/docker/runDb.sh create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java create mode 100644 L28-springDataJdbc/src/main/resources/application.yml create mode 100644 L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql diff --git a/L28-springDataJdbc/build.gradle.kts b/L28-springDataJdbc/build.gradle.kts new file mode 100644 index 0000000..3ec11fe --- /dev/null +++ b/L28-springDataJdbc/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id ("org.springframework.boot") +} + + +dependencies { + dependencies { + implementation ("ch.qos.logback:logback-classic") + implementation ("org.flywaydb:flyway-core") + implementation ("org.postgresql:postgresql") + implementation ("com.google.code.findbugs:jsr305") + implementation ("org.springframework.boot:spring-boot-starter-data-jdbc") + + runtimeOnly("org.flywaydb:flyway-database-postgresql") + + compileOnly ("org.projectlombok:lombok") + annotationProcessor ("org.projectlombok:lombok") + } +} diff --git a/L28-springDataJdbc/docker/runDb.sh b/L28-springDataJdbc/docker/runDb.sh new file mode 100755 index 0000000..f2c3084 --- /dev/null +++ b/L28-springDataJdbc/docker/runDb.sh @@ -0,0 +1,6 @@ +docker run --rm --name pg-docker \ +-e POSTGRES_PASSWORD=pwd \ +-e POSTGRES_USER=usr \ +-e POSTGRES_DB=demoDB \ +-p 5430:5432 \ +postgres:17 \ No newline at end of file diff --git a/L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java b/L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java new file mode 100644 index 0000000..bcb6ae6 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java @@ -0,0 +1,167 @@ +package ru.otus; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; +import ru.otus.crm.model.Client; +import ru.otus.crm.model.ClientDetails; +import ru.otus.crm.model.Manager; +import ru.otus.crm.model.TableWithPk; +import ru.otus.crm.repository.ClientRepository; +import ru.otus.crm.repository.ManagerRepository; +import ru.otus.crm.repository.TableWithPkRepository; +import ru.otus.crm.service.DBServiceClient; +import ru.otus.crm.service.DBServiceManager; + +@Component("actionDemo") +public class ActionDemo implements CommandLineRunner { + private static final Logger log = LoggerFactory.getLogger(ActionDemo.class); + + private final ClientRepository clientRepository; + private final ManagerRepository managerRepository; + private final DBServiceClient dbServiceClient; + private final DBServiceManager dbServiceManager; + + private final TableWithPkRepository tableWithPkRepository; + + public ActionDemo( + ClientRepository clientRepository, + ManagerRepository managerRepository, + DBServiceClient dbServiceClient, + DBServiceManager dbServiceManager, + TableWithPkRepository tableWithPkRepository) { + this.managerRepository = managerRepository; + this.clientRepository = clientRepository; + this.dbServiceClient = dbServiceClient; + this.dbServiceManager = dbServiceManager; + this.tableWithPkRepository = tableWithPkRepository; + } + + @Override + public void run(String... args) { + + //// создаем Manager + log.info(">>> manager creation"); + dbServiceManager.saveManager(new Manager( + "m:" + System.currentTimeMillis(), "ManagerFirst", new HashSet<>(), new ArrayList<>(), true)); + + var managerName = "m:" + System.currentTimeMillis(); + var managerSecond = dbServiceManager.saveManager(new Manager( + managerName, + "ManagerSecond", + Set.of( + new Client(null, "managClient1", managerName, 11, new ClientDetails(null, "inf01")), + new Client(null, "managClient2", managerName, 22, new ClientDetails(null, "info2"))), + List.of( + new Client(null, "managClient1Ordered", managerName, 100, new ClientDetails(null, "inf01")), + new Client(null, "managClient2Ordered", managerName, 200, new ClientDetails(null, "info2"))), + true)); + log.info(">>> get Manager:{}", managerSecond.getId()); + var managerSecondSelected = dbServiceManager + .getManager(managerSecond.getId()) + .orElseThrow(() -> new RuntimeException("Manager not found, id:" + managerSecond.getId())); + log.info(">>> managerSecondSelected:{}", managerSecondSelected); + + //// обновляем Manager с удалением его клиентов + dbServiceManager.saveManager(new Manager( + managerSecondSelected.getId(), "dbServiceSecondUpdated", new HashSet<>(), new ArrayList<>(), false)); + var managerUpdated = dbServiceManager + .getManager(managerSecondSelected.getId()) + .orElseThrow(() -> new RuntimeException("Manager not found, id:" + managerSecondSelected.getId())); + log.info(">>> managerUpdated:{}", managerUpdated); + + /// создаем Client + var firstClient = dbServiceClient.saveClient(new Client( + null, + "dbServiceFirst" + System.currentTimeMillis(), + managerSecond.getId(), + 1, + new ClientDetails(null, "init1"))); + + var clientSecond = dbServiceClient.saveClient(new Client( + null, + "dbServiceSecond" + System.currentTimeMillis(), + managerSecond.getId(), + 2, + new ClientDetails(null, "init2"))); + var clientSecondSelected = dbServiceClient + .getClient(clientSecond.id()) + .orElseThrow(() -> new RuntimeException("Client not found, id:" + clientSecond.id())); + log.info(">>> clientSecondSelected:{}", clientSecondSelected); + + log.info("delete instead of update"); + /// обновляем Client + dbServiceClient.saveClient(new Client( + clientSecondSelected.id(), + "dbServiceSecondUpdated", + managerSecond.getId(), + clientSecondSelected.orderColumn(), + new ClientDetails(null, "updated"))); + var clientUpdated = dbServiceClient + .getClient(clientSecondSelected.id()) + .orElseThrow(() -> new RuntimeException("Client not found, id:" + clientSecondSelected.id())); + log.info("clientUpdated:{}", clientUpdated); + + /// получаем все сущности + log.info(">>> All clients"); + dbServiceClient.findAll().forEach(client -> log.info("client:{}", client)); + + log.info(">>> All managers"); + dbServiceManager.findAll().forEach(manager -> log.info("manager:{}", manager)); + + /// применяем переопределенные методы репозитариев + var clientFoundByName = clientRepository + .findByName(firstClient.name()) + .orElseThrow(() -> new RuntimeException("client not found, name:" + firstClient.name())); + log.info("clientFoundByName:{}", clientFoundByName); + + var clientFoundByNameIgnoreCase = clientRepository + .findByNameIgnoreCase(firstClient.name().toLowerCase()) + .orElseThrow(() -> new RuntimeException("client not found, name:" + firstClient.name())); + log.info("clientFoundByNameIgnoreCase:{}", clientFoundByNameIgnoreCase); + + clientRepository.updateName(firstClient.id(), "newName"); + var updatedClient = + clientRepository.findById(firstClient.id()).orElseThrow(() -> new RuntimeException("client not found")); + + log.info(">>> updatedClient:{}", updatedClient); + + /// проверяем проблему N+1 + log.info(">>> checking N+1 problem"); + var managerId = managerSecond.getId(); + if (managerId == null) { + throw new IllegalArgumentException("managerId can't be null"); + } + var managerN = managerRepository + .findById(managerId) + .orElseThrow(() -> new RuntimeException("Manager not found, name:" + managerSecond.getId())); + log.info(">>> managerN:{}", managerN); + + /* + получаем основную сущность: + [SELECT "manager"."id" AS "id", "manager"."label" AS "label" FROM "manager" WHERE "manager"."id" = ?] + получаем дочерние: + [SELECT "client"."id" AS "id", "client"."name" AS "name", "client"."manager_id" AS "manager_id" FROM "client" WHERE "client"."manager_id" = ?] + */ + log.info(">>> select all"); + var allManagers = managerRepository.findAll(); + log.info(">>> allManagers.size():{}", allManagers.size()); + log.info(">>> allManagers:{}", allManagers); + + /// + log.info(">>> TableWithPkRepository"); + var pk = new TableWithPk.Pk("p1", String.valueOf(System.currentTimeMillis())); + TableWithPk tableWithPk = new TableWithPk(pk, "value", true); + log.info("tableWithPk:{}", tableWithPk); + + tableWithPkRepository.saveEntry(tableWithPk); + + TableWithPk loadedTableWithPk = tableWithPkRepository.findById(pk).orElseThrow(); + log.info("loadedTableWithPk:{}", loadedTableWithPk); + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java b/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java new file mode 100644 index 0000000..25715a4 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java @@ -0,0 +1,11 @@ +package ru.otus; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DbServiceDemo { + public static void main(String[] args) { + SpringApplication.run(DbServiceDemo.class, args); + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java new file mode 100644 index 0000000..23b5833 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java @@ -0,0 +1,15 @@ +package ru.otus.crm.model; + +import jakarta.annotation.Nonnull; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.MappedCollection; +import org.springframework.data.relational.core.mapping.Table; + +@Table("client") +public record Client( + @Id @Column("id") Long id, + @Nonnull String name, + String managerId, + Integer orderColumn, + @MappedCollection(idColumn = "client_id") ClientDetails clientDetails) {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java new file mode 100644 index 0000000..2bd1551 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java @@ -0,0 +1,8 @@ +package ru.otus.crm.model; + +import jakarta.annotation.Nonnull; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +@Table("client_details") +public record ClientDetails(@Id Long clientId, @Nonnull String info) {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java new file mode 100644 index 0000000..44bed3f --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java @@ -0,0 +1,65 @@ +package ru.otus.crm.model; + +import jakarta.annotation.Nonnull; +import java.util.List; +import java.util.Set; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceCreator; +import org.springframework.data.annotation.Transient; +import org.springframework.data.domain.Persistable; +import org.springframework.data.relational.core.mapping.MappedCollection; +import org.springframework.data.relational.core.mapping.Table; + +@Table("manager") +@Getter +public class Manager implements Persistable { + + @Id + @Nonnull + private final String id; + + private final String label; + + @MappedCollection(idColumn = "manager_id") + private final Set clients; + + @MappedCollection(idColumn = "manager_id", keyColumn = "order_column") + private final List clientsOrdered; + + @Transient + private final boolean isNew; + + public Manager(String id, String label, Set clients, List clientsOrdered, boolean isNew) { + this.id = id; + this.label = label; + this.clients = clients; + this.clientsOrdered = clientsOrdered; + this.isNew = isNew; + } + + @PersistenceCreator + private Manager(String id, String label, Set clients, List clientsOrdered) { + this(id, label, clients, clientsOrdered, false); + } + + @Override + public boolean isNew() { + return isNew; + } + + @Override + public String getId() { + return id; + } + + @Override + public String toString() { + return "Manager{" + "id='" + + id + '\'' + ", label='" + + label + '\'' + ", clients=" + + clients + ", clientsOrdered=" + + clientsOrdered + ", isNew=" + + isNew + '}'; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java new file mode 100644 index 0000000..86e548f --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java @@ -0,0 +1,59 @@ +package ru.otus.crm.model; + +import static org.springframework.data.relational.core.mapping.Embedded.OnEmpty.USE_EMPTY; + +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceCreator; +import org.springframework.data.annotation.Transient; +import org.springframework.data.domain.Persistable; +import org.springframework.data.relational.core.mapping.Embedded; +import org.springframework.data.relational.core.mapping.Table; + +@Table("table_with_pk") +public class TableWithPk implements Persistable { + + @Id + @Embedded(onEmpty = USE_EMPTY) + private final Pk pk; + + private final String value; + + @Transient + private final boolean isNew; + + @PersistenceCreator + public TableWithPk(Pk pk, String value) { + this(pk, value, false); + } + + public TableWithPk(Pk pk, String value, boolean isNew) { + this.pk = pk; + this.value = value; + this.isNew = isNew; + } + + @Override + public Pk getId() { + return pk; + } + + @Override + public boolean isNew() { + return isNew; + } + + public Pk getPk() { + return getId(); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return "TableWithPk{" + "pk=" + pk + ", value='" + value + '\'' + ", isNew=" + isNew + '}'; + } + + public record Pk(String idPart1, String idPart2) {} +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java new file mode 100644 index 0000000..615a2c4 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java @@ -0,0 +1,20 @@ +package ru.otus.crm.repository; + +import java.util.Optional; +import org.springframework.data.jdbc.repository.query.Modifying; +import org.springframework.data.jdbc.repository.query.Query; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.query.Param; +import ru.otus.crm.model.Client; + +public interface ClientRepository extends ListCrudRepository { + + Optional findByName(String name); + + @Query("select * from client where upper(name) = upper(:name)") + Optional findByNameIgnoreCase(@Param("name") String name); + + @Modifying + @Query("update client set name = :newName where id = :id") + void updateName(@Param("id") long id, @Param("newName") String newName); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java new file mode 100644 index 0000000..edfd786 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java @@ -0,0 +1,29 @@ +package ru.otus.crm.repository; + +import java.util.List; +import org.springframework.data.repository.ListCrudRepository; +import ru.otus.crm.model.Manager; + +public interface ManagerRepository extends ListCrudRepository { + + // закоментируйте, чтобы получить N+1 + /* + @Override + @Query(value = """ + select m.id as manager_id, + m.label as manager_label, + c.id as client_id, + c.name as client_name, + c.order_column as order_column, + cd.info as client_info + from manager m + left outer join client c + on m.id = c.manager_id + left outer join client_details cd + on cd.client_id = c.id + order by m.id + """, + resultSetExtractorClass = ManagerResultSetExtractorClass.class) + */ + List findAll(); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java new file mode 100644 index 0000000..73ae69e --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java @@ -0,0 +1,42 @@ +package ru.otus.crm.repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import ru.otus.crm.model.Client; +import ru.otus.crm.model.ClientDetails; +import ru.otus.crm.model.Manager; + +public class ManagerResultSetExtractorClass implements ResultSetExtractor> { + + @Override + public List extractData(ResultSet rs) throws SQLException, DataAccessException { + var managerList = new ArrayList(); + String prevManagerId = null; + while (rs.next()) { + var managerId = rs.getString("manager_id"); + Manager manager = null; + if (prevManagerId == null || !prevManagerId.equals(managerId)) { + manager = new Manager( + managerId, rs.getString("manager_label"), new HashSet<>(), new ArrayList<>(), false); + managerList.add(manager); + prevManagerId = managerId; + } + Long clientId = (Long) rs.getObject("client_id"); + if (manager != null && clientId != null) { + manager.getClients() + .add(new Client( + clientId, + rs.getString("client_name"), + managerId, + rs.getInt("order_column"), + new ClientDetails(clientId, rs.getString("client_info")))); + } + } + return managerList; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java new file mode 100644 index 0000000..711ace3 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java @@ -0,0 +1,21 @@ +package ru.otus.crm.repository; + +import java.util.Optional; +import javax.annotation.Nonnull; +import org.springframework.data.jdbc.repository.query.Modifying; +import org.springframework.data.jdbc.repository.query.Query; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.query.Param; +import ru.otus.crm.model.TableWithPk; + +public interface TableWithPkRepository extends ListCrudRepository { + + @Modifying + @Query( + "insert into table_with_pk(id_part1, id_part2, value) VALUES (:#{#tableWithPk.pk.idPart1}, :#{#tableWithPk.pk.idPart2}, :#{#tableWithPk.value})") + void saveEntry(@Nonnull @Param("tableWithPk") TableWithPk tableWithPk); + + @Override + @Query("select * from table_with_pk where id_part1 = :#{#pk.idPart1} and id_part2 = :#{#pk.idPart2}") + Optional findById(@Param("pk") TableWithPk.Pk pk); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java new file mode 100644 index 0000000..ed7155c --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java @@ -0,0 +1,14 @@ +package ru.otus.crm.service; + +import java.util.List; +import java.util.Optional; +import ru.otus.crm.model.Client; + +public interface DBServiceClient { + + Client saveClient(Client client); + + Optional getClient(long id); + + List findAll(); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java new file mode 100644 index 0000000..bc31abe --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java @@ -0,0 +1,14 @@ +package ru.otus.crm.service; + +import java.util.List; +import java.util.Optional; +import ru.otus.crm.model.Manager; + +public interface DBServiceManager { + + Manager saveManager(Manager client); + + Optional getManager(String no); + + List findAll(); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java new file mode 100644 index 0000000..1f6c48f --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java @@ -0,0 +1,48 @@ +package ru.otus.crm.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import ru.otus.crm.model.Client; +import ru.otus.crm.repository.ClientRepository; +import ru.otus.sessionmanager.TransactionManager; + +@Service +public class DbServiceClientImpl implements DBServiceClient { + private static final Logger log = LoggerFactory.getLogger(DbServiceClientImpl.class); + + private final TransactionManager transactionManager; + private final ClientRepository clientRepository; + + public DbServiceClientImpl(TransactionManager transactionManager, ClientRepository clientRepository) { + this.transactionManager = transactionManager; + this.clientRepository = clientRepository; + } + + @Override + public Client saveClient(Client client) { + return transactionManager.doInTransaction(() -> { + var savedClient = clientRepository.save(client); + log.info("saved client: {}", savedClient); + return savedClient; + }); + } + + @Override + public Optional getClient(long id) { + var clientOptional = clientRepository.findById(id); + log.info("client: {}", clientOptional); + return clientOptional; + } + + @Override + public List findAll() { + var clientList = new ArrayList(); + clientRepository.findAll().forEach(clientList::add); + log.info("clientList:{}", clientList); + return clientList; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java new file mode 100644 index 0000000..ab554d5 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java @@ -0,0 +1,47 @@ +package ru.otus.crm.service; + +import java.util.List; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import ru.otus.crm.model.Manager; +import ru.otus.crm.repository.ManagerRepository; +import ru.otus.sessionmanager.TransactionManager; + +@Service +public class DbServiceManagerImpl implements DBServiceManager { + private static final Logger log = LoggerFactory.getLogger(DbServiceManagerImpl.class); + + private final ManagerRepository managerRepository; + private final TransactionManager transactionManager; + + public DbServiceManagerImpl(ManagerRepository managerRepository, TransactionManager transactionManager) { + this.managerRepository = managerRepository; + this.transactionManager = transactionManager; + } + + @Override + public Manager saveManager(Manager manager) { + return transactionManager.doInTransaction(() -> { + var savedManager = managerRepository.save(manager); + + log.info("savedManager manager: {}", savedManager); + return savedManager; + }); + } + + @Override + public Optional getManager(String no) { + var managerOptional = managerRepository.findById(no); + log.info("manager: {}", managerOptional); + return managerOptional; + } + + @Override + public List findAll() { + var managerList = managerRepository.findAll(); + log.info("managerList:{}", managerList); + return managerList; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java new file mode 100644 index 0000000..33b647b --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java @@ -0,0 +1,5 @@ +package ru.otus.sessionmanager; + +import java.util.function.Supplier; + +public interface TransactionAction extends Supplier {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java new file mode 100644 index 0000000..06ba3da --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java @@ -0,0 +1,6 @@ +package ru.otus.sessionmanager; + +public interface TransactionManager { + + T doInTransaction(TransactionAction action); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java new file mode 100644 index 0000000..b916588 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java @@ -0,0 +1,14 @@ +package ru.otus.sessionmanager; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class TransactionManagerSpring implements TransactionManager { + + @Override + @Transactional + public T doInTransaction(TransactionAction action) { + return action.get(); + } +} diff --git a/L28-springDataJdbc/src/main/resources/application.yml b/L28-springDataJdbc/src/main/resources/application.yml new file mode 100644 index 0000000..e745683 --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + datasource: + url: jdbc:postgresql://localhost:5430/demoDB + username: usr + password: pwd + flyway: + enabled: true + +logging: + level: + org.springframework.jdbc.core.JdbcTemplate: TRACE \ No newline at end of file diff --git a/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql b/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql new file mode 100644 index 0000000..619fb78 --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql @@ -0,0 +1,28 @@ +create table Manager +( + id varchar(50) not null primary key, + label varchar(50) +); + +create table client +( + id bigserial not null primary key, + order_column int not null, + name varchar(50) not null, + manager_id varchar(50) not null references Manager (id) +); +create index idx_client_manager_id on client (manager_id); + +create table client_details +( + client_id bigint not null references client (id), + info varchar(50) not null +); + +create table table_with_pk +( + id_part1 varchar(10), + id_part2 varchar(100), + value varchar(100) +); +alter table table_with_pk add primary key (id_part1, id_part2); diff --git a/settings.gradle.kts b/settings.gradle.kts index ae12322..658c4b0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,7 @@ include( "L38-webflux-chat:client-service", "L38-webflux-chat:datastore-service", "L24-webServer", + "L28-springDataJdbc", ) From 77308c2686bb7c9aa87a332a292ebd390e220cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=81=D0=B8=D0=BB=D1=8C=D0=B5=D0=B2=20=D0=90?= =?UTF-8?q?=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=93=D0=B5=D0=BE=D1=80=D0=B3?= =?UTF-8?q?=D0=B8=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 4 Jun 2025 15:05:08 +0500 Subject: [PATCH 2/2] =?UTF-8?q?=D0=94=D0=97-28:=20springBoot=20-=20done?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- L28-springDataJdbc/build.gradle.kts | 6 + .../src/main/java/ru/otus/ActionDemo.java | 167 ----------------- .../src/main/java/ru/otus/AppRunner.java | 53 ++++++ .../src/main/java/ru/otus/DbServiceDemo.java | 12 ++ .../ru/otus/controller/ClientsController.java | 27 +++ .../controller/ClientsRestController.java | 124 +++++++++++++ .../java/ru/otus/controller/JsonResponse.java | 19 ++ .../ru/otus/controller/UsersController.java | 29 +++ .../otus/controller/UsersRestController.java | 48 +++++ .../main/java/ru/otus/crm/model/Client.java | 15 -- .../java/ru/otus/crm/model/ClientDetails.java | 8 - .../main/java/ru/otus/crm/model/Manager.java | 65 ------- .../java/ru/otus/crm/model/TableWithPk.java | 59 ------ .../otus/crm/repository/ClientRepository.java | 20 -- .../crm/repository/ManagerRepository.java | 29 --- .../ManagerResultSetExtractorClass.java | 42 ----- .../crm/repository/TableWithPkRepository.java | 21 --- .../ru/otus/crm/service/DBServiceClient.java | 14 -- .../ru/otus/crm/service/DBServiceManager.java | 14 -- .../otus/crm/service/DbServiceClientImpl.java | 48 ----- .../crm/service/DbServiceManagerImpl.java | 47 ----- .../src/main/java/ru/otus/model/Address.java | 25 +++ .../src/main/java/ru/otus/model/Client.java | 38 ++++ .../src/main/java/ru/otus/model/Phone.java | 33 ++++ .../java/ru/otus/model/RequestAddPhone.java | 12 ++ .../ru/otus/model/RequestCreateClient.java | 14 ++ .../java/ru/otus/model/RequestCreateUser.java | 14 ++ .../java/ru/otus/model/RequestEditClient.java | 12 ++ .../src/main/java/ru/otus/model/User.java | 35 ++++ .../ru/otus/repository/AddressRepository.java | 6 + .../ru/otus/repository/ClientRepository.java | 6 + .../ru/otus/repository/PhoneRepository.java | 9 + .../ru/otus/repository/UserRepository.java | 6 + .../sessionmanager/TransactionAction.java | 5 - .../sessionmanager/TransactionManager.java | 6 - .../TransactionManagerSpring.java | 14 -- .../db/migration/V1__initial_schema.sql | 37 ++-- .../src/main/resources/logback.xml | 18 ++ .../src/main/resources/static/img/login.png | Bin 0 -> 16475 bytes .../src/main/resources/static/img/logo.svg | 1 + .../src/main/resources/static/index.html | 14 ++ .../src/main/resources/templates/clients.html | 174 ++++++++++++++++++ .../src/main/resources/templates/users.html | 91 +++++++++ build.gradle.kts | 2 + gradle.properties | 1 + 45 files changed, 848 insertions(+), 592 deletions(-) delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/AppRunner.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsController.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsRestController.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/controller/JsonResponse.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/controller/UsersController.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/controller/UsersRestController.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/Address.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/Client.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/Phone.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/RequestAddPhone.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateClient.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateUser.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/RequestEditClient.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/model/User.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/repository/AddressRepository.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/repository/ClientRepository.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/repository/PhoneRepository.java create mode 100644 L28-springDataJdbc/src/main/java/ru/otus/repository/UserRepository.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java delete mode 100644 L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java create mode 100644 L28-springDataJdbc/src/main/resources/logback.xml create mode 100644 L28-springDataJdbc/src/main/resources/static/img/login.png create mode 100644 L28-springDataJdbc/src/main/resources/static/img/logo.svg create mode 100644 L28-springDataJdbc/src/main/resources/static/index.html create mode 100644 L28-springDataJdbc/src/main/resources/templates/clients.html create mode 100644 L28-springDataJdbc/src/main/resources/templates/users.html diff --git a/L28-springDataJdbc/build.gradle.kts b/L28-springDataJdbc/build.gradle.kts index 3ec11fe..9daff46 100644 --- a/L28-springDataJdbc/build.gradle.kts +++ b/L28-springDataJdbc/build.gradle.kts @@ -10,6 +10,12 @@ dependencies { implementation ("org.postgresql:postgresql") implementation ("com.google.code.findbugs:jsr305") implementation ("org.springframework.boot:spring-boot-starter-data-jdbc") + implementation("org.postgresql:postgresql") + + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-thymeleaf") + implementation("org.springframework.boot:spring-boot-starter-data-jdbc") + implementation("org.flywaydb:flyway-core") runtimeOnly("org.flywaydb:flyway-database-postgresql") diff --git a/L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java b/L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java deleted file mode 100644 index bcb6ae6..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/ActionDemo.java +++ /dev/null @@ -1,167 +0,0 @@ -package ru.otus; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.CommandLineRunner; -import org.springframework.stereotype.Component; -import ru.otus.crm.model.Client; -import ru.otus.crm.model.ClientDetails; -import ru.otus.crm.model.Manager; -import ru.otus.crm.model.TableWithPk; -import ru.otus.crm.repository.ClientRepository; -import ru.otus.crm.repository.ManagerRepository; -import ru.otus.crm.repository.TableWithPkRepository; -import ru.otus.crm.service.DBServiceClient; -import ru.otus.crm.service.DBServiceManager; - -@Component("actionDemo") -public class ActionDemo implements CommandLineRunner { - private static final Logger log = LoggerFactory.getLogger(ActionDemo.class); - - private final ClientRepository clientRepository; - private final ManagerRepository managerRepository; - private final DBServiceClient dbServiceClient; - private final DBServiceManager dbServiceManager; - - private final TableWithPkRepository tableWithPkRepository; - - public ActionDemo( - ClientRepository clientRepository, - ManagerRepository managerRepository, - DBServiceClient dbServiceClient, - DBServiceManager dbServiceManager, - TableWithPkRepository tableWithPkRepository) { - this.managerRepository = managerRepository; - this.clientRepository = clientRepository; - this.dbServiceClient = dbServiceClient; - this.dbServiceManager = dbServiceManager; - this.tableWithPkRepository = tableWithPkRepository; - } - - @Override - public void run(String... args) { - - //// создаем Manager - log.info(">>> manager creation"); - dbServiceManager.saveManager(new Manager( - "m:" + System.currentTimeMillis(), "ManagerFirst", new HashSet<>(), new ArrayList<>(), true)); - - var managerName = "m:" + System.currentTimeMillis(); - var managerSecond = dbServiceManager.saveManager(new Manager( - managerName, - "ManagerSecond", - Set.of( - new Client(null, "managClient1", managerName, 11, new ClientDetails(null, "inf01")), - new Client(null, "managClient2", managerName, 22, new ClientDetails(null, "info2"))), - List.of( - new Client(null, "managClient1Ordered", managerName, 100, new ClientDetails(null, "inf01")), - new Client(null, "managClient2Ordered", managerName, 200, new ClientDetails(null, "info2"))), - true)); - log.info(">>> get Manager:{}", managerSecond.getId()); - var managerSecondSelected = dbServiceManager - .getManager(managerSecond.getId()) - .orElseThrow(() -> new RuntimeException("Manager not found, id:" + managerSecond.getId())); - log.info(">>> managerSecondSelected:{}", managerSecondSelected); - - //// обновляем Manager с удалением его клиентов - dbServiceManager.saveManager(new Manager( - managerSecondSelected.getId(), "dbServiceSecondUpdated", new HashSet<>(), new ArrayList<>(), false)); - var managerUpdated = dbServiceManager - .getManager(managerSecondSelected.getId()) - .orElseThrow(() -> new RuntimeException("Manager not found, id:" + managerSecondSelected.getId())); - log.info(">>> managerUpdated:{}", managerUpdated); - - /// создаем Client - var firstClient = dbServiceClient.saveClient(new Client( - null, - "dbServiceFirst" + System.currentTimeMillis(), - managerSecond.getId(), - 1, - new ClientDetails(null, "init1"))); - - var clientSecond = dbServiceClient.saveClient(new Client( - null, - "dbServiceSecond" + System.currentTimeMillis(), - managerSecond.getId(), - 2, - new ClientDetails(null, "init2"))); - var clientSecondSelected = dbServiceClient - .getClient(clientSecond.id()) - .orElseThrow(() -> new RuntimeException("Client not found, id:" + clientSecond.id())); - log.info(">>> clientSecondSelected:{}", clientSecondSelected); - - log.info("delete instead of update"); - /// обновляем Client - dbServiceClient.saveClient(new Client( - clientSecondSelected.id(), - "dbServiceSecondUpdated", - managerSecond.getId(), - clientSecondSelected.orderColumn(), - new ClientDetails(null, "updated"))); - var clientUpdated = dbServiceClient - .getClient(clientSecondSelected.id()) - .orElseThrow(() -> new RuntimeException("Client not found, id:" + clientSecondSelected.id())); - log.info("clientUpdated:{}", clientUpdated); - - /// получаем все сущности - log.info(">>> All clients"); - dbServiceClient.findAll().forEach(client -> log.info("client:{}", client)); - - log.info(">>> All managers"); - dbServiceManager.findAll().forEach(manager -> log.info("manager:{}", manager)); - - /// применяем переопределенные методы репозитариев - var clientFoundByName = clientRepository - .findByName(firstClient.name()) - .orElseThrow(() -> new RuntimeException("client not found, name:" + firstClient.name())); - log.info("clientFoundByName:{}", clientFoundByName); - - var clientFoundByNameIgnoreCase = clientRepository - .findByNameIgnoreCase(firstClient.name().toLowerCase()) - .orElseThrow(() -> new RuntimeException("client not found, name:" + firstClient.name())); - log.info("clientFoundByNameIgnoreCase:{}", clientFoundByNameIgnoreCase); - - clientRepository.updateName(firstClient.id(), "newName"); - var updatedClient = - clientRepository.findById(firstClient.id()).orElseThrow(() -> new RuntimeException("client not found")); - - log.info(">>> updatedClient:{}", updatedClient); - - /// проверяем проблему N+1 - log.info(">>> checking N+1 problem"); - var managerId = managerSecond.getId(); - if (managerId == null) { - throw new IllegalArgumentException("managerId can't be null"); - } - var managerN = managerRepository - .findById(managerId) - .orElseThrow(() -> new RuntimeException("Manager not found, name:" + managerSecond.getId())); - log.info(">>> managerN:{}", managerN); - - /* - получаем основную сущность: - [SELECT "manager"."id" AS "id", "manager"."label" AS "label" FROM "manager" WHERE "manager"."id" = ?] - получаем дочерние: - [SELECT "client"."id" AS "id", "client"."name" AS "name", "client"."manager_id" AS "manager_id" FROM "client" WHERE "client"."manager_id" = ?] - */ - log.info(">>> select all"); - var allManagers = managerRepository.findAll(); - log.info(">>> allManagers.size():{}", allManagers.size()); - log.info(">>> allManagers:{}", allManagers); - - /// - log.info(">>> TableWithPkRepository"); - var pk = new TableWithPk.Pk("p1", String.valueOf(System.currentTimeMillis())); - TableWithPk tableWithPk = new TableWithPk(pk, "value", true); - log.info("tableWithPk:{}", tableWithPk); - - tableWithPkRepository.saveEntry(tableWithPk); - - TableWithPk loadedTableWithPk = tableWithPkRepository.findById(pk).orElseThrow(); - log.info("loadedTableWithPk:{}", loadedTableWithPk); - } -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/AppRunner.java b/L28-springDataJdbc/src/main/java/ru/otus/AppRunner.java new file mode 100644 index 0000000..57f0996 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/AppRunner.java @@ -0,0 +1,53 @@ +package ru.otus; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; +import ru.otus.model.Address; +import ru.otus.model.Client; +import ru.otus.model.Phone; +import ru.otus.model.User; +import ru.otus.repository.AddressRepository; +import ru.otus.repository.ClientRepository; +import ru.otus.repository.PhoneRepository; +import ru.otus.repository.UserRepository; + +@Component("actionDemo") +public class AppRunner implements CommandLineRunner { + private static final Logger log = LoggerFactory.getLogger(AppRunner.class); + + private final ClientRepository clientRepository; + private final AddressRepository addressRepository; + private final PhoneRepository phoneRepository; + private final UserRepository userRepository; + + public AppRunner( + UserRepository userRepository, + ClientRepository clientRepository, + AddressRepository addressRepository, + PhoneRepository phoneRepository) { + this.clientRepository = clientRepository; + this.addressRepository = addressRepository; + this.phoneRepository = phoneRepository; + this.userRepository = userRepository; + } + + @Override + public void run(String... args) { + + Client client = new Client("Иван"); + + Address address = new Address("ул. Ленина"); + client.setAddress(address); + + client.addPhone(new Phone("+7-900-111-22-33")); + client.addPhone(new Phone("+7-900-444-55-66")); + + clientRepository.save(client); + addressRepository.save(address); + + userRepository.save(new User("Пользователь", "user", "user")); + userRepository.save(new User("Administrator", "admin", "admin")); + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java b/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java index 25715a4..76f26c4 100644 --- a/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java +++ b/L28-springDataJdbc/src/main/java/ru/otus/DbServiceDemo.java @@ -3,6 +3,18 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/* + Полезные для демо ссылки + + // Стартовая страница + http://localhost:8080 + + // Страница пользователей + http://localhost:8080/users + + // REST сервис + http://localhost:8080/api/user/3 +*/ @SpringBootApplication public class DbServiceDemo { public static void main(String[] args) { diff --git a/L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsController.java b/L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsController.java new file mode 100644 index 0000000..c3e375b --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsController.java @@ -0,0 +1,27 @@ +package ru.otus.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import ru.otus.model.Client; +import ru.otus.repository.ClientRepository; + +@Controller +public class ClientsController { + + private final ClientRepository clientRepository; + + @Autowired + public ClientsController(ClientRepository clientRepository) { + this.clientRepository = clientRepository; + } + + @GetMapping("/clients") + public String clients(Model model) { + Iterable clients = clientRepository.findAll(); + model.addAttribute("clients", clients); + + return "clients"; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsRestController.java b/L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsRestController.java new file mode 100644 index 0000000..a6c1138 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/controller/ClientsRestController.java @@ -0,0 +1,124 @@ +package ru.otus.controller; + +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.otus.model.Address; +import ru.otus.model.Client; +import ru.otus.model.Phone; +import ru.otus.model.RequestAddPhone; +import ru.otus.model.RequestCreateClient; +import ru.otus.model.RequestEditClient; +import ru.otus.repository.AddressRepository; +import ru.otus.repository.ClientRepository; +import ru.otus.repository.PhoneRepository; + +@RestController +@RequestMapping("/api/clients") +public class ClientsRestController { + private final ClientRepository clientRepository; + private final AddressRepository addressRepository; + private final PhoneRepository phoneRepository; + + @Autowired + public ClientsRestController( + ClientRepository clientRepository, AddressRepository addressRepository, PhoneRepository phoneRepository) { + this.clientRepository = clientRepository; + this.addressRepository = addressRepository; + this.phoneRepository = phoneRepository; + } + + @PostMapping + public ResponseEntity createClient(@RequestBody RequestCreateClient request) { + if (request.getName() == null || request.getName().trim().isEmpty()) { + JsonResponse error = new JsonResponse("Invalid input", "Client name cannot be empty"); + return ResponseEntity.status(400).body(error); // 400 Bad Request + } + + Address address = new Address(request.getAddress()); + Client client = new Client(request.getName()); + client.setAddress(address); + client.addPhone(new Phone(request.getNumber())); + var savedClient = clientRepository.save(client); + addressRepository.save(address); + + return ResponseEntity.status(201).body(savedClient); + } + + @DeleteMapping("/{clientId}/phone/{phoneId}") + public ResponseEntity deletePhone(@PathVariable Long clientId, @PathVariable Long phoneId) { + Optional client = clientRepository.findById(clientId); + if (client.isEmpty()) { + JsonResponse error = new JsonResponse("Invalid input", "Client not found"); + return ResponseEntity.status(404).body(error); + } + + phoneRepository.deleteById(phoneId); + return ResponseEntity.status(200).body(new JsonResponse("Phone deleted", "Phone Deleted")); + } + + @PostMapping("/{clientId}/phone") + public ResponseEntity addPhone(@PathVariable Long clientId, @RequestBody RequestAddPhone request) { + Optional client = clientRepository.findById(clientId); + if (client.isEmpty()) { + JsonResponse error = new JsonResponse("Invalid input", "Client not found"); + return ResponseEntity.status(404).body(error); + } + + if (request.getNumber() == null || request.getNumber().trim().isEmpty()) { + return ResponseEntity.status(400).body(new JsonResponse("Invalid input", "Phone number cannot be empty")); + } + + Phone phone = new Phone(request.getNumber(), clientId); + + Phone savedPhone = phoneRepository.save(phone); + return ResponseEntity.status(201).body(savedPhone); + } + + @DeleteMapping("/{clientId}") + public ResponseEntity deleteClient(@PathVariable Long clientId) { + Optional client = clientRepository.findById(clientId); + if (client.isEmpty()) { + JsonResponse error = new JsonResponse("Invalid input", "Client not found"); + return ResponseEntity.status(404).body(error); + } + + clientRepository.deleteById(clientId); + return ResponseEntity.status(201).body(new JsonResponse("Client deleted", "Client Deleted")); + } + + @PutMapping("/{clientId}/{field}") + public ResponseEntity editClient( + @PathVariable Long clientId, @PathVariable String field, @RequestBody RequestEditClient request) { + Optional clientOpt = clientRepository.findById(clientId); + if (clientOpt.isEmpty()) { + JsonResponse error = new JsonResponse("Invalid input", "Client not found"); + return ResponseEntity.status(404).body(error); + } + + Client client = clientOpt.get(); + + if (field.equals("name")) { + client.setName(request.getValue()); + clientRepository.save(client); + return ResponseEntity.status(201).body(new JsonResponse("Client name updated", "Client name updated")); + } + + if (field.equals("address")) { + Optional
addressOpt = + addressRepository.findById(client.getAddress().getId()); + if (addressOpt.isEmpty()) { + return ResponseEntity.status(404) + .body(new JsonResponse( + "Address not found", + "Address with ID: " + client.getAddress().getId() + " not found")); + } + Address address = addressOpt.get(); + address.setAddress(request.getValue()); + Address updatedAddress = addressRepository.save(address); + return ResponseEntity.status(201).body(new JsonResponse("Address updated", "Address updated")); + } + return ResponseEntity.status(400).body(new JsonResponse("bad Request", "Bad request")); + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/controller/JsonResponse.java b/L28-springDataJdbc/src/main/java/ru/otus/controller/JsonResponse.java new file mode 100644 index 0000000..f2e0e47 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/controller/JsonResponse.java @@ -0,0 +1,19 @@ +package ru.otus.controller; + +public class JsonResponse { + private String message; + private String details; + + public JsonResponse(String message, String details) { + this.message = message; + this.details = details; + } + + public String getMessage() { + return message; + } + + public String getDetails() { + return details; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/controller/UsersController.java b/L28-springDataJdbc/src/main/java/ru/otus/controller/UsersController.java new file mode 100644 index 0000000..bc3b663 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/controller/UsersController.java @@ -0,0 +1,29 @@ +package ru.otus.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import ru.otus.model.User; +import ru.otus.repository.UserRepository; + +@Controller +public class UsersController { + + private final UserRepository userRepository; + + @Autowired + public UsersController(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @GetMapping("/users") + public String users(Model model) { + Iterable users = userRepository.findAll(); + model.addAttribute("users", users); + + model.addAttribute("message", "helloWorld"); + + return "users"; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/controller/UsersRestController.java b/L28-springDataJdbc/src/main/java/ru/otus/controller/UsersRestController.java new file mode 100644 index 0000000..1bc29c7 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/controller/UsersRestController.java @@ -0,0 +1,48 @@ +package ru.otus.controller; + +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.otus.model.RequestCreateUser; +import ru.otus.model.User; +import ru.otus.repository.UserRepository; + +@RestController +@RequestMapping("/api/users") +public class UsersRestController { + + private final UserRepository userRepository; + + @Autowired + public UsersRestController(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @GetMapping("/{id}") + public ResponseEntity getUserById(@PathVariable Long id) { + Optional user = userRepository.findById(id); + if (user.isEmpty()) { + JsonResponse error = new JsonResponse("User not found", "No user exists with ID: " + id); + return ResponseEntity.status(404).body(error); + } + + return ResponseEntity.ok(user.orElse(null)); + } + + @PostMapping + public ResponseEntity createUser(@RequestBody RequestCreateUser request) { + if (request.getName() == null || request.getName().trim().isEmpty()) { + JsonResponse error = new JsonResponse("Invalid input", "User name cannot be empty"); + return ResponseEntity.status(400).body(error); // 400 Bad Request + } + + User user = new User(); + user.setName(request.getName()); + user.setLogin(request.getLogin()); + user.setPassword(request.getPassword()); + User savedUser = userRepository.save(user); + + return ResponseEntity.status(201).body(savedUser); + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java deleted file mode 100644 index 23b5833..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Client.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.otus.crm.model; - -import jakarta.annotation.Nonnull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Column; -import org.springframework.data.relational.core.mapping.MappedCollection; -import org.springframework.data.relational.core.mapping.Table; - -@Table("client") -public record Client( - @Id @Column("id") Long id, - @Nonnull String name, - String managerId, - Integer orderColumn, - @MappedCollection(idColumn = "client_id") ClientDetails clientDetails) {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java deleted file mode 100644 index 2bd1551..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/ClientDetails.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.otus.crm.model; - -import jakarta.annotation.Nonnull; -import org.springframework.data.annotation.Id; -import org.springframework.data.relational.core.mapping.Table; - -@Table("client_details") -public record ClientDetails(@Id Long clientId, @Nonnull String info) {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java deleted file mode 100644 index 44bed3f..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/Manager.java +++ /dev/null @@ -1,65 +0,0 @@ -package ru.otus.crm.model; - -import jakarta.annotation.Nonnull; -import java.util.List; -import java.util.Set; -import lombok.Getter; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceCreator; -import org.springframework.data.annotation.Transient; -import org.springframework.data.domain.Persistable; -import org.springframework.data.relational.core.mapping.MappedCollection; -import org.springframework.data.relational.core.mapping.Table; - -@Table("manager") -@Getter -public class Manager implements Persistable { - - @Id - @Nonnull - private final String id; - - private final String label; - - @MappedCollection(idColumn = "manager_id") - private final Set clients; - - @MappedCollection(idColumn = "manager_id", keyColumn = "order_column") - private final List clientsOrdered; - - @Transient - private final boolean isNew; - - public Manager(String id, String label, Set clients, List clientsOrdered, boolean isNew) { - this.id = id; - this.label = label; - this.clients = clients; - this.clientsOrdered = clientsOrdered; - this.isNew = isNew; - } - - @PersistenceCreator - private Manager(String id, String label, Set clients, List clientsOrdered) { - this(id, label, clients, clientsOrdered, false); - } - - @Override - public boolean isNew() { - return isNew; - } - - @Override - public String getId() { - return id; - } - - @Override - public String toString() { - return "Manager{" + "id='" - + id + '\'' + ", label='" - + label + '\'' + ", clients=" - + clients + ", clientsOrdered=" - + clientsOrdered + ", isNew=" - + isNew + '}'; - } -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java deleted file mode 100644 index 86e548f..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/model/TableWithPk.java +++ /dev/null @@ -1,59 +0,0 @@ -package ru.otus.crm.model; - -import static org.springframework.data.relational.core.mapping.Embedded.OnEmpty.USE_EMPTY; - -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceCreator; -import org.springframework.data.annotation.Transient; -import org.springframework.data.domain.Persistable; -import org.springframework.data.relational.core.mapping.Embedded; -import org.springframework.data.relational.core.mapping.Table; - -@Table("table_with_pk") -public class TableWithPk implements Persistable { - - @Id - @Embedded(onEmpty = USE_EMPTY) - private final Pk pk; - - private final String value; - - @Transient - private final boolean isNew; - - @PersistenceCreator - public TableWithPk(Pk pk, String value) { - this(pk, value, false); - } - - public TableWithPk(Pk pk, String value, boolean isNew) { - this.pk = pk; - this.value = value; - this.isNew = isNew; - } - - @Override - public Pk getId() { - return pk; - } - - @Override - public boolean isNew() { - return isNew; - } - - public Pk getPk() { - return getId(); - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return "TableWithPk{" + "pk=" + pk + ", value='" + value + '\'' + ", isNew=" + isNew + '}'; - } - - public record Pk(String idPart1, String idPart2) {} -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java deleted file mode 100644 index 615a2c4..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ClientRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.otus.crm.repository; - -import java.util.Optional; -import org.springframework.data.jdbc.repository.query.Modifying; -import org.springframework.data.jdbc.repository.query.Query; -import org.springframework.data.repository.ListCrudRepository; -import org.springframework.data.repository.query.Param; -import ru.otus.crm.model.Client; - -public interface ClientRepository extends ListCrudRepository { - - Optional findByName(String name); - - @Query("select * from client where upper(name) = upper(:name)") - Optional findByNameIgnoreCase(@Param("name") String name); - - @Modifying - @Query("update client set name = :newName where id = :id") - void updateName(@Param("id") long id, @Param("newName") String newName); -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java deleted file mode 100644 index edfd786..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.otus.crm.repository; - -import java.util.List; -import org.springframework.data.repository.ListCrudRepository; -import ru.otus.crm.model.Manager; - -public interface ManagerRepository extends ListCrudRepository { - - // закоментируйте, чтобы получить N+1 - /* - @Override - @Query(value = """ - select m.id as manager_id, - m.label as manager_label, - c.id as client_id, - c.name as client_name, - c.order_column as order_column, - cd.info as client_info - from manager m - left outer join client c - on m.id = c.manager_id - left outer join client_details cd - on cd.client_id = c.id - order by m.id - """, - resultSetExtractorClass = ManagerResultSetExtractorClass.class) - */ - List findAll(); -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java deleted file mode 100644 index 73ae69e..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/ManagerResultSetExtractorClass.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.otus.crm.repository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import org.springframework.dao.DataAccessException; -import org.springframework.jdbc.core.ResultSetExtractor; -import ru.otus.crm.model.Client; -import ru.otus.crm.model.ClientDetails; -import ru.otus.crm.model.Manager; - -public class ManagerResultSetExtractorClass implements ResultSetExtractor> { - - @Override - public List extractData(ResultSet rs) throws SQLException, DataAccessException { - var managerList = new ArrayList(); - String prevManagerId = null; - while (rs.next()) { - var managerId = rs.getString("manager_id"); - Manager manager = null; - if (prevManagerId == null || !prevManagerId.equals(managerId)) { - manager = new Manager( - managerId, rs.getString("manager_label"), new HashSet<>(), new ArrayList<>(), false); - managerList.add(manager); - prevManagerId = managerId; - } - Long clientId = (Long) rs.getObject("client_id"); - if (manager != null && clientId != null) { - manager.getClients() - .add(new Client( - clientId, - rs.getString("client_name"), - managerId, - rs.getInt("order_column"), - new ClientDetails(clientId, rs.getString("client_info")))); - } - } - return managerList; - } -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java deleted file mode 100644 index 711ace3..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/repository/TableWithPkRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.otus.crm.repository; - -import java.util.Optional; -import javax.annotation.Nonnull; -import org.springframework.data.jdbc.repository.query.Modifying; -import org.springframework.data.jdbc.repository.query.Query; -import org.springframework.data.repository.ListCrudRepository; -import org.springframework.data.repository.query.Param; -import ru.otus.crm.model.TableWithPk; - -public interface TableWithPkRepository extends ListCrudRepository { - - @Modifying - @Query( - "insert into table_with_pk(id_part1, id_part2, value) VALUES (:#{#tableWithPk.pk.idPart1}, :#{#tableWithPk.pk.idPart2}, :#{#tableWithPk.value})") - void saveEntry(@Nonnull @Param("tableWithPk") TableWithPk tableWithPk); - - @Override - @Query("select * from table_with_pk where id_part1 = :#{#pk.idPart1} and id_part2 = :#{#pk.idPart2}") - Optional findById(@Param("pk") TableWithPk.Pk pk); -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java deleted file mode 100644 index ed7155c..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceClient.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.otus.crm.service; - -import java.util.List; -import java.util.Optional; -import ru.otus.crm.model.Client; - -public interface DBServiceClient { - - Client saveClient(Client client); - - Optional getClient(long id); - - List findAll(); -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java deleted file mode 100644 index bc31abe..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DBServiceManager.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.otus.crm.service; - -import java.util.List; -import java.util.Optional; -import ru.otus.crm.model.Manager; - -public interface DBServiceManager { - - Manager saveManager(Manager client); - - Optional getManager(String no); - - List findAll(); -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java deleted file mode 100644 index 1f6c48f..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceClientImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package ru.otus.crm.service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -import ru.otus.crm.model.Client; -import ru.otus.crm.repository.ClientRepository; -import ru.otus.sessionmanager.TransactionManager; - -@Service -public class DbServiceClientImpl implements DBServiceClient { - private static final Logger log = LoggerFactory.getLogger(DbServiceClientImpl.class); - - private final TransactionManager transactionManager; - private final ClientRepository clientRepository; - - public DbServiceClientImpl(TransactionManager transactionManager, ClientRepository clientRepository) { - this.transactionManager = transactionManager; - this.clientRepository = clientRepository; - } - - @Override - public Client saveClient(Client client) { - return transactionManager.doInTransaction(() -> { - var savedClient = clientRepository.save(client); - log.info("saved client: {}", savedClient); - return savedClient; - }); - } - - @Override - public Optional getClient(long id) { - var clientOptional = clientRepository.findById(id); - log.info("client: {}", clientOptional); - return clientOptional; - } - - @Override - public List findAll() { - var clientList = new ArrayList(); - clientRepository.findAll().forEach(clientList::add); - log.info("clientList:{}", clientList); - return clientList; - } -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java b/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java deleted file mode 100644 index ab554d5..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/crm/service/DbServiceManagerImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.otus.crm.service; - -import java.util.List; -import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -import ru.otus.crm.model.Manager; -import ru.otus.crm.repository.ManagerRepository; -import ru.otus.sessionmanager.TransactionManager; - -@Service -public class DbServiceManagerImpl implements DBServiceManager { - private static final Logger log = LoggerFactory.getLogger(DbServiceManagerImpl.class); - - private final ManagerRepository managerRepository; - private final TransactionManager transactionManager; - - public DbServiceManagerImpl(ManagerRepository managerRepository, TransactionManager transactionManager) { - this.managerRepository = managerRepository; - this.transactionManager = transactionManager; - } - - @Override - public Manager saveManager(Manager manager) { - return transactionManager.doInTransaction(() -> { - var savedManager = managerRepository.save(manager); - - log.info("savedManager manager: {}", savedManager); - return savedManager; - }); - } - - @Override - public Optional getManager(String no) { - var managerOptional = managerRepository.findById(no); - log.info("manager: {}", managerOptional); - return managerOptional; - } - - @Override - public List findAll() { - var managerList = managerRepository.findAll(); - log.info("managerList:{}", managerList); - return managerList; - } -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/Address.java b/L28-springDataJdbc/src/main/java/ru/otus/model/Address.java new file mode 100644 index 0000000..431a2ee --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/Address.java @@ -0,0 +1,25 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +@Getter +@Setter +@NoArgsConstructor +@Table("addresses") +public class Address { + @Id + @Column("id") + private Long id; + + @Column("address") + private String address; + + public Address(String address) { + this.address = address; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/Client.java b/L28-springDataJdbc/src/main/java/ru/otus/model/Client.java new file mode 100644 index 0000000..b6510b1 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/Client.java @@ -0,0 +1,38 @@ +package ru.otus.model; + +import java.util.HashSet; +import java.util.Set; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.MappedCollection; +import org.springframework.data.relational.core.mapping.Table; + +@Getter +@Setter +@NoArgsConstructor +@Table("clients") +public class Client { + @Id + @Column("id") + private Long id; + + @Column("name") + private String name; + + @Column("client_id") + private Address address; + + @MappedCollection(idColumn = "client_id") + private Set phones = new HashSet<>(); + + public Client(String name) { + this.name = name; + } + + public void addPhone(Phone phone) { + this.phones.add(phone); + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/Phone.java b/L28-springDataJdbc/src/main/java/ru/otus/model/Phone.java new file mode 100644 index 0000000..2c8498c --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/Phone.java @@ -0,0 +1,33 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +@Getter +@Setter +@NoArgsConstructor +@Table("phones") +public class Phone { + @Id + @Column("id") + private Long id; + + @Column("number") + private String number; + + @Column("client_id") + private Long clientId; // Внешний ключ на Client + + public Phone(String number) { + this.number = number; + } + + public Phone(String number, Long clientId) { + this.number = number; + this.clientId = clientId; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/RequestAddPhone.java b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestAddPhone.java new file mode 100644 index 0000000..4eeb7c3 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestAddPhone.java @@ -0,0 +1,12 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class RequestAddPhone { + private String number; +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateClient.java b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateClient.java new file mode 100644 index 0000000..f9cf7a3 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateClient.java @@ -0,0 +1,14 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class RequestCreateClient { + private String name; + private String address; + private String number; +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateUser.java b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateUser.java new file mode 100644 index 0000000..fe9bbfc --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestCreateUser.java @@ -0,0 +1,14 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class RequestCreateUser { + private String name; + private String login; + private String password; +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/RequestEditClient.java b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestEditClient.java new file mode 100644 index 0000000..d7c5eda --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/RequestEditClient.java @@ -0,0 +1,12 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class RequestEditClient { + private String value; +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/model/User.java b/L28-springDataJdbc/src/main/java/ru/otus/model/User.java new file mode 100644 index 0000000..1c5f6d5 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/model/User.java @@ -0,0 +1,35 @@ +package ru.otus.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +@Getter +@Setter +@NoArgsConstructor +@Table("users") +public class User { + + @Id + @Column("id") + private Long id; + + @Column("name") + private String name; + + @Column("login") + private String login; + + @Column("password") + private String password; + + public User(String name, String login, String password) { + // this.id = null; + this.name = name; + this.login = login; + this.password = password; + } +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/repository/AddressRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/repository/AddressRepository.java new file mode 100644 index 0000000..39b2460 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/repository/AddressRepository.java @@ -0,0 +1,6 @@ +package ru.otus.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.model.Address; + +public interface AddressRepository extends CrudRepository {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/repository/ClientRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/repository/ClientRepository.java new file mode 100644 index 0000000..d9f2b41 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/repository/ClientRepository.java @@ -0,0 +1,6 @@ +package ru.otus.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.model.Client; + +public interface ClientRepository extends CrudRepository {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/repository/PhoneRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/repository/PhoneRepository.java new file mode 100644 index 0000000..a4e6651 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/repository/PhoneRepository.java @@ -0,0 +1,9 @@ +package ru.otus.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.model.Phone; + +public interface PhoneRepository extends CrudRepository { + + void deleteById(Long Id); +} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/repository/UserRepository.java b/L28-springDataJdbc/src/main/java/ru/otus/repository/UserRepository.java new file mode 100644 index 0000000..775ee01 --- /dev/null +++ b/L28-springDataJdbc/src/main/java/ru/otus/repository/UserRepository.java @@ -0,0 +1,6 @@ +package ru.otus.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.otus.model.User; + +public interface UserRepository extends CrudRepository {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java deleted file mode 100644 index 33b647b..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionAction.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.otus.sessionmanager; - -import java.util.function.Supplier; - -public interface TransactionAction extends Supplier {} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java deleted file mode 100644 index 06ba3da..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManager.java +++ /dev/null @@ -1,6 +0,0 @@ -package ru.otus.sessionmanager; - -public interface TransactionManager { - - T doInTransaction(TransactionAction action); -} diff --git a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java b/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java deleted file mode 100644 index b916588..0000000 --- a/L28-springDataJdbc/src/main/java/ru/otus/sessionmanager/TransactionManagerSpring.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.otus.sessionmanager; - -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -@Component -public class TransactionManagerSpring implements TransactionManager { - - @Override - @Transactional - public T doInTransaction(TransactionAction action) { - return action.get(); - } -} diff --git a/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql b/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql index 619fb78..8beeb9c 100644 --- a/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql +++ b/L28-springDataJdbc/src/main/resources/db/migration/V1__initial_schema.sql @@ -1,28 +1,29 @@ -create table Manager +create table users ( - id varchar(50) not null primary key, - label varchar(50) + id bigint generated by default as identity primary key, + name varchar(50), + login varchar(50), + password varchar(50) ); -create table client +create table clients ( - id bigserial not null primary key, - order_column int not null, - name varchar(50) not null, - manager_id varchar(50) not null references Manager (id) + id bigint generated by default as identity primary key, + name varchar(255) NOT NULL ); -create index idx_client_manager_id on client (manager_id); -create table client_details +create table addresses ( - client_id bigint not null references client (id), - info varchar(50) not null + id bigint generated by default as identity primary key, + address varchar(255) NOT NULL, + client_id bigint, + foreign key (client_id) references clients (id) on delete cascade ); -create table table_with_pk +create table phones ( - id_part1 varchar(10), - id_part2 varchar(100), - value varchar(100) -); -alter table table_with_pk add primary key (id_part1, id_part2); + id bigint generated by default as identity primary key, + number varchar(255) NOT NULL, + client_id bigint, + foreign key (client_id) references clients (id) on delete cascade +); \ No newline at end of file diff --git a/L28-springDataJdbc/src/main/resources/logback.xml b/L28-springDataJdbc/src/main/resources/logback.xml new file mode 100644 index 0000000..5ff97c1 --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/logback.xml @@ -0,0 +1,18 @@ + + + + + + %d{yyyy-MM-dd_HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/L28-springDataJdbc/src/main/resources/static/img/login.png b/L28-springDataJdbc/src/main/resources/static/img/login.png new file mode 100644 index 0000000000000000000000000000000000000000..c9381999aa1d577870b9ced04aa0ca9090fd94cc GIT binary patch literal 16475 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 W*$_p8k^1kPzqGi5Sha{@;Qs-uMM(7k literal 0 HcmV?d00001 diff --git a/L28-springDataJdbc/src/main/resources/static/img/logo.svg b/L28-springDataJdbc/src/main/resources/static/img/logo.svg new file mode 100644 index 0000000..0002d77 --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/static/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/L28-springDataJdbc/src/main/resources/static/index.html b/L28-springDataJdbc/src/main/resources/static/index.html new file mode 100644 index 0000000..434e006 --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/static/index.html @@ -0,0 +1,14 @@ + + + Index + + +
+ +
+

Добро пожаловать на стартовую страницу примера

+ Пользователи
+ Клиенты +
+ + \ No newline at end of file diff --git a/L28-springDataJdbc/src/main/resources/templates/clients.html b/L28-springDataJdbc/src/main/resources/templates/clients.html new file mode 100644 index 0000000..58d9d3b --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/templates/clients.html @@ -0,0 +1,174 @@ + + + Клиенты + + + + + +

+
+

Создать клиента

+ + + + +

+
+
+

Список клиентов

+ + + + + + + + + + + + + + + + + +
IdИмяАдресТелефоны
+ + + + + + + + + + +

+ + +

+ +
+ + \ No newline at end of file diff --git a/L28-springDataJdbc/src/main/resources/templates/users.html b/L28-springDataJdbc/src/main/resources/templates/users.html new file mode 100644 index 0000000..8dea814 --- /dev/null +++ b/L28-springDataJdbc/src/main/resources/templates/users.html @@ -0,0 +1,91 @@ + + + Пользователи + + + + + + + + +

Получить пользователя по id

+ + +

+
+

Создать пользователя

+ + + + +

+
+
+

Все пользователи

+ + + + + + + + + + + + + + +
IdИмяЛогинПароль
+ + + +
+ + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 61b78f5..b28aaad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,7 @@ allprojects { val jetty: String by project val freemarker: String by project + val thymeleaf: String by project val reflections: String by project @@ -79,6 +80,7 @@ allprojects { dependency("org.eclipse.jetty:jetty-io:$jetty") dependency("org.eclipse.jetty:jetty-util:$jetty") dependency("org.freemarker:freemarker:$freemarker") + dependency("org.thymeleaf:thymeleaf:$thymeleaf") dependency("org.reflections:reflections:$reflections") diff --git a/gradle.properties b/gradle.properties index fcf5992..3502b64 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,6 +23,7 @@ redisson=3.31.0 jetty=12.0.16 freemarker=2.3.34 +thymeleaf=3.1.3.RELEASE reflections=0.10.2