Я хочу создать строку SQL для манипуляций с базой данных (обновления, удаления, вставки, выборки и тому подобное) - вместо ужасного метода конкатенации строк с использованием миллионов "+" и кавычек, которые в лучшем случае нечитабельны - там должен быть лучший способ.
Я действительно думал об использовании MessageFormat - но его предполагалось использовать для пользовательских сообщений, хотя я думаю, что это будет разумная работа, - но я думаю, что в библиотеках java sql должно быть что-то более согласованное с операциями типа SQL.
Подойдет ли Groovy?




Я предпочитаю использовать именованные параметры JDBC Spring, чтобы написать стандартную строку вроде «select * from blah where colX = ': someValue'»; Думаю, это довольно удобно.
Альтернативой может быть предоставление строки в отдельном файле .sql и считывание содержимого с помощью служебного метода.
О, также стоит взглянуть на Squill: https://squill.dev.java.net/docs/tutorial.html
Не совсем. Вы можете использовать любой SqlParameterSource с именованными параметрами JDBC. Моим потребностям соответствовало использование MapSqlParameterSource, а не различных компонентов. В любом случае это хорошее решение. Однако RowMappers имеет дело с другой стороной головоломки SQL: превращением наборов результатов в объекты.
Если вы поместите строки SQL в файл свойств, а затем прочитаете его, вы можете сохранить строки SQL в текстовом файле.
Это не решает проблемы типа SQL, но, по крайней мере, значительно упрощает копирование и вставку из TOAD или sqlplus.
Почему вы хотите сгенерировать все sql вручную? Смотрели ли вы на ORM, например Hibernate? В зависимости от вашего проекта он, вероятно, будет делать не менее 95% того, что вам нужно, делайте это более чистым способом, чем необработанный SQL, и если вам нужно получить последний бит производительности, вы можете создать SQL-запросы, требующие ручной настройки.
Прежде всего рассмотрите возможность использования параметров запроса в подготовленных операторах:
PreparedStatement stm = c.prepareStatement("UPDATE user_table SET name=? WHERE id=?");
stm.setString(1, "the name");
stm.setInt(2, 345);
stm.executeUpdate();
Еще одна вещь, которую можно сделать, - это сохранить все запросы в файле свойств. Например в файле query.properties можно разместить указанный выше запрос:
update_query=UPDATE user_table SET name=? WHERE id=?
Затем с помощью простого служебного класса:
public class Queries {
private static final String propFileName = "queries.properties";
private static Properties props;
public static Properties getQueries() throws SQLException {
InputStream is =
Queries.class.getResourceAsStream("/" + propFileName);
if (is == null){
throw new SQLException("Unable to load property file: " + propFileName);
}
//singleton
if (props == null){
props = new Properties();
try {
props.load(is);
} catch (IOException e) {
throw new SQLException("Unable to load property file: " + propFileName + "\n" + e.getMessage());
}
}
return props;
}
public static String getQuery(String query) throws SQLException{
return getQueries().getProperty(query);
}
}
вы можете использовать свои запросы следующим образом:
PreparedStatement stm = c.prepareStatement(Queries.getQuery("update_query"));
Это довольно простое решение, но оно работает хорошо.
Я предпочитаю использовать чистый SQL-конструктор вроде этого: mentabean.soliveirajr.com
Могу я предложить вам поместить InputStream внутри оператора if (props == null), чтобы вы не создавали его экземпляр, когда он не нужен.
Мне интересно, если вам нужно что-то вроде Покоробление. Также кое-что очень полезное - это jDBI. Однако это не поможет вам с вопросами.
Я бы посмотрел на Spring JDBC. Я использую его всякий раз, когда мне нужно выполнить SQL программно. Пример:
int countOfActorsNamedJoe
= jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
Это действительно здорово для любого вида выполнения sql, особенно для запросов; это поможет вам сопоставить наборы результатов с объектами, не добавляя сложности полной ORM.
как я могу получить реальный выполненный запрос sql? Я хочу это записать.
Одной из технологий, которую вы должны рассмотреть, является SQLJ - способ встраивать операторы SQL непосредственно в Java. В качестве простого примера в файле TestQueries.sqlj может быть следующее:
public class TestQueries
{
public String getUsername(int id)
{
String username;
#sql
{
select username into :username
from users
where pkey = :id
};
return username;
}
}
Существует дополнительный этап предварительной компиляции, который берет ваши файлы .sqlj и переводит их в чистую Java - короче говоря, он ищет специальные блоки, разделенные символом
#sql
{
...
}
и превращает их в вызовы JDBC. Использование SQLJ дает несколько ключевых преимуществ:
Существуют реализации переводчика для большинства основных поставщиков баз данных, поэтому вы сможете легко найти все, что вам нужно.
Согласно Википедии, этот сейчас уже устарел.
На момент написания (январь 2016 г.) SQLJ упоминается в в Википедии как «устаревший» без каких-либо ссылок. Он был официально заброшен? Если да, я прикреплю предупреждение вверху этого ответа.
NB Технология все еще поддерживается, например, в последняя версия Oracle, 12c. Я признаю, что это не самый современный стандарт, но он по-прежнему работает и имеет некоторые преимущества (например, проверка запросов к базе данных во время компиляции), которые недоступны в других системах.
Как получить конкатенацию строк, помимо длинных строк SQL в PreparedStatements (которые вы можете легко предоставить в текстовом файле и в любом случае загрузить как ресурс), которые вы разбиваете на несколько строк?
Вы не создаете строки SQL напрямую, не так ли? Это самый большой запрет в программировании. Используйте PreparedStatements и укажите данные в качестве параметров. Это значительно снижает вероятность внедрения SQL-кода.
Но если вы не открываете доступ к веб-странице, актуален ли SQL-инъекция?
SQL-инъекция всегда актуальна, потому что это может произойти случайно или намеренно.
@Vidar - возможно, вы не открываете веб-страницу общедоступному сейчас, но даже код, который «всегда» будет внутренним, часто в конечном итоге получает какое-то внешнее воздействие в какой-то момент в дальнейшем. И быстрее и безопаснее делать это правильно с первого раза, чем потом проверять всю кодовую базу на предмет проблем ...
Даже PreparedStatement нужно создавать из String, не так ли?
Да, но создавать PreparedStatement из String безопасно, если вы создаете безопасный PreparedStatement. Вероятно, вам следует написать класс PreparedStatementBuilder для их генерации, чтобы скрыть беспорядок конкатенации вещей.
Этот статья был бы полезен, чтобы понять, как справиться с проблемой «SQL-инъекции».
Я поддерживаю рекомендации по использованию ORM, такого как Hibernate. Однако, безусловно, бывают ситуации, когда это не работает, поэтому я воспользуюсь этой возможностью, чтобы рассказать о некоторых материалах, которые я помог написать: SqlBuilder - это java-библиотека для динамического создания операторов sql с использованием стиля «построителя». он довольно мощный и довольно гибкий.
Я работал над приложением Java-сервлетов, которое должно создавать очень динамические операторы SQL для специальных отчетов. Основная функция приложения - передать кучу именованных параметров HTTP-запроса в предварительно закодированный запрос и создать красиво отформатированную таблицу вывода. Я использовал Spring MVC и структуру внедрения зависимостей для хранения всех моих SQL-запросов в файлах XML и загрузки их в приложение для создания отчетов вместе с информацией о форматировании таблиц. В конце концов, требования к отчетности стали более сложными, чем возможности существующих фреймворков сопоставления параметров, и мне пришлось написать свои собственные. Это было интересное упражнение в разработке, и в результате была получена структура для отображения параметров, гораздо более надежная, чем все, что я мог найти.
Новые сопоставления параметров выглядели так:
select app.name as "App",
${optional(" app.owner as "Owner", "):showOwner}
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = ${integer(0,50):serverId}
and app.id in ${integerList(50):appId}
group by app.name, ${optional(" app.owner, "):showOwner} sv.name
order by app.name, sv.name
Прелесть полученного фреймворка заключалась в том, что он мог обрабатывать параметры HTTP-запроса непосредственно в запросе с надлежащей проверкой типов и проверкой ограничений. Никаких дополнительных сопоставлений для проверки ввода не требуется. В приведенном выше примере запроса параметр с именем serverId будет проверяться, чтобы убедиться, что он может быть приведен к целому числу и находится в диапазоне от 0 до 50. Параметр appId будет обрабатываться как массив целых чисел с ограничением длины 50. Если поле showOwner присутствует и имеет значение «истина», биты SQL в кавычках будут добавлены к сгенерированному запросу для необязательного поля. сопоставления. field Доступно еще несколько сопоставлений типов параметров, включая необязательные сегменты SQL с дополнительными сопоставлениями параметров. Он позволяет реализовать настолько сложное отображение запросов, насколько это может придумать разработчик. Он даже имеет элементы управления в конфигурации отчета, чтобы определить, будет ли данный запрос иметь окончательные сопоставления с помощью PreparedStatement или просто запускаться как предварительно созданный запрос.
Для примеров значений запроса Http:
showOwner: true
serverId: 20
appId: 1,2,3,5,7,11,13
Это даст следующий SQL:
select app.name as "App",
app.owner as "Owner",
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = 20
and app.id in (1,2,3,5,7,11,13)
group by app.name, app.owner, sv.name
order by app.name, sv.name
Я действительно думаю, что Spring или Hibernate или одна из этих структур должны предлагать более надежный механизм сопоставления, который проверяет типы, позволяет использовать сложные типы данных, такие как массивы и другие подобные функции. Я написал свой движок исключительно для своих целей, он не совсем читается для общего выпуска. На данный момент он работает только с запросами Oracle, а весь код принадлежит большой корпорации. Когда-нибудь я воспользуюсь своими идеями и построю новый фреймворк с открытым исходным кодом, но я надеюсь, что один из существующих крупных игроков примет вызов.
Для произвольного SQL используйте jOOQ. jOOQ в настоящее время поддерживает SELECT, INSERT, UPDATE, DELETE, TRUNCATE и MERGE. Вы можете создать SQL так:
String sql1 = DSL.using(SQLDialect.MYSQL)
.select(A, B, C)
.from(MY_TABLE)
.where(A.equal(5))
.and(B.greaterThan(8))
.getSQL();
String sql2 = DSL.using(SQLDialect.MYSQL)
.insertInto(MY_TABLE)
.values(A, 1)
.values(B, 2)
.getSQL();
String sql3 = DSL.using(SQLDialect.MYSQL)
.update(MY_TABLE)
.set(A, 1)
.set(B, 2)
.where(C.greaterThan(5))
.getSQL();
Вместо получения строки SQL вы также можете просто выполнить ее, используя jOOQ. Видеть
(Отказ от ответственности: я работаю в компании, стоящей за jOOQ)
не будет ли это во многих случаях плохим решением, поскольку вы не можете позволить dbms проанализировать инструкцию заранее с разными значениями для «5», «8» и т. д.? Я предполагаю, что выполнение с помощью jooq решит это?
@Vegard: у вас есть полный контроль над тем, как jOOQ должен отображать значения привязки в своем выводе SQL: jooq.org/doc/3.1/manual/sql-building/bind-values. Другими словами, вы можете выбрать, отображать ли "?" или встроить значения привязки.
да, но что касается чистых способов сборки sql, то в моих глазах это был бы немного беспорядочный код, если бы вы не использовали JOOQ для выполнения. в этом примере вы устанавливаете A на 1, B на 2 и т. д., но вам нужно сделать это еще раз, когда вы выполняете, если вы не выполняете с JOOQ.
@Vegard: ничто не мешает вам передать переменную в jOOQ API и пересобрать инструкцию SQL. Кроме того, вы можете извлекать значения привязки в их порядке, используя jooq.org/javadoc/latest/org/jooq/Query.html#getBindValues (), или именованные значения привязки по их именам, используя jooq.org/javadoc/latest/org/jooq/Query.html#getParams (). Мой ответ просто содержит очень упрощенный пример ... Я не уверен, что это отвечает вашим опасениям?
у вас есть очень хороший момент: использование этих методов должно привести к более чистому коду, чем то, о чем я изначально думал. Что мне еще нравится в jOOQ, так это возможность писать SQL в некотором роде.
Это дорогостоящее решение.
Не забудьте добавить заявление об отказе от ответственности, в котором говорится, что вы генеральный директор компании, стоящей за jOOQ. ;)
@Stephan: Действительно, спасибо за подсказку. Я задним числом добавил этот отказ от ответственности к большинству ответов, но есть еще 1-2, которые еще не были изменены.
Вы также можете посмотреть MyBatis (www.mybatis.org). Он помогает вам писать операторы SQL вне вашего Java-кода и, помимо прочего, сопоставляет результаты sql с вашими Java-объектами.
Вы можете прочитать его из файла XML. Его легко обслуживать и работать с ним. Существуют стандартные парсеры STaX, DOM, SAX, позволяющие сделать несколько строк кода на java.
У вас может быть некоторая семантическая информация с атрибутами в теге, чтобы помочь вам делать больше с SQL. Это может быть имя метода или тип запроса или что-то еще, что помогает вам меньше кодировать.
Вы можете поместить xml вне банки и легко поддерживать его. Те же преимущества, что и у файла свойств.
XML является расширяемым и легко конвертируемым в другие форматы.
Metamug использует xml для настройки файлов ресурсов REST с помощью sql.
Вы можете использовать yaml или json, если они вам нравятся. Они лучше, чем хранить в обычном файле свойств.
Вопрос в том, как СОЗДАТЬ SQL. Для создания SQL, если вам нужно использовать XML, Parser, Validation и т. д., Это является чрезмерной нагрузкой. Большинство ранних попыток построения SQL с использованием XML были отклонены в пользу Annotation. принятый ответ от Петр Кочанский - это простой и элегантный и, по сути, решает проблему и обслуживается. ПРИМЕЧАНИЕ:НЕТ альтернативного способа поддерживать лучший SQL на другом языке.
Я удалил свой предыдущий комментарий I don't see a reason to make use of XML. , так как не мог его редактировать.
Google предоставляет библиотеку под названием Комната Persitence Library, которая обеспечивает очень чистый способ написания SQL для приложений Android, в основном уровень абстракции над лежащим в основе База данных SQLite. Bellow - это короткий фрагмент кода с официального сайта:
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
В официальных документах библиотеки есть больше примеров и лучшая документация.
Также есть MentaBean, который является Java ORM. Он имеет приятные функции и кажется довольно простым способом написания SQL.
Согласно Документация по помещению: Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Итак, это не общая библиотека ORM для СУБД. В первую очередь он предназначен для приложений Android.
Я полагаю, вы имеете в виду, что используете BeanPropertySqlParameterSource? Я почти согласен с вами, класс, который я только что упомянул, хорош при использовании строго bean-компонентов, но в противном случае я бы рекомендовал использовать настраиваемый ParameterizedRowMapper для создания объектов.