Самый чистый способ создания строки SQL в Java

Я хочу построить строку SQL для обработки базы данных (обновления, удаления, вставки, выборки, что-то вроде этого) – вместо ужасного метода строки concat с использованием миллионов «+» и кавычек, которые в лучшем случае нечитаемы – там должен быть лучшим способом.

Я думал об использовании MessageFormat, но он должен использоваться для пользовательских сообщений, хотя я думаю, что он выполнил бы разумную работу, но я думаю, что должно быть что-то более согласованное с операциями типа SQL в javasql-библиотеках.

Будет ли Groovy быть хорошим?

Прежде всего рассмотрим использование параметров запроса в подготовленных операторах:

PreparedStatement stm = c.prepareStatement("UPDATE user_table SET name=? WHERE id=?"); stm.setString(1, "the name"); stm.setInt(2, 345); stm.executeUpdate(); 

Другое, что можно сделать, это сохранить все запросы в файле свойств. Например, в файле queries.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 используйте 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. Видеть

http://www.jooq.org

(Отказ от ответственности: я работаю в компании за jOOQ)

Одной технологией, которую вы должны рассмотреть, является 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:

  • полностью абстрагирует слой JDBC – программистам нужно только подумать о Java и SQL
  • переводчик может быть сделан для проверки ваших запросов на синтаксис и т. д. в отношении базы данных во время компиляции
  • возможность напрямую связывать переменные Java в запросах с использованием префикса «:»

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

Мне интересно, если вы после чего-то вроде Squiggle . Также очень полезно jDBI . Тем не менее, это не поможет вам.

Я бы посмотрел на Spring JDBC . Я использую его всякий раз, когда мне нужно программно выполнять SQL. Пример:

 int countOfActorsNamedJoe = jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"}); 

Это действительно отлично подходит для любого исполнения sql, особенно для запросов; это поможет вам отображать объекты результатов в объекты, не добавляя сложность полного ORM.

Я склонен использовать JDBC-параметры Spring, поэтому я могу написать стандартную строку типа «select * from blah where colX = ': someValue'"; Я думаю, это довольно читаемо.

Альтернативой было бы предоставление строки в отдельном файле .sql и чтение содержимого с использованием утилиты.

О, также стоит взглянуть на Squill: https://squill.dev.java.net/docs/tutorial.html

Почему вы хотите создать все sql вручную? Вы рассматривали ORM как Hibernate. В зависимости от вашего проекта он, вероятно, сделает не менее 95% того, что вам нужно, сделайте это более чистым способом, чем исходный SQL, и если вам нужно получить последний бит производительности, вы можете создать SQL-запросы, которые необходимо настроить вручную.

Во-вторых, рекомендации по использованию ORM, например, Hibernate. Тем не менее, есть ситуации, когда это не работает, поэтому я воспользуюсь этой возможностью, чтобы обсудить некоторые вещи, которые я помог написать: SqlBuilder – это Java-библиотека для динамического создания SQL-операторов с использованием стиля «строитель». он довольно мощный и достаточно гибкий.

Вы также можете посмотреть MyBatis ( http://www.mybatis.org ). Это помогает вам писать инструкции SQL вне вашего java-кода и сопоставляет результаты sql в ваших java-объектах, среди прочего.

Я работаю над приложением 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 присутствует и установлено в «true», биты SQL в кавычках будут добавлены в сгенерированный запрос для необязательного поля отображения. поле. Доступны еще несколько сопоставлений типов параметров, включая необязательные сегменты 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 в настоящий момент, и весь код принадлежит крупной корпорации. Когда-нибудь я смогу принять мои идеи и создать новую среду с открытым исходным кодом, но я надеюсь, что один из существующих крупных игроков возьмет на себя эту задачу.

Прочтите XML-файл.

Вы можете прочитать его из XML-файла. Его легко поддерживать и работать. Есть стандартные анализаторы STaX, DOM, SAX, доступные там, чтобы сделать несколько строк кода в java.

Делать больше с атрибутами

Вы можете иметь некоторую семантическую информацию с атрибутами в теге, чтобы помочь сделать больше с SQL. Это может быть имя метода или тип запроса или все, что помогает вам меньше кода.

Maintaince

Вы можете поместить xml за пределы банки и легко сохранить его. Такие же преимущества, как и файл свойств.

преобразование

XML расширяемый и легко конвертируемый в другие форматы.

Случай использования

Metamug использует xml для настройки файлов ресурсов REST с помощью sql.

Если вы поместите строки SQL в файл свойств и затем прочитаете это, вы можете сохранить строки SQL в текстовом файле.

Это не решает проблемы типа SQL, но, по крайней мере, это значительно облегчает копирование и вставку из TOAD или sqlplus.

Google предоставляет библиотеку, именуемую библиотекой Room Persitence, которая обеспечивает очень чистый способ написания sql. Ниже приведен короткий фрагмент кода с официального сайта:

 @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.

Как вы получаете конкатенацию строк, помимо длинных строк SQL в PreparedStatements (которые вы можете легко предоставить в текстовом файле и загружать как ресурс в любом случае), что вы разбиваете несколько строк?

Вы не создаете строки SQL напрямую? Это самое большое нет-нет в программировании. Пожалуйста, используйте PreparedStatements и поставьте данные как параметры. Это значительно снижает вероятность внедрения SQL Injection.