Java - сохранение SQL-операторов во внешнем файле

Я ищу библиотеку Java/framework/метод хранения операторов SQL во внешнем файле. Команда поддержки (включая администраторов баз данных) должна иметь возможность изменять (слегка) инструкцию, чтобы синхронизировать их в случае изменения схемы базы данных или для целей настройки.

Вот требования:

  • Файл должен быть доступен для чтения с Java приложение, но также должно быть доступно для редактирования группой поддержки без необходимости модных редакторов
  • В идеале файл должен быть простым текстовый формат, но XML тоже ОК.
  • Разрешить DML, а также заявления DDL для сохранения/восстановления
  • Новые операторы могут быть добавлены на более позднем этапе (приложение достаточно гибко, чтобы их подбирать и выполнять)
  • Заявления могут быть сгруппированы (и выполнены в виде группы приложением)
  • Заявления должны разрешать параметры

Примечания:

  • После извлечения заявления выполняется с помощью Spring s JDBCTemplate
  • Hibernate или Spring s контейнер IOC не будет использоваться

До сих пор мне удалось найти следующие библиотеки Java, которые используют внешние файлы для хранения операторов SQL. Однако меня интересует скорее хранилище, чем библиотека, которая скрывает все сложности JDBC.

  • Библиотека Axamol SQL

    Пример содержимого файла:

    <s:query name="get_emp">
      <s:param name="name" type="string"/>
      <s:sql databases="oracle">
        select    *
        from      scott.emp
                  join scott.dept on (emp.deptno = dept.deptno)
        where     emp.ename = <s:bind param="name"/>
      </s:sql>
    </s:query>
    
  • iBATIS

    Пример содержимого файла:

    <sqlMap namespace="Contact"">
        <typeAlias alias="contact"
            type="com.sample.contact.Contact"/">
        <select id="getContact"
            parameterClass="int" resultClass="contact"">
                select CONTACTID as contactId,
                       FIRSTNAME as firstName,
                       LASTNAME as lastName from
                       ADMINISTRATOR.CONTACT where CONTACTID = #id#
        </select>
    </sqlMap>
    <insert id="insertContact" parameterClass="contact">
    INSERT INTO ADMINISTRATOR.CONTACT( CONTACTID,FIRSTNAME,LASTNAME)
            VALUES(#contactId#,#firstName#,#lastName#);
     </insert>
    <update id="updateContact" parameterClass="contact">
    update ADMINISTRATOR.CONTACT SET
    FIRSTNAME=#firstName# ,
    LASTNAME=#lastName#
    where contactid=#contactId#
    </update>
    <delete id="deleteContact" parameterClass="int">
    DELETE FROM ADMINISTRATOR.CONTACT WHERE CONTACTID=#contactId#
    </delete>
    
  • WEB4J

    -- This is a comment 
     ADD_MESSAGE   {
     INSERT INTO MyMessage -- another comment
      (LoginName, Body, CreationDate)
      -- another comment
      VALUES (?,?,?)
     }
    
    -- Example of referring to a constant defined above.
    FETCH_RECENT_MESSAGES {
     SELECT 
     LoginName, Body, CreationDate 
     FROM MyMessage 
     ORDER BY Id DESC LIMIT ${num_messages_to_view}
    }
    

Кто-нибудь может рекомендовать решение, которое проверено и проверено?

Ответ 1

Просто создайте простой файл свойств Java с парами "ключ-значение", подобный этому:

users.select.all = select * from user

Объявите приватное поле типа "Свойства" в вашем классе DAO и введите его с помощью конфигурации Spring, которая будет считывать значения из файла.

UPDATE: если вы хотите поддерживать SQL-выражения в нескольких строках, используйте это обозначение:

users.select.all.0 = select *
users.select.all.1 = from   user

Ответ 2

Если вы должны это сделать, вы должны посмотреть проект MyBatis. Я не использовал его, но слышал, что он рекомендовал несколько раз.

Разделение SQL и Java не является моим любимым подходом, поскольку SQL на самом деле является кодом и тесно связан с кодом Java, который его вызывает. Поддержание и отладка разделенного кода может быть сложной задачей.

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

Ответ 3

Простое решение, которое мы реализовали, когда сталкиваемся с этим, заключалось в том, чтобы вытеснить SQL/DML в файл (mySql.properties), а затем использовать MessageFormat.format(String [] args) для добавления динамических свойств в SQL.

Например: mySql.properties:

select    *
    from      scott.emp
              join scott.dept on (emp.deptno = dept.deptno)
    where     emp.ename = {0}

Полезные методы:

public static String format(String template, Object[] args) {
    String cleanedTemplate = replaceSingleQuotes(template);
    MessageFormat mf = new MessageFormat(cleanedTemplate);
    String output = mf.format(args);
    return output;
}
private static String replaceSingleQuotes(String template) {
    String cleaned = template.replace("'", "''");
    return cleaned;
}

Затем используйте его так:

String sqlString = youStringReaderImpl("/path/to/file");
String parsedSql = format(sqlString, new String[] {"bob"});

Ответ 4

Я настоятельно рекомендую вам использовать хранимые процедуры. Это то, для чего они предназначены.

Ответ 5

Вы также можете использовать класс QueryLoader в Apache Commons DbUtils, который будет читать sql из файла свойств. Тем не менее, вам придется использовать DbUtils, который выполняет ту же задачу, что и JDBCTemplate.

Ответ 6

Библиотека ElSql предоставляет эту функциональность.

ElSql состоит из небольшого файла jar (шесть открытых классов), который позволяет загружать внешний файл SQL (elsql). Файл использует простой формат, чтобы при необходимости обеспечить немного больше поведения, чем просто загрузить файл:

-- an example comment
@NAME(SelectBlogs)
  @PAGING(:paging_offset,:paging_fetch)
    SELECT @INCLUDE(CommonFields)
    FROM blogs
    WHERE id = :id
      @AND(:date)
        date > :date
      @AND(:active)
        active = :active
    ORDER BY title, author
@NAME(CommonFields)
  title, author, content

// Java code:
bundle.getSql("SelectBlogs", searchArgs);

Файл разбит на @NAME блоки, на которые можно ссылаться из кода. Каждый блок определяется значительным отступом пробела. @PAGING вставляет необходимый код для подкачки, такой как FETCH/OFFSET. @AND будет выводиться только в том случае, если указанная переменная существует (помогая строить динамические поиски). DSL также обрабатывает LIKE vs = для подстановочных знаков при поиске. Цель дополнительных тегов DSL - предоставить общие основы, которые часто ударяются при попытке построить динамический SQL нейтральным способом базы данных.

Дополнительная информация о blog или руководство пользователя.

Ответ 7

Вставка здесь в Чистый способ экстернализации длинного (+20 строк sql) при использовании spring jdbc?:

Я столкнулся с той же проблемой некоторое время назад и придумал YAML. Он поддерживает многострочные значения свойств строки, поэтому вы можете записать что-то подобное в ваших файлах запросов:

selectSomething: >
  SELECT column1, column2 FROM SOMETHING

insertSomething: >
  INSERT INTO SOMETHING(column1, column2)
  VALUES(1, '1')

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

anotherSelect: <
  SELECT column1 FROM SOMETHING
  WHERE column2 IN (
    SELECT * FROM SOMETHING_ELSE
  )

Затем вы можете прочитать содержимое файла на хэш-карте с помощью библиотеки SnakeYAML, используя следующий код:

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.FileUtils;
import java.io.FileReader;

import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileNotFoundException;

public class SQLReader {
  private Map<String, Map> sqlQueries = new HashMap<String, Map>();

  private SQLReader() {
    try {
      final File sqlYmlDir = new File("dir_with_yml_files");
      Collection<File> ymlFiles = FileUtils.listFiles(sqlYmlDir, new String[]{"yml"}, false);
      for (File f : ymlFiles) {
        final String fileName = FilenameUtils.getBaseName(f.getName());
        Map ymlQueries = (Map)new Yaml().load(new FileReader(f));
        sqlQueries.put(fileName, ymlQueries);
      }
    }
    catch (FileNotFoundException ex) {
      System.out.println("File not found!!!");
    }
  }
}

В приведенном выше примере создается карта карт, сопоставляющая каждый файл YAML с картой, содержащей имена/строки запросов.

Ответ 8

Вы можете использовать Spring и иметь свои SQL-операторы, хранящиеся в вашем файле beans, которые вводятся, когда вы получаете класс из bean factory. Этот класс также может использовать экземпляр SimpleJDBCTemplate, который можно настроить с помощью файла bean, чтобы упростить ваш код.

Ответ 9

Просто и надежно делать классы из Spring. Возьмите ваши файлы SQL и сохраните их в определенном месте в вашем пути к классам. Это может быть в JAR файле, который только содержит SQL, если вы хотите. Затем используйте Spring ClassPathResource для загрузки файла в поток и использования Apache IOUtils для преобразования его в строку. Затем вы можете выполнить SQL с помощью SimpleJdbcTemplate или кода DB по вашему выбору.

Я предлагаю вам создать класс утилиты, который использует простой класс Java с общедоступными строковыми полями, которые соответствуют именам файлов SQL в соответствии с выбранным вами соглашением. Затем используйте отражение в сочетании с классом ClassPathResource, чтобы найти файлы SQL, соответствующие вашему соглашению об именах, и назначить их полям String. После этого просто обратитесь к полям класса, когда вам нужен SQL. Он прост, отлично работает и достигает цели, которую вы хотите. Он также использует хорошо изношенные классы и методы. Ничего особенного. Я сделал это пару лет назад. Прекрасно работает. Слишком ленив, чтобы получить код. У вас не будет времени выяснить это самостоятельно.

Ответ 10

Для этого вы можете использовать средства локализации. Затем вы используете имя базы данных в качестве локали, чтобы получить "оракскую" версию "insert-foo-in-bar" вместо английской или французской версии.

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

Ответ 11

dynamic-query - хорошая среда с открытым исходным кодом для тех, кто хочет что-то между JDBC и ORM.

1 простой SQL. - Он сохраняет простой sql для внешних файлов. нет избыточных тегов, поддерживает комментарии.

/* It also supports comment.
This code is in an external file 'sample.sql', Not inisde java code.*/
listUsers : select * from user_table
where user_id= $$;  /* $$ will automatically catch a parameter userId */


2 расширяемый SQL. -It поддерживает параметры, в том числе другие файлы и суб-запрос.

listUsers:
select
    id, amount, created
    @checkEmail{ ,email } 
from user_table
where amount > $amt and balance < $amt
    @checkDate { and created = $$ }
    @checkEmail{ and email in (
        select email from vip_list ) } ;        
/* Above query can be four queries like below.
1. listUsers
2. listUsers.checkDate 
3. listUsers.checkEmail
4. listUsers.checkDate.checkEmail 
*/



-- It can include other files like below
& ../hr/additional hr.sql ; 
& ../fi/additional fi.sql ;


Пример примера кода Java. установив значения в db.

QueryUtil qu = qm.createQueryUtil("selectAll");
try {
    qu.setConnection(conn);

    // with native jdbc
    qu.setString("alpha");
    qu.setDouble(10.1);
    qu.executeQuery();

    // or with bean
    qu.executeQuery(new User("alpha", 10.1));

    // or with map
    Map<String, Object> map=new HashMap<String, Object>();
    map.put("userName", "alpha");
    map.put("amt", 10.1);
    qu.executeQuery(map);

    // or with array
    qu.executeQueryParameters("alpha", 10.1);

Пример примера кода Java. получение значений из db.

    while (qu.next()) // == qu.rs.next()
    {
        // native jdbc
        String usreName = qu.getString("user_name"); 
        double amt = qu.getDouble("amt");

        // or bean
        User user = new User();
        qu.updateBean(user);

        // or array
        Object[] values = qu.populateArray();
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    qu.closeJust();
}

Ответ 12

Вы можете использовать скорость, чтобы иметь "скриптовые" sql-шаблоны, которые вы можете использовать для гибкого использования файлов. У вас есть примитивные утверждения, такие как условные и циклы, для создания ваших команд sql.

Но я настоятельно рекомендую использовать подготовленные заявления и/или хранимые процедуры. Построение SQL так, как вы планируете, сделает вас уязвимыми для SQL-инъекций, сервер БД не сможет кэшировать SQL-запросы (что приведет к плохой производительности).

BTW: вы также можете сохранить определение подготовленных операторов в файлах. Это не лучшее решение, но довольно близко к нему, и вы получаете преимущества защиты и производительности SQL-инъекций.

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