Я хочу создать строку SQL для обработки базы данных (обновления, удаления, вставки, выборки, что-то вроде этого) - вместо ужасного метода строки concat с использованием миллионов "+" и кавычек, которые в лучшем случае нечитаемы - должен быть лучший способ.
Я думал об использовании MessageFormat, но его предполагалось использовать для пользовательских сообщений, хотя я думаю, что он выполнил бы разумную работу, но я думаю, что должно быть что-то более согласованное с операциями типа SQL в java-sql-библиотеках.
Будет ли 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. См
(Отказ от ответственности: я работаю в компании за 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:
Для большинства основных поставщиков баз данных существуют реализации переводчика, поэтому вы должны легко найти все, что вам нужно.
Я бы посмотрел Spring JDBC. Я использую его всякий раз, когда мне нужно программно выполнять SQL. Пример:
int countOfActorsNamedJoe
= jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
Это действительно отлично подходит для любого исполнения sql, особенно для запросов; это поможет вам отображать объекты результатов в объекты, не добавляя сложность полного ORM.
Я обычно использую Spring Именованные параметры JDBC, поэтому могу написать стандартную строку типа "select * from blah where colX = ': someValue'"; Я думаю, что это довольно читаемый.
Альтернативой будет предоставление строки в отдельном файле .sql и чтение содержимого с использованием утилиты.
О, также стоит взглянуть на Squill: https://squill.dev.java.net/docs/tutorial.html
Во-вторых, рекомендации по использованию ORM, например Hibernate. Однако есть ситуации, когда это не работает, поэтому я воспользуюсь этой возможностью, чтобы обсудить некоторые вещи, которые я помог написать: SqlBuilder - это java-библиотека для динамического создания sql-операторов с использованием стиля "строитель". он довольно мощный и довольно гибкий.
Почему вы хотите сгенерировать все sql вручную? Вы рассматривали ORM как Hibernate. В зависимости от вашего проекта он, вероятно, сделает не менее 95% того, что вам нужно, сделайте это более чистым способом, чем исходный SQL, и если вам нужно получить последний бит производительности, вы можете создать SQL-запросы, которые необходимо настроить вручную.
Вы также можете посмотреть MyBatis (www.mybatis.org). Это помогает вам писать инструкции SQL за пределами вашего java-кода и отображать результаты sql в ваши java-объекты, между прочим.
Вы можете прочитать его из файла XML. Его легко поддерживать и работать. Есть стандартные анализаторы STaX, DOM, SAX, доступные там, чтобы сделать несколько строк кода в java.
У вас может быть семантическая информация с атрибутами тега, чтобы помочь сделать больше с SQL. Это может быть имя метода или тип запроса или все, что помогает вам меньше кода.
Вы можете поместить xml за пределы банки и легко сохранить его. Такие же преимущества, как и файл свойств.
XML расширяемый и легко конвертируемый в другие форматы.
Metamug использует xml для настройки файлов ресурсов REST с помощью 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 присутствует и установлено в "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 в настоящий момент, и весь код принадлежит крупной корпорации. Когда-нибудь я смогу принять мои идеи и создать новую инфраструктуру с открытым исходным кодом, но я надеюсь, что один из существующих крупных игроков возьмет на себя эту задачу.
Google предоставляет библиотеку, называемую Room Persitence Library, которая обеспечивает очень чистый способ написания 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 в файл свойств и затем прочитаете это, вы можете сохранить строки SQL в текстовом файле.
Это не решает проблемы типа SQL, но, по крайней мере, значительно облегчает копирование и вставку из TOAD или sqlplus.
Как вы получаете конкатенацию строк, за исключением длинных строк SQL в PreparedStatements (которые вы можете легко предоставить в текстовом файле и загружать как ресурс в любом случае), что вы разбиваете несколько строк?
Вы не создаете строки SQL напрямую? Это самый большой нет-нет в программировании. Пожалуйста, используйте PreparedStatements и поставьте данные как параметры. Это значительно сокращает вероятность внедрения SQL Injection.
InputStream
в операторif (props == null)
чтобы не создавать его, когда он не нужен.