documents-service's Introduction
documents-service's People
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 модуля
- Лучше создать еще модуль с бищнес логикой (сейчас у тебя этот слой в модуле с сервлетами)
- И еще отдельно создать модуль с DTO (они у тебя сейчас тоже в модуле с сервлетами)
Autowired и конструктор
@Autowired
private DirectoriesDAO directoriesDAO;
public DirectoriesService(DirectoriesDAO directoriesDAO) {
this.directoriesDAO = directoriesDAO;
}
У тебя на поле прописан Autowired + сделан конструктор.
Должно быть либо Autowired либо просто конструктор.
По хорошему должен быть конструктор. Бины инжектить лучше через конструктор почему:
- Защитишь себя от циклических зависимостей
- Защитишь себя от того что объект это класса нельзя создать без этих полей (в данном случае 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. ....
Связи на объекты (другие классы)
@Column(name = "type_id")
private Integer typeId;
почему не private DocumentType type; ?
dbschema
Папку надо перенести в resources
Swagger подключить к Rest
Если к Rest подключщь Swagger проще будет проверять. У него есть страничка со списком доступных рестов у тебя в приложении и прямо с этой странички можно дергать ресты.
URL (хост + порт) вынести в константы
Подключение к серверу хост и порт лучше вынести в константы или настройки
Дублируется код для получения конфигурации в сервисах
directoriesDAO = new DirectoriesDAOImpl(ConnectionPool.getInstanceByProperties(properties));
при подключении Spring у тебя это уйдет
значения enum в uppercase
public enum Access {
none,
read,
write,
verify
}
NONE, READ и т.д.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.