Я недавно работал над проектом, где мне нужно было сделать что-то подобное. Я создал следующий каталог для хранения файлов sql:
. / Grails-приложение / CONF / SQL
Например, есть файл ./grails-app/conf/sql/hr/FIND_PERSON_BY_ID.sql, в котором есть что-то вроде следующего:
select a.id
, a.first_name
, a.last_name
from person
where id = ?
Я создал класс SqlCatalogService , который загружал бы все файлы в этом каталоге (и подкаталогах) и сохранял имена файлов (без расширения) и текст файла на карте. У сервиса есть метод get (id), который возвращает текст sql, который кэшируется на карте. Поскольку файлы / каталоги, хранящиеся в grails-app / conf, помещаются в путь к классам, SqlCatalogService использует следующий код для чтения в файлах:
....
....
Map<String,String> sqlCache = [:]
....
....
void loadSqlCache() {
try {
loadSqlCacheFromDirectory(new File(this.class.getResource("/sql/").getFile()))
} catch (Exception ex) {
log.error(ex)
}
}
void loadSqlCacheFromDirectory(File directory) {
log.info "Loading SQL cache from disk using base directory ${directory.name}"
synchronized(sqlCache) {
if(sqlCache.size() == 0) {
try {
directory.eachFileRecurse { sqlFile ->
if(sqlFile.isFile() && sqlFile.name.toUpperCase().endsWith(".SQL")) {
def sqlKey = sqlFile.name.toUpperCase()[0..-5]
sqlCache[sqlKey] = sqlFile.text
log.debug "added SQL [${sqlKey}] to cache"
}
}
} catch (Exception ex) {
log.error(ex)
}
} else {
log.warn "request to load sql cache and cache not empty: size [${sqlCache.size()}]"
}
}
}
String get(String sqlId) {
def sqlKey = sqlId?.toUpperCase()
log.debug "SQL Id requested: ${sqlKey}"
if(!sqlCache[sqlKey]) {
log.debug "SQL [${sqlKey}] not found in cache, loading cache from disk"
loadSqlCache()
}
return sqlCache[sqlKey]
}
Сервисы, использующие различные источники данных, используют SqlCatalogService для извлечения sql путем вызова метода get (id):
class PersonService {
def hrDataSource
def sqlCatalogService
private static final String SQL_FIND_PERSON_BY_ID = "FIND_PERSON_BY_ID"
Person findPersonById(String personId) {
try {
def sql = new groovy.sql.Sql(hrDataSource)
def row = sql.firstRow(sqlCatalogService.get(SQL_FIND_PERSON_BY_ID), [personId])
row ? new Person(row) : null
} catch (Exception ex) {
log.error ex.message, ex
throw ex
}
}
}
Пока у нас есть только несколько SQL-операторов, поэтому сохранение всего текста на карте не является проблемой. Если вы храните много файлов sql, вам, возможно, придется подумать об использовании чего-то вроде Ehcache и определении стратегии выселения (т. Е. Наименее недавно использованной или наименее часто используемой) и хранении только наиболее используемой в памяти и оставляющей оставшуюся часть на диске до тех пор, пока она не понадобится. .
Перед этим я подумал об использовании GORM и сохранении текста sql в базе данных. Но решил, что с использованием sql в файлах стало легче разрабатывать, поскольку мы могли в значительной степени сохранить sql в файл непосредственно из нашего инструмента sql (заменяя параметры жесткого кода на вопросительные знаки) и могли позволить нашей системе контроля версий отслеживать изменения. Я не говорю, что вышеуказанный сервис - самый эффективный или правильный способ справиться с этим, но до сих пор он работал для наших нужд.