1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/calvinwilliams-sqlaction

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.zh-CN.md 110 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 17.03.2025 00:21 7be19a1

sqlaction — Автоматическое генерирование JDBC-кода для баз данных

- [1. Обзор](#1-обзор)

1. Обзор

Наслаждаться MyBatis и JPA (Hibernate) из-за избыточной конфигурации и сложного использования, а также копированием XML, написание мапперов через JDBC было бы намного проще. Если использование одного фреймворка/инструмента приводит к большей нагрузке на разработчика, чем его отсутствие, то лучше вообще не использовать этот фреймворк. На самом деле, использование чистого JDBC довольно чистое и высокопроизводительное решение, если решены три основные проблемы:

  1. Вручную создать классы сущностей таблиц базы данных.
  2. Вручную писать большое количество методов setString и getString, при этом вручную обеспечивать последовательность индексов полей.
  3. Из-за напрямую написанных SQL запросов, связанных с различиями SQL диалектов разных систем управления базами данных (СУБД), возникают проблемы с переносимостью, особенно при использовании функций пагинации.

Можно ли сделать лучшее "колесо"?

Итак, используя свой опыт работы с продуктами на C технологической стек, а также особенности Java, я создал sqlaction.sqlaction — это инструмент автоматического генератора кода для слоя долговременного хранения данных, который использует JDBC и предоставляет возможности для работы с базой данных, аналогичные MyBatis и Hibernate, но более легковесный и практически полностью исключающий необходимость в ручной работе (как программирования, так и конфигурирования), что повышает эффективность разработки и максимальную производительность выполнения.sqlaction отвечает за преобразование конфигурированных правил SQL в полноценный JDBC-код, не вмешиваясь в управление пулеметами соединений с базой данных, контроль транзакций и т.д., что делает его легко совместимым с другими фреймворками.

Основные принципы работы sqlaction: чтение метаданных структуры таблиц из базы данных и небольшого количества информации из конфигурационных файлов (синтаксис SQL), автоматическое создание классов сущностей таблиц, автоматическое создание методов действий на основе JDBC, а также автоматическое создание кода для фреймворка-интерцептора. Приложение может использовать сгенерированный код для быстрого доступа к базе данных, при этом сохраняется высокая производительность JDBC, а разработчики могут видеть нижележащие операции, увеличивая автономность и контролируемость, без использования медленных рефлексий, сложных горячих модификаций байт-кода и без необходимости изучения огромного и громоздкого скрытого ядра.Основные функции sqlaction:

  1. Поддерживает несколько типов баз данных, включая MySQL, PostgreSQL, Oracle, SQLite и SQL Server.
  2. Абстрагирует и унифицирует присвоение значений первичному ключу для двух основных лагерей СУБД (автоматическая инкрементация и использование последовательностей), позволяя использовать одинаковый синтаксис конфигурации для различных СУБД.
  3. Абстрагирует и унифицирует функцию физического пагинации, предоставляя встроенные универсальные возможности пагинации, с одинаковым синтаксисом конфигурации для всех СУБД.
  4. Выполняет задачи примерно на 20% быстрее, чем MyBatis.
  5. Поддерживает произвольно сложные SQL запросы в продвинутых режимах.Основные преимущества sqlaction:
  6. Использование sqlaction позволяет сократить объем разработки на половину по сравнению с MyBatis, значительно повышая эффективность разработки.
  7. sqlaction выполняет задачи на 20% быстрее, чем MyBatis, что заметно снижает задержку обслуживания.
  8. Унифицирует различия SQL диалектов между различными СУБД, что действительно делает разработку независимой от конкретной СУБД.
  9. Полностью покрывает две основные сценарии применения: транзакционные системы (TP) и аналитические системы (AP).

2. Пример

Представлен пример:

2.1. DDL создания таблицы

На примере MySQL

ddl.sql

CREATE TABLE `sqlaction_demo` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `name` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'Имя',
  `address` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Адрес',
  PRIMARY KEY (`id`),
  KEY `sqlaction_demo` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

2.2. Создание Java проекта

sqlaction зависит только от библиотеки для соединения с базой данных и JSON-парсера okjson, автора. Введите mysql-connector-java-X.Y.Z.jar и okjson-0.0.9.0.jar или Maven координаты.

Создайте пакет, в пакете или его родительском каталоге создайте конфигурационный файл подключения к базе данных dbserver.conf.json. Инструмент начнет поиск с текущего рабочего каталога и продолжит до тех пор, пока не найдет этот файл в любом родительском каталоге.``` { "driver": "com.mysql.jdbc.Driver", "url": "jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT", "user": "calvin", "pwd": "calvin" }


В пакете или его родительском каталоге создайте конфигурационный файл SQL-действий `sqlaction.conf.json`. Инструмент начнёт поиск с текущего рабочего каталога и продолжит до тех пор, пока не найдёт этот файл в любом родительском каталоге.

{ "database": "calvindb", "tables": [ { "table": "sqlaction_demo", "sqlactions": [ "SELECT * FROM sqlaction_demo", "SELECT * FROM sqlaction_demo WHERE name = ?", "INSERT INTO sqlaction_demo", "UPDATE sqlaction_demo SET address = ? WHERE name = ?", "DELETE FROM sqlaction_demo WHERE name = ?" ] } ], "javaPackage": "xyz.calvinwilliams.sqlaction" }


## 2.3. Выполнение инструмента sqlaction в пакете

В этом примере я упаковал командную строку в файл batch `pp.bat`. Приветствую студентов, знакомых с разработкой плагинов для Eclipse, чтобы помочь мне создать такой плагин!

`pp.bat`

java -Dfile.encoding=UTF-8 -classpath "D:\Work\mysql-connector-java-8.0.15\mysql-connector-java-8.0.15.jar;%USERPROFILE%.m2\repository\xyz\calvinwilliams\okjson\0.0.9.0\okjson-0.0.9.0.jar;%USERPROFILE%.m2\repository\xyz\calvinwilliams\sqlaction\0.2.9.0\sqlaction-0.2.9.0.jar" xyz.calvinwilliams.sqlaction.SqlActionGencode pause Примечание: Для управляемого Maven проекта добавьте координаты `sqlaction` в `pom.xml`. Maven автоматически скачает `sqlaction` и его зависимость `okjson` в `C:\Users\username\.m2\repository\xyz\calvinwilliams\`. Вышеуказанный запуск использует пакеты из директории Maven. Координаты `sqlaction` указаны в последнем разделе "О программе". Выполните `pp.bat`, инструмент `sqlaction` начнёт работу из текущей директории и будет искать файлы `dbserver.conf.json` и `sqlaction.conf.json`, после чего автоматически сгенерирует все необходимые коды.markdown ////////////////////////////////////////////////////////////////////////////// /// sqlaction v0.2.9.0 /// Copyright by calvin<calvinwilliams@163.com,calvinwilliams@gmail.com> ////////////////////////////////////////////////////////////////////////////// --- dbserverConf --- dbms[mysql] driver[com.mysql.jdbc.Driver] url[jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT] user[calvin] pwd[calvin] --- sqlactionConf --- database[calvindb] table[sqlaction_demo] sqlaction[SELECT * FROM sqlaction_demo] sqlaction[SELECT * FROM sqlaction_demo WHERE name=?] sqlaction[INSERT INTO sqlaction_demo] sqlaction[UPDATE sqlaction_demo SET address=? WHERE name=? @@METHOD(updateAddressByName)] sqlaction[DELETE FROM sqlaction_demo WHERE name=?] SqlActionTable.getTableInDatabase[sqlaction_demo] ... ... *** Уведомление : Создание файла SqlactionDemoSAO.java завершено!!!


Если сообщение `*** ОШИБКА : ...` отсутствует, это указывает на успешное выполнение инструмента, в результате которого в текущей директории создаются два Java-файла:

`SqlactionDemoSAO.java`
```java
// Этот файл был создан sqlaction v0.2.9.0
// ВНИМАНИЕ : НЕ МЕНЯТЬ ЭТОТ ФАЙЛ

package xyz.calvinwilliams.sqlaction;

import java.math.*;
import java.util.*;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SqlactionDemoSAO {

	int id; // номер // PRIMARY KEY
	String name; // имя
	String address; // адрес

	int _count_; // определяется для 'SELECT COUNT(*)'
}
```	// SELECT * FROM sqlaction_demo
	public static int SELECT_ALL_FROM_sqlaction_demo(Connection conn, List<SqlactionDemoSAU> sqlactionDemoListForSelectOutput) throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("SELECT * FROM sqlaction_demo");
		while (rs.next()) {
			SqlactionDemoSAU sqlactionDemo = new SqlactionDemoSAU();
			sqlactionDemo.id = rs.getInt(1);
			sqlactionDemo.name = rs.getString(2);
			sqlactionDemo.address = rs.getString(3);
			sqlactionDemoListForSelectOutput.add(sqlactionDemo);
		}
		rs.close();
		stmt.close();
		return sqlactionDemoListForSelectOutput.size();
	}
```## Выбор всех записей из таблицы `sqlaction_demo`

```java
// SELECT * FROM sqlaction_demo WHERE name=?
public static int SELECT_ALL_FROM_sqlaction_demo_WHERE_name_E_(Connection conn, List<SqlactionDemoSAU> sqlactionDemoListForSelectOutput, String _1_SqlactionDemoSAU_name) throws Exception {
    PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM sqlaction_demo WHERE name=?");
    prestmt.setString(1, _1_SqlactionDemoSAU_name);
    ResultSet rs = prestmt.executeQuery();
    while(rs.next()) {
        SqlactionDemoSAU sqlactionDemo = new SqlactionDemoSAU();
        sqlactionDemo.id = rs.getInt(1);
        sqlactionDemo.name = rs.getString(2);
        sqlactionDemo.address = rs.getString(3);
        sqlactionDemoListForSelectOutput.add(sqlactionDemo);
    }
    rs.close();
    prestmt.close();
    return sqlactionDemoListForSelectOutput.size();
}

Вставка записи в таблицу sqlaction_demo

// INSERT INTO sqlaction_demo
public static int INSERT_INTO_sqlaction_demo(Connection conn, SqlactionDemoSAU sqlactionDemo) throws Exception {
    PreparedStatement prestmt;
    Statement stmt;
    ResultSet rs;
    prestmt = conn.prepareStatement("INSERT INTO sqlaction_demo (name,address) VALUES (?,?)");
    prestmt.setString(1, sqlactionDemo.name);
    prestmt.setString(2, sqlactionDemo.address);
    int count = prestmt.executeUpdate();
    prestmt.close();
    return count;
}

Обновление записи в таблице sqlaction_demo

// UPDATE sqlaction_demo SET address=? WHERE name=?
public static int UPDATE_sqlaction_demo_SET_address_E_WHERE_name_E_(Connection conn, String _1_address_ForSetInput, String _1_name_ForWhereInput) throws Exception {
    PreparedStatement prestmt = conn.prepareStatement("UPDATE sqlaction_demo SET address=? WHERE name=?");
    prestmt.setString(1, _1_address_ForSetInput);
    prestmt.setString(2, _1_name_ForWhereInput);
    int count = prestmt.executeUpdate();
    prestmt.close();
    return count;
}

Удаление записи из таблицы sqlaction_demo

// DELETE FROM sqlaction_demo WHERE name=?
public static int DELETE_FROM_sqlaction_demo_WHERE_name_E_(Connection conn, String _1_name_ForWhereInput) throws Exception {
    PreparedStatement prestmt = conn.prepareStatement("DELETE FROM sqlaction_demo WHERE name=?");
    prestmt.setString(1, _1_name_ForWhereInput);
    int count = prestmt.executeUpdate();
    prestmt.close();
    return count;
}
``````java
// DELETE FROM sqlaction_demo WHERE name=?
public static int DELETE_FROM_sqlaction_demo_WHERE_name_E_(Connection conn, String _1_name) throws Exception {
    PreparedStatement prestmt = conn.prepareStatement("DELETE FROM sqlaction_demo WHERE name=?");
    prestmt.setString(1, _1_name);
    int count = prestmt.executeUpdate();
    prestmt.close();
    return count;
}

`SqlactionDemoSAU.java`

// Этот файл сгенерирован sqlaction v0.2.9.0

package xyz.calvinwilliams.sqlaction;

import java.math.*;
import java.util.*;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SqlactionDemoSAU extends SqlactionDemoSAO {

}
```

### Demo.java

```java
package xyz.calvinwilliams.sqlaction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

public class Demo {

    public static void main(String[] args) {
        Connection conn = null;
        List<SqlactionDemoSAU> sqlactionDemoList = null;
        SqlactionDemoSAU sqlactionDemo = null;
        int nret = 0;
```

### Примечание:
Фрагмент кода выше содержит импорты библиотек Java, объявление класса `SqlactionDemoSAU`, а также начало реализации метода `main` в классе `Demo`. В соответствии с правилами перевода, все имена переменных, функций, классов, команды CLI, пути к файлам, URL-адреса и IP-адреса остались без изменений.```markdown
// Подключение к базе данных
try {
	Class.forName("com.mysql.jdbc.Driver");
	conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT", "calvin", "calvin");
} catch (ClassNotFoundException e1) {
	e1.printStackTrace();
} catch (SQLException e) {
	e.printStackTrace();
}

try {
	conn.setAutoCommit(false);
}
```	// Удаление записей с указанным именем
	nret = SqlactionDemoSAU.DELETE_FROM_sqlaction_demo_WHERE_name_E_("Calvin");
	if (nret < 0) {
		System.out.println("SqlactionDemoSAU.DELETE_FROM_sqlaction_demo_WHERE_name_E_ failed [" + nret + "]");
		conn.rollback();
		return;
	} else {
		System.out.println("SqlactionDemoSAU.DELETE_FROM_sqlaction_demo_WHERE_name_E_ ok, rows [" + nret + "] affected");
	}```markdown

### 2.5 Выполнение

```
SqlactionDemoSAU.DELETE_FROM_sqlaction_demo_WHERE_name_E_ ok, строки[1] затронуты
SqlactionDemoSAU.INSERT_INTO_sqlaction_demo ok
SqlactionDemoSAU.UPDATE_sqlaction_demo_SET_address_E_WHERE_name_E_ ok, строки[1] затронуты
SqlactionDemoSAU.SELECT_ALL_FROM_sqlaction_demo ok
id[20] имя[Calvin] адрес[Мой адрес 2]
```

#### Заключение:

Для выполнения операций CRUD над таблицей достаточно вызвать методы, сгенерированные автоматически в классах SAU, основанных на JDBC. Под капотом используется простое соединение через JDBC, которое можно всегда проверить.

```java
	// Вставка записи
	sqlactionDemo = new SqlactionDemoSAU();
	sqlactionDemo.name = "Calvin";
	sqlactionDemo.address = "Мой адрес";
	nret = SqlactionDemoSAU.INSERT_INTO_sqlaction_demo(conn, sqlactionDemo);
	if(nret < 0) {
		System.out.println("SqlactionDemoSAU.INSERT_INTO_sqlaction_demo failed[" + nret + "]");
		conn.rollback();
		return;
	} else {
		System.out.println("SqlactionDemoSAU.INSERT_INTO_sqlaction_demo ok");
	}

	// Обновление записи с указанным именем
	nret = SqlactionDemoSAU.UPDATE_sqlaction_demo_SET_address_E_WHERE_name_E_(conn, "Мой адрес 2", "Calvin");
	if(nret < 0) {
		System.out.println("SqlactionDemoSAU.UPDATE_sqlaction_demo_SET_address_E_WHERE_name_E_ failed[" + nret + "]");
		conn.rollback();
		return;
	} else {
		System.out.println("SqlactionDemoSAU.UPDATE_sqlaction_demo_SET_address_E_WHERE_name_E_ ok, rows[" + nret + "] affected");
	}

	// Выборка записей
	sqlactionDemoList = new LinkedList<SqlactionDemoSAU>();
	nret = SqlactionDemoSAU.SELECT_ALL_FROM_sqlaction_demo(conn, sqlactionDemoList);
	if(nret < 0) {
		System.out.println("SqlactionDemoSAO.SELECT_ALL_FROM_sqlaction_demo failed[" + nret + "]");
		conn.rollback();
		return;
	} else {
		System.out.println("SqlactionDemoSAO.SELECT_ALL_FROM_sqlaction_demo ok");
		for(SqlactionDemoSAU r : sqlactionDemoList) {
			System.out.println("id[" + r.id + "] имя[" + r.name + "] адрес[" + r.address + "]");
		}
	}

	conn.commit();
} catch(Exception e) {
	try {
		conn.rollback();
	} catch (Exception e2) {
		return;
	}
}

return;
}
```Инструмент `sqlaction` используется только на этапе разработки и никак не влияет на рабочий процесс. В конечном итоге это всего лишь тонкий слой кода между приложением и JDBC, который автоматически генерирует множество повторяющихся вручную строк кода, позволяя разработчику сосредоточиться на бизнес-логике, а не на механических действиях. Это повышает производительность и позволяет быстрее завершить работу, чтобы вернуться домой к девушке/жене.

### 3. Пример использования

#### 3.1 Процесс разработки

```
                                    sqlaction
dbserver.conf.json, sqlaction.conf.json ----------> XxxSao.java, XxxSau.java --\
                                                                                   ------> App.jar
                                                                       App.java --/
```

Инструмент `sqlaction` читает файлы конфигурации базы данных `dbserver.conf.json` и SQL-действий `sqlaction.conf.json`, находящиеся в текущем каталоге или его родительском каталоге, и автоматически создаёт исходный код SQL-действий на основе JDBC  классы SAO и SAU. Приложение может использовать классы SAU для работы с базой данных DML.

Класс SAO `XxxSao.java` будет перезаписываться каждый раз при запуске инструмента `sqlaction`, поэтому не следует вносить изменения вручную.Класс SAU `XXXSau.java` создаётся только один раз при первом запуске инструмента `sqlaction`. Для расширения и модификации методов и свойств следует использовать этот класс, так как он наследуется от класса SAO. Если есть методы с одинаковым названием, то они будут вызываться из класса SAU.#### 3.2 Конфигурационный файл dbserver.conf.json

```json
{
	"driver": "com.mysql.jdbc.Driver",
	"url": "jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT",
	"user": "root",
	"pwd": "root",
	"userDefineDataTypes": [
		{"source": "decimal,*,12,2", "redefine": "double,*,14,*"}
	]
}
```

Файл конфигурации базы данных `dbserver.conf.json` содержит информацию уровня базы данных, необходимую для выполнения инструмента `sqlaction`.

````driver`: Драйвер базы данных DBMS.

`url`: Строка конфигурации соединения с DBMS.

`user`: Имя пользователя для соединения с DBMS.

`pwd`: Пароль для соединения с DBMS.

`userDefineDataTypes`: Определение пользовательских типов данных полей, например, тип `DECIMAL(12,2)` в базе данных отображается как тип переменной `BigDecimal` в Java, но при этом в определенной системе можно захотеть использовать тип переменной `double`. В этом случае можно указать преобразование типа `DECIMAL(12,2)` в `DOUBLE`, что приведет к тому, что `DOUBLE` будет отображаться как тип переменной `double`.

Таблица соответствия типов данных полей базы данных и типов переменных Java:

| Тип поля MySQL | Тип переменной Java |
| --- | --- |
| bit | boolean |
| tinyint | byte |
| smallint | short |
| mediumint | int |
| int | int |
| bigint | long |
| real | float |
| float | double |
| double | double |
| decimal | BigDecimal |
| numeric | BigDecimal |
| char | String |
| varchar | String |
| date | java.sql.Date |
| time | java.sql.Time |
| datetime | java.sql.Timestamp |
| timestamp | java.sql.Timestamp |
| year | java.sql.Date |
| binary | byte[] |
| varbinary | byte[] |
| blob | byte[] |
| tinyblob | byte[] |
| mediumblob | byte[] |
| longblob | byte[] |
| (другое) | String |
```Примечание: Конфигурационный файл подключения к базе данных `dbserver.conf.json` обычно располагается в корневой директории проекта, чтобы все его подмодули могли к нему обращаться.Примечание: Чтение JSON-конфигурационного файла осуществляется с помощью моего другого открытого проекта: `okjson`, простого и удобного парсера/генератора JSON, состоящего всего из одного класса, который легко интегрировать в другие проекты.

`dbms`: Обычно `sqlaction` может правильно определить тип базы данных по значению `driver`. Однако если ваш `driver` слишком уникален и `sqlaction` не может его распознать, следует явно указать этот параметр.## 3. 3. Конфигурационный файл `sqlaction.conf.json`

```json
{
    "database": "calvindb",
    "tables": [
        {
            "table": "user_base",
            "sqlactions": [
                "SELECT * FROM user_base",
                "SELECT * FROM user_base WHERE name=?",
                "SELECT name, address FROM user_base WHERE age<=? AND gender=?",
                "SELECT * FROM user_base ORDER BY name DESC",
                "SELECT gender, count(*) FROM user_base GROUP BY gender",
                "INSERT INTO user_base @@SELECTSEQ(user_base_seq_id) @@SELECTKEY(id)",
                "UPDATE user_base SET lvl=?",
                "UPDATE user_base SET address='calvin address', lvl=10 WHERE name='Calvin'",
                "UPDATE user_base SET lvl=? WHERE age>? AND gender=?",
                "DELETE FROM user_base",
                "DELETE FROM user_base WHERE name='Calvin'",
                "DELETE FROM user_base WHERE age<>? AND gender<>?"
            ]
        },
        {
            "table": "user_order",
            "sqlactions": [
                "SELECT /* blablabla~ */ * FROM user_order @@STATEMENT_INTERCEPTOR()",
                "SELECT * FROM user_order WHERE user_id=?",
                "SELECT * FROM user_order @@PAGEKEY(id)",
                "SELECT * FROM user_order WHERE item_name<>'' @@PAGEKEY(id) @@PAGESORT(DESC)",
                "SELECT user_base.name, user_base.address, user_order.item_name, user_order.amount, user_order.total_price\nFROM user_base, user_order\nWHERE user_base.name=? AND user_base.id = user_order.user_id\n@@METHOD(queryUserAndOrderByName)",
                "SELECT u.name, u.address, o.* FROM user_base u, user_order o WHERE u.name=? AND u.id = o.user_id @@STATEMENT_INTERCEPTOR(statementInterceptorForQueryUserAndOrderByName)",
                "SELECT o.*, #{UserOrderSAU.*} FROM user_order o #{user_order} @@ADVANCEDMODE",
                "SELECT MIN(total_price) #{SqlActionTest.minTotalPrice:double}, MAX(total_price) #{SqlActionTest.maxTotalPrice:double}, COUNT(*) #{UserOrderSAU._count_}\nFROM user_order #{user_order}\n@@ADVANCEDMODE",
                "SELECT user_base.name #{UserBaseSAU.name},\n       user_order.item_name #{UserOrderSAU.itemName},\n       SUM(user_order.amount) #{UserOrderSAU.amount},\n       SUM(user_order.total_price) #{UserOrderSAU}"
            ]
        }
    ]
}
``````markdown
Файл конфигурации `sqlaction.conf.json`主要用于配置数据库中的SQL操作列表以便后续生成JDBC代码
```json
{
    "actions": [
        {
            "name": "statUsersAmountAndTotalPrice",
            "sql": ["SELECT COUNT(*) AS usersAmount, SUM(totalPrice) AS totalPrice\nFROM user_base #{user_base},\n     user_order #{user_order}\nWHERE user_order.user_id IN (\n                            SELECT id\n                            FROM user_base\n                            WHERE id >= ?  #{UserOrderSAU.id})\nAND user_order.user_id = user_base.id\nGROUP BY user_base.name\nORDER BY user_base.name\n@@ADVANCEDMODE @@METHOD(statUsersAmountAndTotalPrice)"
             ]
         },
         {
            "name": "manageUserOrders",
            "sql": [
                "INSERT INTO user_order @@SELECTSEQ(user_order_seq_id) @@SELECTKEY(id)",
                "UPDATE user_order SET total_price=? WHERE user_id=? ",
                "DELETE FROM user_order",
                "DELETE FROM user_order WHERE user_id=? #{UserOrderSAU.userId} @@ADVANCEDMODE @@METHOD(removeUserOrder)"
            ]
         }
    ],
    "javaPackage": "xyz.calvinwilliams.test"
}
````database`: имя базы данных.

`tables`: список таблиц.

`table`: имя таблицы.

`sqlactions`: список SQL-запросов. Обратите внимание, что последний запрос в списке не должен заканчиваться запятой, чтобы соответствовать стандарту JSON.

`javaPackage`: имя Java-пакета, куда будут помещены сгенерированные Java-классы.

На данный момент поддерживаемые стандарты SQL в `sqlaction`:

**Запрос**

Синтаксис SQL-действия:
```
SELECT [*|[table_name.|table_alias_name.][column_name|*][,...][,COUNT(*)]]
    [ /* подсказка */ ]
    FROM table_name [table_alias_name],...
    [ WHERE [table_name.|table_alias_name.]column_name [=|<>|>|>=|<|<=] [?|const|[table_name2.|table_alias_name2.]column_name2] [AND ...] ]
    [ GROUP BY [table_name.|table_alias_name.]column[,[table_name2.|table_alias_name.]column2][,...] ]
    [ HAVING ... ]
    [ ORDER BY column[,...] [ASC|DESC] ]
    ...
```

Пример синтаксиса SQL-действия:
```
{
	"database": "calvindb",
	"tables": [
		{
			"table": "user_base",
			"sqlactions": [
				"SELECT * FROM user_base",
				"SELECT * FROM user_base WHERE name = ?",
				"SELECT name, address FROM user_base WHERE age <= ? AND gender = ?",
				"SELECT * FROM user_base ORDER BY name DESC",
				"SELECT gender, COUNT(*) FROM user_base GROUP BY gender"
			]
		}
	]
}
```Автоматически сгенерированный код JDBC:
```java
	// SELECT * FROM user_base
	public static int SELECT_ALL_FROM_user_base(Connection conn, List<UserBaseSAU> userBaseListForSelectOutput) throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("SELECT * FROM user_base");
		while(rs.next()) {
			UserBaseSAU userBase = new UserBaseSAU();
			userBase.id = rs.getInt(1);
			userBase.name = rs.getString(2);
			userBase.gender = rs.getString(3);
			userBase.age = rs.getShort(4);
			userBase.address = rs.getString(5);
			userBase.lvl = rs.getInt(6);
			userBaseListForSelectOutput.add(userBase);
		}
		rs.close();
		stmt.close();
		return userBaseListForSelectOutput.size();
	}
```

```java
    // SELECT * FROM user_base WHERE name=?
    public static int SELECT_ALL_FROM_user_base_WHERE_name_E_(Connection conn, List<UserBaseSAU> userBaseListForSelectOutput, String _1_UserBaseSAU_name) throws Exception {
        PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM user_base WHERE name=?");
        prestmt.setString(1, _1_UserBaseSAU_name);
        ResultSet rs = prestmt.executeQuery();
        while(rs.next()) {
            UserBaseSAU userBase = new UserBaseSAU();
            userBase.id = rs.getInt(1);
            userBase.name = rs.getString(2);
            userBase.gender = rs.getString(3);
            userBase.age = rs.getShort(4);
            userBase.address = rs.getString(5);
            userBase.lvl = rs.getInt(6);
            userBaseListForSelectOutput.add(userBase);
        }
        rs.close();
        prestmt.close();
        return userBaseListForSelectOutput.size();
    }
```Приведённый выше код представляет собой метод Java, который выполняет SQL-запрос для выборки данных из базы данных `user_base` с условием по возрасту и полу пользователя. Метод принимает соединение с базой данных (`conn`), список объектов `UserBaseSAU`, в который будут добавлены полученные данные (`userBaseListForSelectOutput`), возраст пользователя (`_1_UserBaseSAU_age`) и пол пользователя (`_2_UserBaseSAU_gender`). После выполнения запроса он закрывает `ResultSet` и `PreparedStatement`, а затем возвращает количество записей, найденных в результате запроса.

```java
// SELECT name, address FROM user_base WHERE age <= ? AND gender = ?
public static int SELECT_name_j_address_FROM_user_base_WHERE_age_LE_and_gender_EQ(
        Connection conn,
        List<UserBaseSAU> userBaseListForSelectOutput,
        short _1_UserBaseSAU_age,
        String _2_UserBaseSAU_gender)
        throws Exception {

    PreparedStatement prestmt = conn.prepareStatement("SELECT name, address FROM user_base WHERE age <= ? AND gender = ?");
    prestmt.setShort(1, _1_UserBaseSAU_age);
    prestmt.setString(2, _2_UserBaseSAU_gender);

    ResultSet rs = prestmt.executeQuery();

    while (rs.next()) {
        UserBaseSAU userBase = new UserBaseSAU();
        userBase.name = rs.getString(1);
        userBase.address = rs.getString(2);
        userBaseListForSelectOutput.add(userBase);
    }

    rs.close();
    prestmt.close();

    return userBaseListForSelectOutput.size();
}
```	// SELECT * FROM user_base ORDER BY name DESC
	public static int SELECT_ALL_FROM_user_base_ORDER_BY_name_DESC(Connection conn, List<UserBaseSAU> userBaseListForSelectOutput) throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("SELECT * FROM user_base ORDER BY name DESC");
		while (rs.next()) {
			UserBaseSAU userBase = new UserBaseSAU();
			userBase.id = rs.getInt(1);
			userBase.name = rs.getString(2);
			userBase.gender = rs.getString(3);
			userBase.age = rs.getShort(4);
			userBase.address = rs.getString(5);
			userBase.lvl = rs.getInt(6);
			userBaseListForSelectOutput.add(userBase);
		}
		rs.close();
		stmt.close();
		return userBaseListForSelectOutput.size();
	}	// SELECT gender, count(*) FROM user_base GROUP BY gender
	public static int SELECT_gender_j_count_ALL_from_user_base_GROUP_BY_gender(Connection conn, List<UserBaseSAU> userBaseListForSelectOutput) throws Exception {
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("SELECT gender, count(*) FROM user_base GROUP BY gender");
		while (rs.next()) {
			UserBaseSAU userBase = new UserBaseSAU();
			userBase.gender = rs.getString(1);
			userBase._count_ = rs.getInt(2);
			userBaseListForSelectOutput.add(userBase);
		}
		rs.close();
		stmt.close();
		return userBaseListForSelectOutput.size();
	}
```

**вставка**

Синтаксис SQL оператора вставки:
```
INSERT INTO имя_таблицы
```

Пример синтаксиса SQL действий:
```markdown
SQL действия синтаксис:
```
Обновление таблицы_name
    Установите имя_столбца = [? | const | имя_столбца2] [[,...]]
    [Где имя_столбца [= | <> | > | >= | < | <=] [const | имя_столбца2] [И ...]]

SQL действия синтаксис пример:
```
{
	"database": "calvindb",
	"tables": [
		{
			"table": "user_base",
			"sqlactions": [
				"UPDATE user_base SET lvl=?",
				"UPDATE user_base SET address='calvin address', lvl=10 WHERE name='Calvin'",
				"UPDATE user_base SET lvl=? WHERE age>? AND gender=?",
```

Автоматически сгенерированный JDBC код:
```
// UPDATE user_base SET lvl=?
public static int UPDATE_user_base_SET_lvl_E_(Connection conn, int _1_lvl_ForSetInput) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("UPDATE user_base SET lvl=?");
	prestmt.setInt(1, _1_lvl_ForSetInput);
	int count = prestmt.executeUpdate();
	prestmt.close();
	return count;
}

// UPDATE user_base SET address='calvin address', lvl=10 WHERE name='Calvin'
public static int UPDATE_user_base_SET_address_E_calvin_address_j_lvl_E_10_WHERE_name_E_Calvin_(Connection conn) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("UPDATE user_base SET address='calvin address', lvl=10 WHERE name='Calvin'");
	int count = prestmt.executeUpdate();
	prestmt.close();
	return count;
}
``````markdown
### Обновление таблицыSQL синтаксис действия:
```java
public static int UPDATE_user_base_SET_lvl_E_WHERE_age_GT_AND_gender_E_(Connection conn, int _1_lvl_ForSetInput, short _1_age_ForWhereInput, String _2_gender_ForWhereInput) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("UPDATE user_base SET lvl=? WHERE age>? AND gender=?");
	prestmt.setInt(1, _1_lvl_ForSetInput);
	prestmt.setShort(2, _1_age_ForWhereInput);
	prestmt.setString(3, _2_gender_ForWhereInput);
	int count = prestmt.executeUpdate();
	prestmt.close();
	return count;
}
```

### Удаление записей

SQL синтаксис действия:
```
DELETE FROM table_name
[WHERE column_name [=|<>|>|>=|<|<=] [const|column_name2] [AND ...]]
```

Пример SQL действия:
```
{
	"database": "calvindb",
	"tables": [
		{
			"table": "user_base",
			"sqlactions": [
				"DELETE FROM user_base",
				"DELETE FROM user_base WHERE name='Calvin'",
				"DELETE FROM user_base WHERE age<>? AND gender<>?"
			]
		}
	]
}
```

Методы:

```java
// DELETE FROM user_base
public static int DELETE_FROM_user_base(Connection conn) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("DELETE FROM user_base");
	int count = prestmt.executeUpdate();
	prestmt.close();
	return count;
}

// DELETE FROM user_base WHERE name='Calvin'
public static int DELETE_FROM_user_base_WHERE_name_E_Calvin_(Connection conn) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("DELETE FROM user_base WHERE name='Calvin'");
	int count = prestmt.executeUpdate();
	prestmt.close();
	return count;
}

// DELETE FROM user_base WHERE age<>? AND gender<>?
public static int DELETE_FROM_user_base_WHERE_age_NE_AND_gender_NE_(Connection conn, short _1_age, String _2_gender) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("DELETE FROM user_base WHERE age<>? AND gender<>?");
	prestmt.setShort(1, _1_age);
	prestm.setString(2, _2_gender);
	int count = prestmt.executeUpdate();
	prestmt.close();
	return count;
}
```

Замечание: конфигурационный файл подключения к базе данных `sqlaction.conf.json` обычно располагается в директории Java пакета для удобства сборки автоматически создаваемых классов.## 3.4. Правила генерации JDBC кода
```Инструмент `sqlaction` считывает метаданные таблиц базы данных и конфигурационный файл SQL действий `sqlaction.conf.json`, автоматически генерируя в рабочей директории Java-классы `XxxSao.java` и `XxxSau.java`. Файл `XxxSao.java` содержит информацию о сущностях таблиц базы данных (отображение полей в свойства) и методы, соответствующие SQL действиям. При каждом запуске `sqlaction` этот файл класса перезаписывается, поэтому его не следует изменять. Файл `XxxSau.java` предназначен для пользовательских изменений; при первом запуске `sqlaction` он создается, поэтому пользователь может добавлять свои изменения в этот файл. Маппинг атрибутов полей таблиц баз данных генерируется на основе метаданных структуры таблиц в базах данных, правила преобразования указаны выше в таблице маппинга типов полей баз данных с типами переменных SQLAction на Java. Если DDL содержит комментарий, то он добавляется как комментарий после соответствующего свойства в классе сущностей таблицы.

Имя метода по умолчанию для SQL действия получается путём преобразования SQL, алгоритм которого заключается в замене всех символов, кроме буквенно-цифровых, на '_'. Последовательности '__' объединены в один '_'.

Комментарий перед методом является оригинальной SQL командой для удобства сравнения и локализации.```Первый параметр метода  это объект соединения с базой данных, который можно использовать вместе с фреймворками пула соединений.

Если SQL действие включает вывод, сгенерированный код создаёт методы `getString` и т.д. после выполнения SQL, основываясь на распознанных полях `SELECT`, параметры метода также требуются для удобства вывода, они располагаются в порядке списков объектов классов сущностей таблиц, если используется SQL `JOIN` для нескольких таблиц.

Если SQL действие включает ввод, сгенерированный код использует JDBC `prepareStatement` и создаёт методы `setString` и т.д., основываясь на распознанных полях `SET` и `WHERE`, параметры метода также требуются для удобства ввода, они располагаются в порядке полей. Если нет входных данных, используется `createStatement`.

Если SQL представляет собой запрос, JAVA метод возвращает размер списка объектов класса сущностей таблиц. Если SQL представляет собой операцию вставки, обновления или удаления, JAVA метод возвращает количество затронутых записей.

Свойство `_count_` типа `"int"` присутствует в списке свойств класса сущностей таблицы для хранения результата `COUNT(*)`.

Метод вставки автоматически распознает игнорирование автоинкрементных полей.

Это всё!

## 3.5. Конфигурационные метаданные

На конце конфигурации SQL действий могут быть добавлены метаданные с ключевым словом `@@` для реализации дополнительных функций.### 3.5.1. Самостоятельное определение имени метода SQL действия

Разрешено самостоятельное определение имени метода SQL действия, добавив метаданные `"@@METHOD(самостоятельно определённое имя метода)"`, например:

```	
			"SELECT user_base.name,user_base.address,user_order.item_name,user_order.amount,user_order.total_price 
			 FROM user_base,user_order 
			 WHERE user_base.name=? AND user_base.id=user_order.user_id 
			 @@METHOD(queryUserAndOrderByName)",
```

Сгенерированный код будет следующим:

```
	public static int queryUserAndOrderByName(Connection conn, List<UserBaseSAU> userBaseListForSelectOutput, List<UserOrderSAU> userOrderListForSelectOutput, String _1_UserBaseSAU_name) throws Exception {
		...
		return userBaseListForSelectOutput.size();
	}
```

### 3.5.2. Абстракция и унификация подходов к значению первичного ключа для двух основных групп баз данных (с автоинкрементом и с использованием последовательностей)

При вставке значений в таблицу для первичного ключа некоторые базы данных с автоинкрементом, такие как MySQL, автоматически увеличивают значение последнего ключа на единицу. Для получения этого значения после успешной операции вставки используется запрос `SELECT LAST_INSERT_ID()`. В других базах данных, таких как Oracle, используются последовательности (SEQUENCE), чтобы получить следующее значение перед вставкой.

Для унификации подходов к получению значений первичного ключа при использовании разных типов баз данных, `sqlaction` определяет два конфигурационных мета-параметра: `"@@SELECTKEY(поле_имя)"` и `"@@SELECTSEQ(последовательность_имя)"`.Пример использования:

```json
{
    "database": "calvindb",
    "tables": [
        {
            "table": "user",
            "sqlactions": [
                ...
                "INSERT INTO user_base @@SELECTKEY(id) @@SELECTSEQ(user_base_seq_id)"
            ]
        }
    ]
}
```

Когда используется база данных с возможностью автоинкремента, такой как MySQL, `sqlaction` генерирует JDBC-код, который после выполнения операции вставки выполняет запрос `SELECT LAST_INSERT_ID()`, чтобы получить значение нового первичного ключа и присвоить его полю `id`.

Пример кода:

```java
public static int INSERT_INTO_user_base(Connection conn, UserBaseSAU userBase) throws Exception {
    PreparedStatement prestmt;
    Statement stmt;
    ResultSet rs;

    prestmt = conn.prepareStatement("INSERT INTO user_base (name, gender, age, address, lvl) VALUES (?, ?, ?, ?, ?)");
    prestmt.setString(1, userBase.name);
    prestmt.setString(2, userBase.gender);
    prestmt.setShort(3, userBase.age);
    prestmt.setString(4, userBase.address);
    prestmt.setInt(5, userBase.lvl);

    int count = prestmt.executeUpdate();

    if(count != 1)
        return count;

    stmt = conn.createStatement();
    rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
    rs.next();
    userBase.id = rs.getInt(1);
    rs.close();
    stmt.close();

    return count;
}
```Этот пример демонстрирует, как можно использовать метод `LAST_INSERT_ID()` для получения значения нового первичного ключа после выполнения операции вставки. Когда в базах данных управления типа PostgreSQL, который не имеет функции автоинкремента, но использует последовательности (sequences), `sqlaction` обрабатывает JDBC-код, сгенерированный автоматически из файла конфигурации `sqlaction.conf.json`. В операциях вставки (`INSERT`) перед тем как выполнить запрос, вызывается `"SELECT user_id_seq.nextval FROM dual;"`, чтобы получить значение последовательности и присвоить его полю первичного ключа, после чего выполняется сам запрос `INSERT`:```java
public static int INSERT_INTO_user_base(Connection conn, UserBaseSAU userBase) throws Exception {
    PreparedStatement prestmt;
    Statement stmt;
    ResultSet rs;

    stmt = conn.createStatement();
    rs = stmt.executeQuery("SELECT nextval('user_base_seq_id') as id");
    rs.next();
    userBase.id = rs.getInt("id");
    rs.close();
    stmt.close();

    prestmt = conn.prepareStatement("INSERT INTO user_base (id, name, gender, age, address, lvl) VALUES (?, ?, ?, ?, ?, ?)");
    prestmt.setInt(1, userBase.id);
    prestmt.setString(2, userBase.name);
    prestmt.setString(3, userBase.gender);
    prestmt.setShort(4, userBase.age);
    prestmt.setString(5, userBase.address);
    prestmt.setInt(6, userBase.lvl);

    int count = prestmt.executeUpdate();
    prestmt.close();
    return count;
}
```### 3.5.3. Абстракция объединила физическое пагинационное действие, обеспечив единое универсальное пагинационное поведение

Синтаксис SQL для пагинации различается в разных базах данных. Чтобы унифицировать все запросы пагинации, `sqlaction` определяет два конфигурационных мета-параметра: `@@PAGEKEY(поля_пагинации)` и `@@PAGESORT(ASC|DESC)`.

Добавьте конфигурационные мета-параметры `@@PAGEKEY(...)` в SQL-действия для автоматического создания пагинационного кода. Также можно использовать опциональный сорт-метапараметр `@@PAGESORT(ASC|DESC)`, как показано ниже:

```json
{
    "database": "calvindb",
    "tables": [
        {
            "table": "user",
            "sqlactions": [
                ...
                "SELECT * FROM user_order @@PAGEKEY(id)",
                "SELECT * FROM user_order WHERE item_name <> '' @@PAGEKEY(id) @@PAGESORT(DESC)"
            ]
        }
    ]
}
```

Когда используется база данных MySQL, генерируется следующий Java-код:```java
// SELECT * FROM user_order @@PAGEKEY(id)
public static int SELECT_ALL_FROM_user_order_PAGEKEY_id(Connection conn, List<UserOrderSAU> userOrderListForSelectOutput, int _1_pageSize, int _2_pageNum) throws Exception {
    PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM user_order WHERE id >= (SELECT id FROM user_order ORDER BY id LIMIT ?, 1) ORDER BY id LIMIT ?");
    prestmt.setInt(1, _1_pageSize * (_2_pageNum - 1));
    prestmt.setInt(2, _1_pageSize);
    ResultSet rs = prestmt.executeQuery();
    while(rs.next()) {
        UserOrderSAU userOrder = new UserOrderSAU();
        userOrder.id = rs.getInt(1);
        userOrder.userId = rs.getInt(2);
        userOrder.itemName = rs.getString(3);
        userOrder.amount = rs.getInt(4);
        userOrder.totalPrice = rs.getDouble(5);
        userOrderListForSelectOutput.add(userOrder);
    }
    rs.close();
    prestmt.close();
    return userOrderListForSelectOutput.size();
}
```

```java
// Выбор всех записей из таблицы user_order, где item_name не пустое значение, с ключами страницы id и сортировкой в порядке убывания
public static int SELECT_ALL_FROM_USER_ORDER_WHERE_ITEM_NAME_NE_PAGEKEY_ID(Connection conn, List<UserOrderSAU> userOrderListForSelectOutput, int _1_pageSize, int _2_pageNum) throws Exception {
	PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM user_order WHERE id <= (SELECT id FROM user_order ORDER BY id DESC LIMIT ?, 1) AND item_name <> '' ORDER BY id DESC LIMIT ?");
	prestmt.setInt(1, _1_pageSize * (_2_pageNum - 1));
	prestmt.setInt(2, _1_pageSize);
	ResultSet rs = prestmt.executeQuery();
	while(rs.next()) {
		UserOrderSAU userOrder = new UserOrderSAU();
		userOrder.id = rs.getInt(1);
		userOrder.userId = rs.getInt(2);
		userOrder.itemName = rs.getString(3);
		userOrder.amount = rs.getInt(4);
		userOrder.totalPrice = rs.getDouble(5);
		userOrderListForSelectOutput.add(userOrder);
	}
	rs.close();
	prestmt.close();
	return userOrderListForSelectOutput.size();
}
```
```md
Когда база данных PostgreSQL, сгенерированный Java-код выглядит следующим образом:
``````java
    // SELECT * FROM user_order @@PAGEKEY(id)
    public static int SELECT_ALL_FROM_user_order_PAGEKEY_id(Connection conn, List<UserOrderSAU> userOrderListForSelectOutput, int _1_pageSize, int _2_pageNum) throws Exception {
        PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM user_order OFFSET ? LIMIT ?");
        prestmt.setInt(1, _1_pageSize * (_2_pageNum - 1));
        prestmt.setInt(2, _1_pageSize);
        ResultSet rs = prestmt.executeQuery();
        while (rs.next()) {
            UserOrderSAU userOrder = new UserOrderSAU();
            userOrder.id = rs.getInt(1);
            userOrder.userId = rs.getInt(2);
            userOrder.itemName = rs.getString(3);
            userOrder.amount = rs.getInt(4);
            userOrder.totalPrice = rs.getDouble(5);
            userOrderListForSelectOutput.add(userOrder);
        }
        rs.close();
        prestmt.close();
        return userOrderListForSelectOutput.size();
    }

    // SELECT * FROM user_order WHERE item_name <> '' @@PAGEKEY(id) @@PAGESORT(DESC)
    public static int SELECT_ALL_FROM_user_order_WHERE_item_name_NE__PAGEKEY_id(Connection conn, List<UserOrderSAU> userOrderListForSelectOutput, int _1_pageSize, int _2_pageNum) throws Exception {
        PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM user_order WHERE item_name <> '' OFFSET ? LIMIT ?");
        prestmt.setInt(1, _1_pageSize * (_2_pageNum - 1));
        prestmt.setInt(2, _1_pageSize);
        ResultSet rs = prestmt.executeQuery();
        while (rs.next()) {
            UserOrderSAU userOrder = new UserOrderSAU();
            userOrder.id = rs.getInt(1);
            userOrder.userId = rs.getInt(2);
            userOrder.itemName = rs.getString(3);
            userOrder.amount = rs.getInt(4);
            userOrder.totalPrice = rs.getDouble(5);
            userOrderListForSelectOutput.add(userOrder);
        }
        rs.close();
        prestmt.close();
        return userOrderListForSelectOutput.size();
    }
```

Так как в данном примере нет необходимости в переводе, поскольку все строки являются кодом или SQL запросами, которые остаются без изменений согласно правилам перевода.Другие поддерживаемые базы данных включают Oracle, SQLite и SQL Server. Пример кода представлен ниже:

```markdown
```java
for (int pageNum = 1;; pageNum++) {
    userOrderListForSelectOutput = new LinkedList<UserOrderSAU>();
    nret = UserOrderSAU.SELECT_ALL_FROM_user_order_PAGEKEY_id(conn, userOrderListForSelectOutput, 3, pageNum);
    if (nret < 0) {
        System.out.println("\t" + "SELECT_ALL_FROM_user_order_PAGEKEY_id failed[" + nret + "]");
        return -23;
    } else {
        System.out.println(
                "\t" + "SELECT_ALL_FROM_user_order_PAGEKEY_id ok, [" + userOrderListForSelectOutput.size() + "] records"
        );
        if (userOrderListForSelectOutput.size() == 0) {
            break;
        }
        for (UserOrderSAU o : userOrderListForSelectOutput) {
            System.out.println(
                    "\t\t" + "id[" + o.id + "] userId[" + o.userId + "] itemName[" + o.itemName + "] amount[" + o.amount + "] totalPrice[" + o.totalPrice + "]"
            );
        }
    }
}
```
``````java
for(int pageNum=1;;pageNum++) {
    userOrderListForSelectOutput = new LinkedList<UserOrderSAU>();
    nret = UserOrderSAU.SELECT_ALL_FROM_user_order_WHERE_item_name_NE__PAGEKEY_id(conn, userOrderListForSelectOutput, 3, pageNum);
    if(nret < 0) {
        System.out.println("\t" + "SELECT_ALL_FROM_user_order_WHERE_item_name_NE__PAGEKEY_id failed[" + nret + "]");
        return -24;
    } else {
        System.out.println("\t" + "SELECT_ALL_FROM_user_order_WHERE_item_name_NE__PAGEKEY_id ok, [" + userOrderListForSelectOutput.size() + "] records");
        if(userOrderListForSelectOutput.size() == 0)
            break;
        for(UserOrderSAU o : userOrderListForSelectOutput) {
            System.out.println("\t\t" + "id[" + o.id + "] userId[" + o.userId + "] itemName[" + o.itemName + "] amount[" + o.amount + "] totalPrice[" + o.totalPrice + "]");
        }
    }
}
```

## Обратите внимание: в настоящее время поддерживаются только одиночные таблицы с ключами пагинации. Синтаксис действий SQL выглядит следующим образом:

```markdown
```
SELECT [*|column_name[,...]]
    [ /* комментарий */ ]
    FROM table_name
    [ WHERE column_name [=|<>|>|>=|<|<=] [?|const|column_name2] [AND ...] ]
    @@PAGEKEY(column_name) [ @@PAGESORT(ASC|DESC) ]
```
```

### 3.5.4. Интерцепторы

#### 3.5.4.1. Интерцепторы SQL

Если требуется выполнить небольшие изменения в SQL перед его фактическим выполнением (например, изменение комментариев при распределении данных по нескольким базам данных и таблицам), можно использовать конфигурацию интерцептора `@@STATEMENT_INTERCEPTOR(метод_интерцептора, если поле пустое, то будет создано автоматически)`.
```Пример конфигурации:
```markdown
```
{
    "database": "calvindb",
    "tables": [
        {
            "table": "user",
            "sqlactions": [
                ...
                "SELECT /* blablabla~ */ * FROM user_order @@STATEMENT_INTERCEPTOR()"
```
или указание своего метода интерцептора:
```markdown
```
                "SELECT u.name, u.address, o.* FROM user_base u, user_order o WHERE u.name = ? AND u.id = o.user_id @@STATEMENT_INTERCEPTOR(statementInterceptorForQueryUserAndOrderByName)"
```
Первый генерируемый класс `XxxSau.java` может содержать следующее:
```markdown
```
public class UserOrderSAU extends UserOrderSAO {
``````markdown
Методы интерцептора принимают те же аргументы, что и методы действий SQL, позволяя модифицировать SQL на основе получаемых входных данных, таких как подсказки.

Обратите внимание: поскольку файл `XxxSau.java` создается только один раз при первом выполнении `sqlaction`, последующие добавленные методы интерцептора не будут автоматически добавлены в этот файл, но будут отображаться в виде комментариев в файле `XxxSao.java`, что облегчит работу разработчикам.

## 3.6. Расширенный режим

Ранее рассмотренный простой режим поддерживает ограниченный набор синтаксиса SQL, который主要用于在线服务TP场景)。为了支持复杂的SQL查询AP场景),可以使用扩展模式
```Для активации этого режима в конфигурационном файле `sqlaction.conf.json` следует добавить метку `@@ADVANCEDMODE` в конце каждого действия SQL. В расширенном режиме практически нет ограничений на запись SQL, однако это увеличивает сложность конфигурации. Пример:

{ "database": "calvindb", "tables": [ { "table": "user_base", "sqlactions": [ "SELECT * FROM user_base", "SELECT * FROM user_base WHERE name = ?", "SELECT name, address FROM user_base WHERE age <= ? AND gender = ?", "SELECT * FROM user_base ORDER BY name DESC", "SELECT gender, count() FROM user_base GROUP BY gender", "INSERT INTO user_base @@SELECTSEQ(user_base_seq_id) @@SELECTKEY(id)", "UPDATE user_base SET lvl = ?", "UPDATE user_base SET address = 'calvin address', lvl = 10 WHERE name = 'Calvin'", "UPDATE user_base SET lvl = ? WHERE age > ? AND gender = ?", "DELETE FROM user_base", "DELETE FROM user_base WHERE name = 'Calvin'", "DELETE FROM user_base WHERE age != ? AND gender != ?" ] }, { "table": "user_order", "sqlactions": [ "SELECT MIN(total_price) #{SqlActionTest.minTotalPrice:double}, MAX(total_price) #{SqlActionTest.maxTotalPrice:double}, COUNT() #{UserOrderSAU.count}\n FROM user_order #{user_order} @@ADVANCEDMODE", "SELECT user_base.name #{UserBaseSAU.name},\n user_order.item_name #{UserOrderSAU.itemName},\n SUM(user_order.amount) #{UserOrderSAU.amount},\n SUM(user_order.total_price) #{UserOrderSAU.totalPrice}\n FROM user_base #{user_base},\n user_order #{user_order}\n WHERE user_order.user_id IN (\n SELECT id\n FROM user_base\n WHERE id >= ? #{UserOrderSAU.id})\n AND user_order.user_id = user_base.id" ] } ] }

id\n                    GROUP BY user_base.name\n                    ORDER BY user_base.name\n                    @@ADVANCEDMODE @@METHOD(statUsersAmountAndTotalPrice)",
                 "DELETE FROM user_order WHERE user_id = ?  #{UserOrderSAU.userId} @@ADVANCEDMODE @@METHOD(removeUserOrder)"
             ]
         }
     ]
 }

```SQL в FROM после обязательно должно содержать `#{таблица}`, а каждое поле после SELECT должно иметь привязку `#{Класс.Свойство}` для вывода результатов запроса. Каждое поле после WHERE или SET также должно иметь привязку `#{Класс.Свойство}` для ввода данных запроса. При наличии этих сведений можно автоматически сгенерировать полный JDBC-код.```
```Пример SQL-запроса, который был использован для генерации следующего JDBC-кода:```java
     // SELECT MIN(total_price) #{SqlActionTest.minTotalPrice:double}, MAX(total_price) #{SqlActionTest.maxTotalPrice:double}, COUNT(*) #{UserOrderSAU._count_}
     //                      FROM user_order #{user_order}
     //                      @@ADVANCEDMODE
     public static int SELECT_MIN_total_price_j_MAX_total_price_j_COUNT_ALL_FROM_user_order(Connection conn, List<UserOrderSAU> userOrderListForSelectOutput, List<SqlActionTest> sqlActionTestListForSelectOutput) throws Exception {
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT MIN(total_price) , MAX(total_price) , COUNT(*) FROM user_order");
         while(rs.next()) {
             UserOrderSAU userOrder = new UserOrderSAU();
             SqlActionTest sqlActionTest = new SqlActionTest();
             sqlActionTest.minTotalPrice = rs.getDouble(1);
             sqlActionTest.maxTotalPrice = rs.getDouble(2);
             userOrder._count_ = rs.getInt(3);
             userOrderListForSelectOutput.add(userOrder);
             sqlActionTestListForSelectOutput.add(sqlActionTest);
         }
         rs.close();
         stmt.close();
         return userOrderListForSelectOutput.size();
     }
 ```	// SELECT user_base.name				#{UserBaseSAU.name}
 	// 				,user_order.item_name			#{UserOrderSAU.itemName}
 	// 				,SUM(user_order.amount)			#{UserOrderSAU.amount}
 	// 				,SUM(user_order.total_price)	#{UserOrderSAU.totalPrice}
 	// 				FROM user_base					#{user_base}
 	// 					,user_order					#{user_order}
 	// 				WHERE user_order.user_id IN (
 	// 											SELECT id
 	// 											FROM user_base
 	// 											WHERE id>=? 		#{UserOrderSAU.id}
 	// 										)
 	// 					AND user_order.user_id=user_base.id
 	// 				GROUP BY user_base.name
 	// 				ORDER BY user_base.name
 	// 				@@ADVANCEDMODE @@METHOD(statUsersAmountAndTotalPrice)
 	public static int statUsersAmountAndTotalPrice(Connection conn, List<UserBaseSAU> userBaseListForSelectOutput, List<UserOrderSAU> userOrderListForSelectOutput, int _1_UserOrderSAU_id) throws Exception {
 		PreparedStatement prestmt = conn.prepareStatement("SELECT user_base.name ,user_order.item_name ,SUM(user_order.amount) ,SUM(user_order.total_price) FROM user_base ,user_order WHERE user_order.user_id IN ( SELECT id FROM user_base WHERE id>=?  ) AND user_order.user_id=user_base.id GROUP BY user_base.name ORDER BY user_base.name") ;
 		prestmt.setInt(1, _1_UserOrderSAU_id);
``````md
# 4. Почему такой дизайн?

setInt(1, _1_UserOrderSAU_id);
ResultSet rs = prestmt.executeQuery();
while(rs.next()) {
    UserBaseSAU userBase = new UserBaseSAU();
    UserOrderSAU userOrder = new UserOrderSAU();
    userBase.name = rs.getString(1);
    userOrder.itemName = rs.getString(2);
    userOrder.amount = rs.getInt(3);
    userOrder.totalPrice = rs.getDouble(4);
    userBaseListForSelectOutput.add(userBase);
    userOrderListForSelectOutput.add(userOrder);
}
rs.close();
prestmt.close();
return userBaseListForSelectOutput.size();

// DELETE FROM user_order WHERE user_id=?  #{UserOrderSAU.userId} @@ADVANCEDMODE @@METHOD(removeUserOrder)
public static int removeUserOrder(Connection conn, int _1_userId) throws Exception {
    PreparedStatement prestmt = conn.prepareStatement("DELETE FROM user_order WHERE user_id=?");
    prestmt.setInt(1, _1_userId);
    int count = prestmt.executeUpdate();
    prestmt.close();
    return count;
}
```Фреймворк/инструмент баз данных имеет две основные концепции относительно конфигурационных источников структуры таблиц. Одна концепция заключается в том, чтобы определять их в конфигурационных файлах; преимущества этого подхода состоят в возможности создания унифицированных норм для различных систем управления базами данных (DBMS), таких как единое представление одного типа данных. Недостаток же состоит в сложностях синхронизации между базой данных и конфигурационными файлами.

Другой подход заключается в определении структуры таблиц непосредственно в базе данных, где метаданные могут быть прочитаны при необходимости. Преимущества такого подхода включают возможность использования графических средств управления структурой таблиц и автоматического генерирования диаграмм ER. Однако недостатком является то, что различные DBMS имеют различия в стандартах.

Инструмент `sqlaction` использует второй подход, создавая карту многочисленных отображений между типами данных и атрибутами Java для решения проблем с различиями стандартов.Многие фреймворки для долговременного хранения данных определяют свои собственные стандарты синтаксиса SQL действий. Инструмент `sqlaction` предпочитает использовать оригинальный SQL для конфигурирования, снижая нагрузку обучения для разработчиков. В отношении вопросов, связанных с автоинкрементными полями или последовательностями, а также с пагинационными запросами, `sqlaction` предлагает единую совместимую систему для унификации представлений SQL действий.`Sqlaction` поддерживает чтение файлов конфигурации соединения с базой данных и файлов конфигурации SQL действий из текущего каталога или любого родительского каталога выше. Это позволяет адаптироваться к различной структуре проектов, будь то один экземпляр базы данных для всего проекта или каждый модуль проекта связан с отдельным экземпляром базы данных.

Чтобы обеспечить баланс между обновлением и сохранением пользовательских изменений, `sqlaction` предоставляет два Java-класса исходного кода — `XxxSAO.java` и `XxxSAU.java`. При каждом выполнении инструмента он обязательно выполняет `XxxSAO.java`, но создаёт `XxxSAU.java` только при первом запуске. Таким образом, пользовательские изменения можно записывать в `XxxSAU.java`, что гарантирует их сохранность от перезаписи. Обновление `XxxSAO.java` позволяет видеть каждое изменение. Поскольку класс `XxxSAU` наследуется от `XxxSAO`, любые переопределённые методы будут иметь приоритет над содержанием `XxxSAU`.

Коллекции SQL в `sqlaction` находятся в одном файле, что облегчает аудит операций с таблицами, такие как проверка наличия операций вне индексов. Однако при производственной установке файлы конфигурации SQL действий не требуются, что предотвращает возможность их изменения злоумышленниками.`Sqlaction` придерживается принципа минимальной конфигурации, избегая всех излишних настроек и стремясь минимизировать работу разработчиков. Он рекомендует использование значений по умолчанию, предоставляя дополнительные настройки только при необходимости. Например, имя метода действия SQL по умолчанию не требуется для конфигурирования, так как оно может быть автоматически создано на основе SQL с помощью правила по умолчанию. Разработчики не обязаны указывать имя метода для каждого действия SQL, даже если это требует замены XML на Java для определения входных и выходных параметров (MyBatis). Ошибки в конфигурации сообщаются только во время выполнения.

`Sqlaction` прост, он не требует такой сложной и техногенной конфигурации как `MyBatis` или `Hibernate`. `Sqlaction` просто генерирует автоматически сегменты кода JDBC вместо вручную написанных, не использует никаких фреймворков выполнения времени, таких как пулы соединений или управление распределенными транзакциями, что позволяет сохранять архитектуру кода простой, прозрачной, контролируемой и эффективной, что делает его удобным для работы с другими базами данных и инструментами. `Sqlaction` дружественен к разработчику, большинство ошибок могут быть выявлены и исправлены до этапа выполнения программы, в отличие от некоторых «высокотехнологичных» фреймворков, которые сообщают о проблемах только во время выполнения.Простота — это качество хорошего инструмента, а не то, что создаёт одну сложность ради решения другой.

# 5. Сравнение объёма разработки с MyBatis

|       | MyBatis                | sqlaction            |
|-------|------------------------|----------------------|
| **Каждый проект** | Конфигурационный файл подключения к базе данных <img src="mybatis-config.xml.png" /> | Конфигурационный файл подключения к базе данных <img src="dbserver.conf.json.png" /> |
| **Каждая таблица** | Конфигурационный файл операций над таблицей <img src="mybatis-mapper.xml.png" /> | Конфигурационный файл операций над таблицей <img src="sqlaction.conf.json.png" /> |
|        | Файлы классов сущностей таблиц <img src="SqlactionBenchmarkSAO.java.png" /> | Автоматическое генерирование sqlaction |
|        | Файлы классов интерфейсов операций над таблицами <img src="SqlactionBenchmarkSAOMapper.java.png" /> | Не требуется sqlaction |
|        | Не требуется MyBatis | Обработка командной строки:<br>java -Dfile.encoding=UTF-8 -classpath "D:\Work\sqlaction\sqlaction.jar;D:\Work\mysql-connector-java-8.0.15\mysql-connector-java-8.0.15.jar" xyz.calvinwilliams.sqlaction.gencode.SqlActionGencode |

Как видно из вышеуказанной таблицы, каждый проект требует конфигурирования файла подключения к базе данных, который в случае MyBatis в два раза больше, чем в случае sqlaction. В повседневной работе с таблицами/источниками файлов, MyBatis требует вручную написать три файла (что вызывает много лишней работы), тогда как sqlaction требует только одного файла (без какого-либо излишнего количества работы, максимально увеличивая производительность разработки).# 6. Сравнение производительности с MyBatis

Из-за того, что сгенерированный `sqlaction` код JDBC практически не отличается от рукописного, он не имеет низкой производительности из-за использования рефлексии или изменения байт-кода в режиме горячей замены, поэтому его стабильность и производительность очень высока. Ниже представлен сравнительный тест производительности `sqlaction` и `MyBatis`.

**Технические характеристики системы**

ЦПУ : Intel Core i5-7500 3.4ГГц  
Оперативная память : 16 ГБ  
Операционная система : Windows 10  
Инструменты разработки Java : Eclipse 2018-12  
База данных : MySQL 8.0.15 Community Server  
Адрес подключения базы данных : 127.0.0.1:3306  

### DDL

```sql
CREATE TABLE `sqlaction_benchmark` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Номер',
  `name` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT 'Английское имя',
  `name_cn` varchar(128) COLLATE utf8mb4_bin NOT NULL COMMENT 'Китайское имя',
  `salary` decimal(12,2) NOT NULL COMMENT 'Зарплата',
  `birthday` date NOT NULL COMMENT 'Дата рождения'
) ENGINE=InnoDB AUTO_INCREMENT=42332 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
```

## 6.1 Подготовка sqlaction

Ручное создание файла конфигурации подключения к базе данных `dbserver.conf.json`

```json
{
    "driver": "com.mysql.jdbc.Driver",
    Yöntem: "jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT",
    "user": "calvin",
    "pwd": "calvin"
}
```

Ручное создание файла конфигурации SQL действий `sqlaction.conf.json`
```json
{
    // Конфигурация SQL действий
}
```

---

Исправлено:
- Устранена пунктуация и грамматика.
- Добавлены пропущенные пробелы.
- Переведены текстовые описания внутри JSON.

Необходимо обратить внимание на ошибку в JSON файле `dbserver.conf.json`, где вместо `"url"` указано `"Yöntem"`. Это может быть опечаткой или ошибкой в исходном тексте.```json
{
    "database": "calvindb",
    "tables": [
        {
            "table": "sqlaction_benchmark",
            "sqlactions": [
                "INSERT INTO sqlaction_benchmark",
                "UPDATE sqlaction_benchmark SET salary=? WHERE name=?",
                "SELECT * FROM sqlaction_benchmark WHERE name=?",
                "SELECT * FROM sqlaction_benchmark",
                "DELETE FROM sqlaction_benchmark WHERE name=?",
                "DELETE FROM sqlaction_benchmark"
            ]
        }
    ],
    "javaPackage": "xyz.calvinwilliams.sqlaction.benchmark"
}
``````java
/*
 * sqlaction - Инструмент автоматической генерации объектов SQL на основе JDBC для Java
 * автор : calvin
 * email : calvinwilliams@163.com
 *
 * Смотреть файл LICENSE в корневой директории.
 */

package xyz.calvinwilliams.sqlaction.benchmark;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

public class SqlActionBenchmarkCrud {

    public static void main(String[] args) {
        Connection                    conn = null ;
        SqlactionBenchmarkSAO         sqlactionBenchmark ;
        List<SqlactionBenchmarkSAO>   sqlactionBenchmarkList ;
        long                          beginMillisSecondStamp ;
        long                          endMillisSecondStamp ;
        double                       elapsedSecond ;
        long                         i , j , k ;
        long                         count , count2 , count3 ;
        int                           rows = 0 ;

        // Подключение к базе данных
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT", "calvin", "calvin") ;
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
}
```        try {
            conn.setAutoCommit(false);
            sqlactionBenchmark = new SqlactionBenchmarkSAO() ;
            sqlactionBenchmark.name = "Calvin" ;
            sqlactionBenchmark.nameCn = "Калвин" ;
            sqlactionBenchmark.salary = new BigDecimal(0) ;
            long time = System.currentTimeMillis() ;
            sqlactionBenchmark.birthday = new java.sql.Date(time) ;
            count = 500 ;
            count2 = 5 ;
            count3 = 1000 ;
            rows = SqlactionBenchmarkSAO.DELETE_FROM_sqlaction_benchmark(conn) ;
            conn.commit();
            
            // Бенчмарк для вставки (INSERT)
            beginMillisSecondstamp = System.currentTimeMillis() ;
            for( i = 0 ; i < count ; i++ ) {
                sqlactionBenchmark.name = "Calvin" + i ;
                sqlactionBenchmark.nameCn = "Калвин" + i ;
                rows = SqlactionBenchmarkSAO.INSERT_INTO_sqlaction_benchmark(conn, sqlactionBenchmark) ;
                if(rows != 1) {
                    System.out.println("SqlactionBenchmarkSAO.INSERT_INTO_sqlaction_benchmark failed[" + rows + "]");
                    return;
                }
                if(i % 10 == 0) {
                    conn.commit();
                }
            }
            conn.commit();
            endMillisSecondstamp = System.currentTimeMillis() ;
            elapsedSecond = (endMillisSecondstamp - beginMillisSecondstamp) / 1000.0 ;
            System.out.println("Все операции INSERT выполнены, количество записей [" + count + "] затраченное время [" + elapsedSecond + "] секунд");
        }```java
            // Бенчмарк для обновления (UPDATE)
            beginMillisSecondstamp = System.currentTimeMillis();
            for (i = 0; i < count; i++) {
                rows = SqlactionBenchmarkSAO.UPDATE_sqlaction_benchmark_SET_salary_E_WHERE_name_E_(conn, new BigDecimal(i), "Calvin" + i);
                if (rows != 1) {
                    System.out.println("SqlactionBenchmarkSAO.UPDATE_sqlaction_benchmark_SET_salary_E_WHERE_name_E_ failed[" + rows + "]");
                    return;
                }
                if (i % 10 == 0) {
                    conn.commit();
                }
            }
            conn.commit();
```

```markdown
endMillisSecondstamp = System.currentTimeMillis();
elapsedSecond = (endMillisSecondstamp - beginMillisSecondstamp) / 1000.0;
System.out.println("Обновление всех записей SQL завершено, количество операций [" + count + "] затраченное время [" + elapsedSecond + "] сек");

// бенчмарк для SELECT ... WHERE ...
beginMillisSecondstamp = System.currentTimeMillis();
for (j = 0; j < count2; j++) {
    for (i = 0; i < count; i++) {
        sqlactionBenchmarkList = new LinkedList<SqlactionBenchmarkSAO>();
        rows = SqlactionBenchmarkSAO.SELECT_ALL_FROM_sqlaction_benchmark_WHERE_name_E_(conn, sqlactionBenchmarkList, "Calvin" + i);
        if (rows != 1) {
            System.out.println("SqlactionBenchmarkSAO.SELECT_ALL_FROM_sqlaction_benchmark_WHERE_name_E_ завершился с ошибкой [" + rows + "]");
            return;
        }
    }
}
endMillisSecondstamp = System.currentTimeMillis();
elapsedSecond = (endMillisSecondstamp - beginMillisSecondstamp) / 1000.0;
System.out.println("Выборка всех записей SQL завершена, количество операций2 [" + count2 + "] количество [" + count + "] затраченное время [" + elapsedSecond + "] сек");
``````java
// benchmark для SELECT
beginMillisSecondStamp = System.currentTimeMillis();
for (k = 0; k < count3; k++) {
    sqlactionBenchmarkList = new LinkedList<SqlactionBenchmarkSAO>();
    rows = SqlactionBenchmarkSAO.SELECT_ALL_FROM_sqlaction_benchmark(conn, sqlactionBenchmarkList);
    if (rows != count) {
        System.out.println("SqlactionBenchmarkSAO.SELECT_ALL_FROM_sqlaction_benchmark завершился с ошибкой [" + rows + "]");
        return;
    }
}
endMillisSecondStamp = System.currentTimeMillis();
elapsedSecond = (endMillisSecondStamp - beginMillisSecondStamp) / 1000.0;
System.out.println("Выборка всех записей SQL в список завершена, количество операций 3 [" + count3 + "] затраченное время [" + elapsedSecond + "] сек");
```

Здесь исправлены названия переменных `beginMillisSecondStamp` и `endMillisSecondStamp`, а также добавлено правильное название переменной `elapsedSecond`. Также исправлены пунктуация и грамматика в выводимых сообщениях.```java
//benchmark для DELETE
beginMillisSecondstamp = System.currentTimeMillis();
for (i = 0; i < count; i++) {
    rows = SqlactionBenchmarkSAO.DELETE_FROM_sqlaction_benchmark_WHERE_name_E_(conn, "Calvin" + i);
    if (rows != 1) {
        System.out.println("SqlactionBenchmarkSAO.DELETE_FROM_sqlaction_benchmark_WHERE_name_E_ завершился с ошибкой [" + rows + "]");
        return;
    }
    if (i % 10 == 0) {
        conn.commit();
    }
}
conn.commit();
endMillisSecondstamp = System.currentTimeMillis();
elpaseSecond = (endMillisSecondstamp - beginMillisSecondstamp) / 1000.0;
System.out.println("Удаление всех записей SQL завершено, количество операций [" + count + "] затраченное время [" + elpaseSecond + "] сек");
```

Примечание: В данном примере были использованы специфические названия методов и переменных, которые остаются без изменения согласно правилам перевода.```markdown
```java
try {
    // some code here
} catch (Exception e) {
    e.printStackTrace();
    try {
        conn.rollback();
    } catch (Exception e2) {
        e.printStackTrace();
        return;
    }
}
finally {
    try {
        conn.close();
    } catch (Exception e2) {
        e2.printStackTrace();
        return;
    }
}
return;
```
```## 6.2 Подготовка MyBatis

Ручное создание файла конфигурации соединения с базой данных `mybatis-config.xml`.
``````xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="cacheEnabled" value="false" />
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT" />
                <property name="username" value="calvin" />
                <property name="password" value="calvin" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mybatis-mapper.xml" />
    </mappers>
</configuration>
```Ручное создание файла конфигурации маппера `mybatis-mapper.xml`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAOMapper">
    <insert id="insertOne" parameterType="xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAO">
        INSERT INTO sqlaction_benchmark (name, name_cn, salary, birthday) VALUES(#{name}, #{name_cn}, #{salary}, #{birthday})
    </insert>
    <update id="updateOneByName" parameterType="xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAO">
        UPDATE sqlaction_benchmark SET salary = #{salary} WHERE name = #{name}
    </update>
    <select id="selectOneByName" parameterType="java.lang.String" resultType="xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAO" flushCache="true" useCache="false">
        SELECT * FROM sqlaction_benchmark WHERE name = #{name}
    </select>
    <select id="selectAll" resultType="xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAO" flushCache="true" useCache="false">
        SELECT * FROM sqlaction_benchmark
    </select>
    <delete id="deleteOneByName" parameterType="java.lang.String">
        DELETE FROM sqlaction_benchmark WHERE name = #{name}
    </delete>
    <delete id="deleteAll">
        DELETE FROM sqlaction_benchmark
    </delete>
</mapper>
```

Ручное создание класса-сущности таблицы базы данных `SqlactionBenchmarkSAO.java`

```java
package xyz.calvinwilliams.mybatis.benchmark;

import java.math.BigDecimal;
import java.sql.Date;

public class SqlactionBenchmarkSAO {
    
    int id; // Номер
    String name; // Английское имя
    String name_cn; // Китайское имя
    BigDecimal salary; // Заработная плата
    Date birthday; // День рождения
    
    int count___; // Определение для 'SELECT COUNT(*)'
}
```

Ручное создание Mapper интерфейса для базы данных `SqlactionBenchmarkSAOMapper.java`

```java
package xyz.calvinwilliams.mybatis.benchmark;

import java.util.List;

public interface SqlactionBenchmarkSAOMapper {
    
    void insertOne(SqlactionBenchmarkSAO sao);
    
    void updateOneByName(SqlactionBenchmarkSAO sao);
    
    SqlactionBenchmarkSAO selectOneByName(String name);
    
    List<SqlactionBenchmarkSAO> selectAll();
    
    void deleteOneByName(String name);
    
    void deleteAll();
}
``````markdown
в. публичный интерфейс `SqlactionBenchmarkSAOMapper` {
	public void insertOne(`SqlactionBenchmarkSAO` sqlactionBenchmark);
	public void updateOneByName(`SqlactionBenchmarkSAO` sqlactionBenchmark);
	public `SqlactionBenchmarkSAO` selectOneByName(строка `name`);
	public список<`SqlactionBenchmarkSAO`> selectAll();
	public void deleteOneByName(строка `name`);
	public void deleteAll();
}
в.
```

Перевод:

```markdown
в. публичный интерфейс `SqlactionBenchmarkSAOMapper` {
	public void insertOne(`SqlactionBenchmarkSAO` sqlactionBenchmark);
	public void updateOneByName(`SqlactionBenchmarkSAO` sqlactionBenchmark);
	public `SqlactionBenchmarkSAO` selectOneByName(строка `name`);
	public список<`SqlactionBenchmarkSAO`> selectAll();
	public void deleteOneByName(строка `name`);
	public void deleteAll();
}
в.
```
```markdown
## Ручное создание класса для тестирования производительности MyBatisBenchmarkCrud.java

```java
/*
 * sqlaction - Автоматическое генерирование кода объектов SQL действия на основе JDBC для Java
 * автор : calvin
 * эл. почта : calvinwilliams@163.com
 *
 * Смотрите файл LICENSE в корневой директории.
 */

пакет xyz.calvinwilliams.mybatis.benchmark;

в. импорт java.io.FileInputStream;
в. импорт java.math.BigDecimal;
в. импорт java.util.List;

в. импорт org.apache.ibatis.session.SqlSession;
в. импорт org.apache.ibatis.session.SqlSessionFactoryBuilder;

в. импорт xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAO;
в. импорт xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAOMapper;
```

### Исправленный код:

```java
package xyz.calvinwilliams.mybatis.benchmark;

import java.io.FileInputStream;
import java.math.BigDecimal;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAO;
import xyz.calvinwilliams.mybatis.benchmark.SqlactionBenchmarkSAOMapper;
```
```в. публичный класс MyBatisBenchmarkCrud {
```markdown
    public static void main(String[] args) {
        SqlSession                    session = null;
        SqlactionBenchmarkSAOMapper   mapper;
        List<SqlactionBenchmarkSAO>   sqlactionBenchmarkList;
        long                          beginMillisSecondstamp;
        long                          endMillisSecondstamp;
        double                       elapsedSecond;
        long                         i, j, k;
        long                         count, count2, count3;
        try {
            FileInputStream in = new FileInputStream("src/main/java/mybatis-config.xml");
            session = new SqlSessionFactoryBuilder().build(in).openSession();
            SqlactionBenchmarkSAO sqlactionBenchmark = new SqlactionBenchmarkSAO();
            sqlactionBenchmark.id = 1;
            sqlactionBenchmark.name = "Calvin";
            sqlactionBenchmark.name_cn = "Калвин";
            sqlactionBenchmark.salary = new BigDecimal(0);
            long time = System.currentTimeMillis();
            sqlactionBenchmark.birthday = new java.sql.Date(time);
            count = 500;
            count2 = 5;
            count3 = 1000;
            mapper = session.getMapper(SqlactionBenchmarkSAOMapper.class);
            mapper.deleteAll();
            session.commit();
            // benchmark for INSERT
            beginMillisSecondstamp = System.currentTimeMillis();
            for (i = 0; i < count; i++) {
                sqlactionBenchmark.name = "Calvin" + i;
                sqlactionBenchmark.name_cn = "Калвин" + i;
                mapper.insertOne(sqlactionBenchmark);
                if (i % 10 == 0) {
                    session.commit();
                }
            }
            session.commit();
            endMillisSecondstamp = System.currentTimeMillis();
            elapsedSecond = (endMillisSecondstamp - beginMillisSecondstamp) / 1000.0;
            System.out.println("Все операции mybatis INSERT завершены, количество [" + count + "] затраченное время [" + elapsedSecond + "] секунд");
            // benchmark for UPDATE
            beginMillisSecondstamp = System.currentTimeMillis();
            for (i = 0; i < count; i++) {
                sqlactionBenchmark.name = "Calvin" + i;
                sqlactionBenchmark.salary = new BigDecimal(i);
``````java
mapper.updateOneByName(sqlactionBenchmark);
if (i % 10 == 0) {
    session.commit();
}
}
session.commit();
endMillisSecondStamp = System.currentTimeMillis();
elapsedSeconds = (endMillisSecondStamp - beginMillisSecondStamp) / 1000.0;
System.out.println("Все операции mybatis UPDATE завершены, количество [" + count + "] затраченное время [" + elapsedSeconds + "] секунд");

// benchmark for SELECT ... WHERE ...
beginMillisSecondStamp = System.currentTimeMillis();
for (int j = 0; j < count2; j++) {
    for (int i = 0; i < count; i++) {
        sqlactionBenchmark = mapper.selectOneByName(sqlactionBenchmark.name);
    }
}

if (sqlactionBenchmark == null) {
    System.out.println("mapper.selectOneByName failed");
    return;
}

endMillisSecondStamp = System.currentTimeMillis();
elapsedSeconds = (endMillisSecondStamp - beginMillisSecondStamp) / 1000.0;
System.out.println("Все операции mybatis SELECT WHERE завершены, количество [" + count2 + "] количество [" + count + "] затраченное время [" + elapsedSeconds + "] секунд");

// benchmark for SELECT
beginMillisSecondStamp = System.currentTimeMillis();
for (int k = 0; k < count3; k++) {
    sqlactionBenchmarkList = mapper.selectAll();
    if (sqlactionBenchmarkList == null) {
        System.out.println("mapper.selectAll failed");
        return;
    }
}

endMillisSecondStamp = System.currentTimeMillis();
elapsedSeconds = (endMillisSecondStamp - beginMillisSecondStamp) / 1000.0;
System.out.println("Все операции mybatis SELECT to List завершены, количество [" + count3 + "] затраченное время [" + elapsedSeconds + "] секунд");

// benchmark for DELETE
beginMillisSecondStamp = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
    sqlactionBenchmark.name = "Calvin" + i;
    mapper.deleteOneByName(sqlactionBenchmark.name);
    if (i % 10 == 0) {
        session.commit();
    }
}

session.commit();
endMillisSecondStamp = System.currentTimeMillis();
elapsedSeconds = (endMillisSecondStamp - beginMillisSecondStamp) / 1000.0;
System.out.println("Все операции mybatis DELETE завершены, количество [" + count + "] затраченное время [" + elapsedSeconds + "] секунд");
} catch (Exception e) {
e.printStackTrace();
}
```

# 6. 3. Примеры тестовых случаевВставка 500 записей в таблицу (каждые 10 записей отправляются с подтверждением)
Обновление 500 записей в таблицу (каждые 10 записей отправляются с подтверждением)
Выбор одной записи из таблицы 500 * 5 раз
Выбор всех записей из таблицы 1000 раз
Удаление 500 записей из таблицы (каждые 10 записей отправляются с подтверждением)

## 6.4. Результаты тестирования

```sql
Все операции INSERT завершены, количество [500], затраченное время [4.742] секунды
Все операции UPDATE WHERE завершены, количество [500], затраченное время [5.912] секунды
Все операции SELECT WHERE завершены, количество [500], количество2 [5], затраченное время [0.985] секунды
Все операции SELECT в список завершены, количество [1000], количество3 [1000], затраченное время [1.172] секунды
Все операции DELETE WHERE завершены, количество [500], затраченное время [5.001] секунды

Все операции INSERT MyBatis завершены, количество [500], затраченное время [5.869] секунды
Все операции UPDATE WHERE MyBatis завершены, количество [500], затраченное время [6.921] секунды
Все операции SELECT WHERE MyBatis завершены, количество [500], количество2 [5], затраченное время [1.239] секунды
Все операции SELECT в список MyBatis завершены, количество [1000], количество3 [1000], затраченное время [1.792] секунды
Все операции DELETE WHERE MyBatis завершены, количество [500], затраченное время [5.382] секунды
```Все операции INSERT SQLAction завершены, количество [500], затраченное время [5,392] секунды
Все операции UPDATE WHERE SQLAction завершены, количество [500], затраченное время [5,821] секунды
Все операции SELECT WHERE SQLAction завершены, количество [5], количество [500], затраченное время [0,952] секунды
Все операции SELECT в список SQLAction завершены, количество [1000], затраченное время [1,15] секунды
Все операции DELETE WHERE SQLAction завершены, количество [500], затраченное время [5,509] секундыВсе операции INSERT MyBatis завершены, количество [500], затраченное время [6.066] секунды
Все операции UPDATE WHERE MyBatis завершены, количество [500], затраченное время [6.946] секунды
Все операции SELECT WHERE MyBatis завершены, количество [5], количество [500], затраченное время [1.183] секунды
Все операции SELECT в список MyBatis завершены, количество [1000], затраченное время [1.804] секунды
Все операции DELETE WHERE MyBatis завершены, количество [500], затраченное время [5.958] секунды

Все операции INSERT SQLAction завершены, количество [500], затраченное время [5.236] секунды
Все операции UPDATE WHERE SQLAction завершены, количество [500], затраченное время [5.84] секунды
Все операции SELECT WHERE SQLAction завершены, количество [5], количество [500], затраченное время [0.985] секунды
Все операции SELECT в список SQLAction завершены, количество [1000], затраченное время [1.222] секунды
Все операции DELETE WHERE SQLAction завершены, количество [500], затраченное время [4.91] секунды

Все операции INSERT MyBatis завершены, количество [500], затраченное время [5.448] секунды
Все операции UPDATE WHERE MyBatis завершены, количество [500], затраченное время [7.287] секунды
Все операции SELECT WHERE MyBatis завершены, количество [5], количество [500], затраченное время [1.149] секунды
Все операции SELECT в список MyBatis завершены, количество [1000], затраченное время [1.873] секунды
Все операции DELETE WHERE MyBatis завершены, количество [500], затраченное время [6.035] секунды
```![benchmark_INSERT.png](benchmark_INSERT.png)

![benchmark_UPDATE_WHERE.png](benchmark_UPDATE_WHERE.png)

![benchmark_SELECT_WHERE.png](benchmark_SELECT_WHERE.png)

![benchmark_SELECT_to_LIST.png](benchmark_SELECT_to_LIST.png)
![benchmark_DELETE_WHERE.png](benchmark_DELETE_WHERE.png)

**На основе приведенного выше графика производительности можно сделать вывод, что выполнение `sqlaction` происходит быстрее, чем выполнение `MyBatis`, примерно на 20%. Это указывает на то, что использование технологии `sqlaction` обеспечивает значительное преимущество в снижении задержек транзакций по сравнению с `MyBatis`.**

**Обобщая эффективность разработки и производительность при работе, мы можем сделать следующие выводы:*** По подготовке тестов, независимо от количества и размера конфигурационных и исходных кодовых файлов, объем работы для `sqlaction` составляет лишь половину от того, который требуется для `MyBatis`. Это позволяет быстрее начать бизнес-разработку, уменьшить нагрузку на разработчиков и освободить их мыслительные ресурсы, а также использовать более простую, прозрачную и управляемую технологию.
* Из концептуального подхода следует, что `sqlaction` автоматически генерирует ранее требуемые вручную коды между приложением и JDBC. В реальном времени его влияние минимально, а производительность эквивалента высокопроизводительному прямому JDBC. Теоретически невозможно найти более быструю библиотеку или инструмент для баз данных. `Sqlaction` автоматически генерирует методы Java для обработки SQL, что позволяет легко интегрироваться с другими надежными библиотеками и инструментами управления базами данных.

Исправленные ошибки:

* "эквивалента" заменено на "эквивалентна".
* "найду" заменено на "найти".# 7. Дальнейшая разработка

1. Триггер для запуска инструмента sqlaction через плагин Eclipse
1. Увеличение использования кэша
1. Дальнейшее улучшение поддержки Oracle.

# 8. О проекте

Добро пожаловать в `sqlaction`. Если вы столкнулись с проблемой во время использования, пожалуйста, сообщите мне об этом, спасибо ^_^

Исходный код доступен по адресам: [Open China](https://gitee.com/calvinwilliams/sqlaction), [GitHub](https://github.com/calvinwilliams/sqlaction).

Apache Maven
```xml
<dependency>
  <groupId>xyz.calvinwilliams</groupId>
  <artifactId>sqlaction</artifactId>
  <version>0.2.9.0</version>
</dependency>
```

# 9. Об авторе

Ли Хуа, правая рука C, левая рука Java, создатель маленьких до больших систем, таких как платформы торговли и middleware, с опытом работы в распределённых системах и контейнерной технологии. В настоящее время работает в одном из городских банков над основной инфраструктурой.

Почта для связи со мной: [NetEase](mailto:calvinwilliams@163.com), [Gmail](mailto:calvinwilliams.c@gmail.com).

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/calvinwilliams-sqlaction.git
git@api.gitlife.ru:oschina-mirror/calvinwilliams-sqlaction.git
oschina-mirror
calvinwilliams-sqlaction
calvinwilliams-sqlaction
release