«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>
Ввиду того что код программы объемен – я разместил его в GitHub – ссылка.
В репозитории проект настроен на работу с БД PostgreSql, которая разворачивается через Docker
Буду рад, если моя программа будет полезна при решении ваших задач.