Скажем, у меня есть.SQL файл, который потенциально может содержать оператор ANY типа, будь то CREATE, UPDATE, DELETE или даже весь дамп базы данных. Идея состоит в том, чтобы проанализировать содержимое этого файла, чтобы операторы отправлялись в двигатель отдельно, а не выполняли весь фрагмент.
Сначала я попытался использовать регулярные выражения и разделился ;
, но проблема в том, что он ломается, когда он сталкивается с процедурой или функцией из-за синтаксиса. Например...
DELIMITER ;;
CREATE DEFINER='root'@'localhost' FUNCTION 'calcCost'(cost FLOAT, price FLOAT) RETURNS decimal(9,2)
BEGIN
DECLARE profit DECIMAL(9,2);
SET profit = price-cost;
RETURN profit;
END ;;
DELIMITER ;
Разделение на ;
здесь это преобразует это в 8 разных утверждений, тогда как мне нужно, чтобы он оставался одним. Кстати, символ, используемый для ключевого слова DELIMITER
может измениться.
Например:
DELIMITER $$
CREATE DEFINER='root'@'localhost' FUNCTION 'calcCost'(cost FLOAT, price FLOAT) RETURNS decimal(9,2)
BEGIN
DECLARE profit DECIMAL(9,2);
SET profit = price-cost;
RETURN profit;
END $$
DELIMITER ;
Будет ли это выполнимо с помощью регулярных выражений, и если да, то как мне это сделать? Что выражение регулярного выражения? Явный тег Java, потому что я ожидаю ответы, основанные на Java regex engine и утилитах.
Изменение: содержимое файла SQL неизвестно мне, пока пользователь не загрузит его в мою программу. Я не могу вручную добавить что-либо к нему, прежде чем моя программа загрузит его.
Можно ли это сделать, используя регулярные выражения?
Нет.
Как мне это сделать?
Как вы заметили себя, разделитель изменчив. Поэтому вам нужно найти команду DELIMITER
.
Кроме того, вы можете полагаться на файлы SQL, подобные этому, чтобы поместить только один оператор в строку, т.е. Вам нужно только искать разделитель в конце строк.
Так:
delimiter = ";"
, и создать пустой буфер (StringBuilder
)DELIMITER
: delimiter
continue
readLine()
удаляет терминатор строкиAlso, you can rely of SQL files like that to only put one statement per line,
. Означает ли это, что ваш метод не будет работать для операторов, которые занимают более одной строки? Например: CREATE TABLE
и большинство CREATE
s? Потому что файлы, которые передаются в мою программу, должны разрешать оператор CREATE TABLE
который занимает несколько строк. (Большинство дамп-приложений БД, таких как mysqldump
форматируют файлы таким образом), и моя программа должна быть совместима с ними.
Это работающая (насколько я проверял) реализация ответа Андреаса. Он делает именно то, что я хотел. Это должно быть совместимо с типом и форматом скриптов, создаваемых большинством баз данных.
try (BufferedReader reader = new BufferedReader(new FileReader(new File(System.getProperty("user.home") + "/Desktop/myscript.sql")))) {
String line;
String delimiter = ";";
StringBuilder buffer = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (buffer.length() == 0 && line.startsWith("DELIMITER")) {
delimiter = line.split(" ")[1];
continue;
}
if (line.startsWith("--") || line.isEmpty()) {
continue;
}
buffer.append(line.replaceFirst("\\h+$", "")).append("\n");
if (line.endsWith(delimiter)) {
String statement = buffer.toString().replace(delimiter, "");
System.out.println("{\n" + statement + "}"); //print the statement to console for debugging purposes
//TODO execute the statement
buffer.setLength(0);
}
}
} catch (IOException e) {
//TODO handle IOException
}
buffer.append(line.trim()).append("\n");
быть более читабельным?
Я делаю что-то подобное, чтобы читать SQL файлы из файла. (Это может быть или не быть сложным с регулярным выражением, но мое не очень глубокое знание регулярного выражения заставило меня сделать это так)
Резюме:
Создайте Map<String, String>
которая хранит имя запроса и фактический SQL-запрос.
Заполнение карты заполняется, когда я загружаю этот DAO, чтобы все запросы, связанные с этим DAO, были на карте.
Пример кода DAO:
private static final Map<String, String> SQL_MAP;
private static final String GET_PERSON_USING_ID;
static
{
try
{
SQL_MAP = Util.readConfigureMap(<MyCurrentClass>.class, "<sql_file>");
}
catch (IOException iE)
{
throw new RuntimeException("Runtime Exception: ", iE);
}
GET_PERSON_USING_ID = SQL_MAP.get("get_person_using_id");
}
Пример кода Util:
public static final String
CONFIG_KEY_START = "---[",
CONFIG_KEY_END = "]";
public static Map<String, String> readToMap(Class iClass, String iFileName)
throws IOException
{
final InputStream vResourceAsStream = iClass.getResourceAsStream(iFileName);
try (Scanner vScanner = new Scanner(vResourceAsStream))
{
Map<String, String>
vMap = new LinkedHashMap<>();
String
vCurrentKey = null;
StringBuilder
vValueBuffer = null;
boolean
vSeeFirst = false;
while (vScanner.hasNextLine())
{
String
vLine = vScanner.nextLine();
if(vLine == null)
{
continue;
}
vLine = vLine.trim();
if(vLine.isEmpty())
{
continue;
}
if(vLine.startsWith(CONFIG_KEY_START))
{
if(!vSeeFirst)
{
vSeeFirst = true;
}
if(vCurrentKey != null)
{
vMap.put(vCurrentKey, vValueBuffer == null ? null : vValueBuffer.toString());
vValueBuffer = null;
}
vCurrentKey = vLine.substring(CONFIG_KEY_START.length());
if(vCurrentKey.endsWith(CONFIG_KEY_END))
{
vCurrentKey = vCurrentKey.substring(0, vCurrentKey.length() - CONFIG_KEY_END.length());
}
continue;
}
if(!vSeeFirst)
{
continue;
}
if(vValueBuffer == null)
{
vValueBuffer = new StringBuilder();
}
else
{
vValueBuffer.append('\n');
}
vValueBuffer.append(vLine);
}
if(vCurrentKey != null)
{
vMap.put(vCurrentKey, vValueBuffer == null ? null : vValueBuffer.toString());
}
return vMap;
}
}
Пример файла sql
---[get_person_using_id]
select a.name from person a where id = ?