NTA

«SQL-блокнот» — web-приложение на Java + Spring Boot / MVC

В начале материала расскажу о возможностях программы, а в конце будет ссылка на репозиторий. Программа запускается везде, где есть JVM (Java Virtual Machine), имеет простой и понятный интерфейс.

Итак, какую программу будем собирать? Ту, которая умеет:

  • Писать, хранить, редактировать SQL запросы;
  • Создавать подключения к различным СУБД;
  • Исполнять SQL запросы;
  • Выгружать результаты отработки запросов в Excel, обходя его ограничение в 1 млн строк (выборка автоматически разбивается по excel листам);
  • Разграничивать роли пользователей (пользователь/администратор);
  • Также программа имеет собственный механизм регистрации/авторизации, логирования действий пользователей;
  • Для примера я также приложил сервер СУБД Postgres, развернутый через Docker, чтобы описанная программа запускалась «из коробки».

Итак, стартовое окно авторизации/регистрации нового пользователя выглядит так:

Начальное окно с пунктами меню выглядит так:

С правами “ADMIN” можно увидеть список пользователей, редактировать их или удалить

Далее подробнее остановлюсь на каждом разделе меню и расскажу о его возможностях.

Пункт меню «Запрос». Здесь можно найти по наименованию «запрос» в списке запросов.

«Список запросов». Запросы, как я сказал ранее, можно редактировать. Возможно редактировать сам скрипт запроса, выгрузить его в Excel, найти в «Журнале» готовые выгрузки запроса. О «Журнале» расскажу далее.

Если в тексте SQL запроса присутствует макрос подстановки (@TMFL1), то в это место подставляется построчный текст, загруженный из txt-файла. Программа попросит загрузить файл:

«Журнал запросов». В Журнале хранятся исполненные запросы, и журнал также позволяет экспортировать готовый отчет Excel пользователю.

Возможен поиск готовых отчетов в журнале:

«Выгрузка пользователю». Когда отчет (файл Excel) сформирован в журнале появляется кнопка «Выгрузить».

Теперь перейдем к описанию некоторых частей кода программы.

Для работы c базами данных, системой доступа, REST API в pom.xml я использовал следующие зависимости.

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency> Работу приложения с Excel реализовывал так: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.0.0</version> </dependency>

Для работы с СУБД Oracle. Зависимости в pom.xml для СУБД Oracle.

<dependency> <groupId>com.oracle.ojdbc</groupId> <artifactId>ojdbc8</artifactId> <version>19.3.0.0</version> </dependency> <dependency> <groupId>com.oracle.ojdbc</groupId> <artifactId>orai18n</artifactId> <version>19.3.0.0</version> </dependency>

Файл с настройками application.properties Для работы с СУБД Oracle

#===========oracle spring.datasource.driverClassName=oracle.jdbc.OracleDriver spring.datasource.url=jdbc:oracle:thin:@curr_server.ru:1526/curr_bd spring.datasource.shema=CURR_SCHEMA spring.datasource.username=User_name spring.datasource.password=************ #=====jpa spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle8iDialect #==Схема по умолчанию spring.jpa.properties.hibernate.default_schema = CURR_SCHEMA #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect spring.jpa.hibernate.ddl-auto=none

Легко перейти на любую другую БД изменив настройки и добавив соответствующе драйвера (зависимости в pom).

В БД создаем необходимые таблицы.

Таблица для списка отчетов:

-- Create table create table REPORTLIST ( id NUMBER default "CURR_SCHEMA"."SEQ_REP".nextval not null, namequery VARCHAR2(1000), description VARCHAR2(2000), repenabled NUMBER )

Таблица для хранения SQL-запросов

-- Create table create table REPORTSCRIPT ( id NUMBER default "CURR_SCHEMA"."SEQ_SKRP".nextval not null, script VARCHAR2(4000), idreport NUMBER )

Таблица для хранения журнала запросов.

-- Create table create table LOGREPORT ( id NUMBER default "CURR_SCHEMA"."SEQ_LOGREP".nextval not null, datein DATE, procname VARCHAR2(1000), script VARCHAR2(4000), nrowcount NUMBER, logtext VARCHAR2(1000), vusername VARCHAR2(100), vfileout VARCHAR2(200), nflagout NUMBER )

Ниже приведу пример использования классов в приложении:

Для работы со списком отчетов – класс ReportsEntity:

@Entity @Table(name = "REPORTLIST", schema = "CURR_SCHEMA") public class ReportsEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "report_sequence") @SequenceGenerator(name = "report_sequence", sequenceName = "SEQ_REP", schema = " CURR_SCHEMA", initialValue = 1, allocationSize = 1) @Column(name="ID") private Long id; @Column(name="NAMEQUERY") private String nameQuery; @Column(name = "DESCRIPTION") private String description; @Column(name = "REPENABLED") private int enableReport;

Для работы с запросами – класс QueryEntity:

@Entity @Table(name = "REPORTSCRIPT", schema = "CURR_SCHEMA") public class QueryEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "script_sequence") @SequenceGenerator(name = "script_sequence", sequenceName = "SEQ_SKRP", schema = " CURR_SCHEMA ", initialValue = 1, allocationSize = 1) @Column(name="ID") private Long id; @Column(name="SCRIPT") private String query; //@OneToOne(mappedBy = "queryEntity", cascade = CascadeType.ALL) @Column(name = "IDREPORT") private Long idReport;

Сервис для выгрузки отчетов в Excel. Для выгрузки данных в Excel написан отдельный класс с учетом формирования файлов с большим объемом данных. Для ограничения расхода памяти, при очень больших объёмах данных использую разбивку выгрузки по 2000+- записей (строка ((SXSSFSheet) sheet).flushRows(2000);).

@Service public class ExportServiceImpl implements ExportService { private static final Logger log = LoggerFactory.getLogger(ExportServiceImpl.class); private final ResultSetToExcelLoc resultSetToExcelLoc; private long countRow; private String repositoryPath; @Autowired public ExportServiceImpl(ResultSetToExcelLoc resultSetToExcelLoc) { this.resultSetToExcelLoc = resultSetToExcelLoc; this.repositoryPath = this.resultSetToExcelLoc.getRepositoryPath(); } @Override public void exportExcelLoc(ResultSet rs, String filename) { log.info("=Start execution of scheduled task"); try { log.info("====start export excel===="); this.repositoryPath = this.resultSetToExcelLoc.getRepositoryPath(); log.info("==repositoryPath==" + this.repositoryPath); resultSetToExcelLoc.setResultSet(rs); // resultSetToExcelLoc.writeIntoExcelSXSSFMulti(filename); //count rows countRow = resultSetToExcelLoc.getiRow(); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); log.error(e.getMessage(), e); } log.info("=Complete execution of scheduled task"); } @Override public long getCountRow() { return countRow; } @Override public String getRepositoryPath() { return repositoryPath; } }

Логирование. Все логи собираются в log-файл (ежедневно в новый файл, название файла с датой). Файлы с логами хранятся на сервере в папке Logs.

<appender name="File" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>.\Logs\applic.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>.\Logs\applic_%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>15</maxHistory> <totalSizeCap>10GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%d{dd.MM.yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{20} - %msg%n</pattern> </encoder> </appender>

Ввиду того что код программы объемен – я разместил его в bitbucket – ссылка.

В репозитории проект настроен на работу с БД PostgreSql, которая разворачивается через Docker

Буду рад, если моя программа будет полезна при решении ваших задач.

0
6 комментариев
Написать комментарий...
Твой дом труба шаталь

логи в гите, кто-то не умеет в gitignore.
мапинги в котнроллере через жопу - кто-то не умеет в REST
пароли в конфиг файлах (zz#~n3wf) - кто-то не умеет в секурити
бизнес логика в контроллерах - кто-то не умеет в Spring
исрпользование spring.jpa.hibernate.ddl-auto - кто-то не умеет в миграции

Ответить
Развернуть ветку
Герман Воронов
бизнес логика в контроллерах - кто-то не умеет в Spring

Да тут наверное даже не в спринге дело
А вообще конечно "дорого быстро качественно" - выбирайте любые два пункта 🤣

Ответить
Развернуть ветку
Daily Science

Просто студент хотел codereview получить на халяву

Ответить
Развернуть ветку
Алексей

Взгляд со стороны...
Какой-то непрофессиональный сленг и оценочное суждение не совсем понятно для чего.
Здесь же совершенно различные статьи размещают разрабы и не только, ориентированные на абсолютно разные скилзы и компетенции программёров.
Коллеги, если вам показалась эта статья не столь интересной или не столь информативной, пожалуйста, не смотрите.
Только не понимаю, зачем давать свою оценку, да еще используя негатив и ненормативную лексику, лишь ради того, чтобы просто дать.

Или у вас какие-то свои мотивы и обиды на всех во всём мире?!

Ответить
Развернуть ветку
Герман Воронов

Thread.sleep(100); еще из забавного

Ответить
Развернуть ветку
Daily Science

" я разместил его в GitHub" и ссылка на bitbucket =)

Ответить
Развернуть ветку
Читать все 6 комментариев
null