Время прочтения: 6 мин.

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

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

  1. Писать, хранить, редактировать SQL запросы;
  2. Создавать подключения к различным СУБД;
  3. Исполнять SQL запросы;
  4. Выгружать результаты отработки запросов в Excel, обходя его ограничение в 1 млн строк (выборка автоматически разбивается по excel листам);
  5. Разграничивать роли пользователей (пользователь/администратор);
  6. Также программа имеет собственный механизм регистрации/авторизации, логирования действий пользователей;
  7. Для примера я также приложил сервер СУБД 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

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