Giter Club home page Giter Club logo

documents-service's Introduction

documents-service

Preview version of module diagram: Modules

Preview version of database schema: Database schema

documents-service's People

Contributors

shandakova avatar

Watchers

 avatar

documents-service's Issues

у сервисов нужно сделать интерфейсы

Сервисы (бизнес логики) могут обернуться в Proxy объекты и тогда будут проблемы с приведением типов, поэтому лучше сделать интерфейсы для них как и для dao слоя

GroupId в дочерних pom

<groupId>com.shandakova</groupId>

Не обязательно прописывать (он берется из parent помки)

@ManyToOne и другие связи

@manytoone по умолчанию fetch type стоит EAGER, это означает что он вытащит из бд эту связанную сущность.

EAGER иногда нужно использовать а иногда нет, лучше всегда проставлять fetch type. Если у тебя связь на объект того же класса то EAGER может сыграть с тобой злую шутку.

Вот например у тебя:

@Getter
@Setter
@Entity
@Table(name = "documents")
public class Document extends Node {
    @Column(name = "description")
    private String description;
    @ManyToOne(cascade = {CascadeType.MERGE})
    @JoinColumn(name = "type_id")
    private DocumentType type;
    @Column(name = "importance", nullable = false)
    @Enumerated(EnumType.STRING)
    private Importance importance;
    @Column(name = "version_number", nullable = false)
    private Integer versionNumber;
    @Column(name = "verified")
    private boolean verified;
    @ManyToOne(cascade = {CascadeType.MERGE})
    @JoinColumn(name = "previous_version_id")
    private Document previousVersion;
}

Вот тут EAGER

 @ManyToOne(cascade = {CascadeType.MERGE})
    @JoinColumn(name = "previous_version_id")
    private Document previousVersion;

значит он вытащит предыдущую версию. А предыдущая версия вытащит предыдущую к ней и т.д. по цепочке. Т.е. сколько в цепочке предыдущих версий столько и будет запросов - это не очень хорошо. Лучше поставить LAZY (ленивую загрузку, т.е. если ты обратишься к этому полю через get то он вытащит если нет то не будет).

@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.MERGE})
    @JoinColumn(name = "previous_version_id")
    private Document previousVersion;

А вот тут

@ManyToOne(cascade = {CascadeType.MERGE})
   @JoinColumn(name = "type_id")
   private DocumentType type;

В целом можно оставить EAGER тип тебе в большинстве случаев понадобиться и в самом типе никаких связей с EAGER вроде как и нет. Поэтому тут EAGER допустим.
Но тип fetch лучше прописать, поскольку @manytoone по умолчанию EAGER а в ManyToMany по умолчанию LAZY в других тоже могут быть другие + от версии к версии Hibernate на моей памяти менялись дефолтные значения.

Вот с parent у тебя аналогичная ситуация:

@ManyToOne(cascade = {CascadeType.ALL})
  @JoinColumn(name = "parent_id")
  private Node parent;

Лучше переделать на вот так:

@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinColumn(name = "parent_id")
    private Node parent;

А если ты хочешь вытащить вместе с parent (но одним parent), то лучше явно указать либо через граф либо через Criteria что тебе нужен ответ вместе с parent. Пример есть тут: https://www.baeldung.com/spring-data-jpa-named-entity-graphs

Создать еще 2 модуля

  1. Лучше создать еще модуль с бищнес логикой (сейчас у тебя этот слой в модуле с сервлетами)
  2. И еще отдельно создать модуль с DTO (они у тебя сейчас тоже в модуле с сервлетами)

Autowired и конструктор

  @Autowired
    private DirectoriesDAO directoriesDAO;

    public DirectoriesService(DirectoriesDAO directoriesDAO) {
        this.directoriesDAO = directoriesDAO;
    }

У тебя на поле прописан Autowired + сделан конструктор.

Должно быть либо Autowired либо просто конструктор.
По хорошему должен быть конструктор. Бины инжектить лучше через конструктор почему:

  1. Защитишь себя от циклических зависимостей
  2. Защитишь себя от того что объект это класса нельзя создать без этих полей (в данном случае directoriesDAO).

Transactional

не указано в какой момент открывается транзакция и в какой момент закрывается

@Transactional
public void create(DirectoryDTO directoryDTO) throws SQLException {

Нужен стартер для запуска приложения

Тесты есть это хорошо, но не хватает некоторого стартера приложения, чтобы можно было его полноценно запустить.
В этом стартере должна быть инициализация сервера приложений с поднятием Spring Context.

Т.е. нужен некоторый App класс, в котором есть main метод и метод по инициализации всех настроек.

Пакет implementation

Тут дело вкуса. Но обычно его называют impl
com.shandakova.documents.dao - тут лежат интерфейсы
com.shandakova.documents.dao.impl - тут лежит реализация

JPA parent

 @Column(name = "parent_id")
    private Integer parentId;

почему не private Node parent; ?

lombok

Какой scope у зависимости должен быть?

entity scan

@EntityScan(basePackages = {"com.shandakova.documents.entities", "com.shandakova.documents.entities.type"})
com.shandakova.documents.entities.type - такого пакета нет.

connection pool

обычно при реализации своего пула соединений переопределяют метод close у соединения. Который возвращает соединение в освобожденные (для переиспользования).
Тогда если ты подключишь другой пул тебе не придется переписывать большую часть кода и логику по тому как закрыть соединение или вернуть его в доступные.

SpringBoot

Нужно подключить SpringBoot и стартер у тебя поменяется на более простой вид.
SpringSecurity не подключай пока, его лучше в самом конце подключать.

constraint column

  @Column
    private String login;

nullable не проставлен, так же в остальных полях проставить чтобы обезопасить себя.
unique так же

у строковых значениях хорошо бы проставить длину

artifactId

rest
лучше артифакты именовать более уникально: documents-service-rest например.
Поскольку jar соберется с названием -.jar
Если я подключу библиотеку у которой artifactId с названием rest и такой же версией. То у тебя в либах будет лежать 2 одинаковых файла (maven скорее всего разраулит это дела добавив суффикс но лучше от греха подальше переименовать).

Соединение по http не закрывается

HttpURLConnection нужно закрывать тогда когда закончила использовать его иначе Socket останется открытым (закроется когда jvm завершит работу), это приведет к утечке ресурсов.
При закрытии соединения закроются потоки (на считывание и запись), но лучше их тоже закрыть BufferedReader.

Spring MVC

Нужно переделать сервлеты на Spring MVC (Rest)

Node repository

Если доделаешь наследование в БД (был issue). То ты сможешь сделать 1 репо вот такого плана:

public interface NodeRepository extends JpaRepository<Node, Integer>, JpaSpecificationExecutor<Node> {

}

И тогда вот такого не придется писать:

List<Directory> directories = directoriesDAO.findAllDirectoriesByParentId(parentId, isDescOrder);
       List<Document> documents = documentsDAO.findAllDocumentsByParentId(parentId, isDescOrder);

Достаточно будет написать 1 метод:
List<Node> nodes= nodeRepository.findAllByParent(parent);
(можно Spec сделать если нужно добавить доп фильтры или paging).

С paging будет примерно так:

Pageable pageable = PageRequest.of(0,10); //1-й номер страницы, 2-й кол-во элементов на странице, есть 3-й это соририовка
        Page<Node> nodes=nodeRepository.findAll(pageable);

Либо сделать Spec в котором можно указать доп фильтры:

public class NodesSpec {
    public static Specification<Node> search(NodeFilter filter) {
        return (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();           
            if(filter.getParentId()!=null){
                Join<Object, Object> parentJoin = root.join("parent");
                predicates.add(cb.equal(parentJoin.get("id"),filter.getParentId()));
            }
           
            return query.where(cb.and(predicates.toArray(new Predicate[0])))
                    .distinct(true).getRestriction();
        };
    }
}
Pageable pageable = PageRequest.of(filter.getPageNum(),filter.getPageSize()); 
Page<Node> nodes=nodeRepository.findAll(NodesSpec.search(filter), pageable);

filter - это некоторый DTO для филтрации по дереву (можно его унаследовать от DTO абстрактного который отвечает за Paging).

в nodes у тебя будут и каталоги документы найденные по этому parent.
А дальше как понять каталог или документ:

nodes.foreach(n->{
     if(n instanceof Directory){
         
     }else if(n instanceof  Document){
         
     }
});

Наследование в БД

Не определен дискриминатор в БД. Дискриминатор это колонка которая отвечает за то, как преобразовать объект (в каталоги или документ) и обратно.

Т.е. например получаешь по идентификатору узел в дереве (тебе не важно какой это тип), ты передаешь идентификатор а на выход ты получаешь объект класса который унаследован от Node (либо документ либо каталог).

@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(
        discriminatorType = DiscriminatorType.STRING,
        name = "base_type"
)

А у наследников опредеяешь значение это дискриминатора:

@DiscriminatorValue(NodeType.Values.DOCUMENT)
public class Document extends Node 

NodeType.Values.DOCUMENT - это строка (константа).
Пример:

public enum NodeType {
    DIRECTORY,DOCUMENT;

    public static class Values {
        public static final String DIRECTORY = "DIRECTORY";
        public static final String DOCUMENT= "DOCUMENT";
    }
} 

Пакеты dto и service

package dto;
package services;

Отошла от правил наименования пакетов. com.shandakova.documents. ....

dbschema

Папку надо перенести в resources

Swagger подключить к Rest

Если к Rest подключщь Swagger проще будет проверять. У него есть страничка со списком доступных рестов у тебя в приложении и прямо с этой странички можно дергать ресты.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.