Шаблонизация sql, или история еще одного велосипеда, в...

Post on 22-Jul-2015

530 views 0 download

Transcript of Шаблонизация sql, или история еще одного велосипеда, в...

Шаблонизация SQLеще один велосипед

Алексей Романчук

Обо мне

О чем будем говорить?

Для кого

• Разработчики

• Много SQL

• Динамический SQL

Для чего стараемся?

• Сложные выборки

• Отчеты

• Любые SQL запросы

Очень простой пример

Очень простой пример

Простой пример

Простой пример

Простой пример

Пример

Пример

Не такой простой пример

Не такой простой пример

Не такой простой пример

Не такой простой пример

Пример почти из жизни

Пример почти из жизни

Почувствуйте разницу

Недостатки inline подхода

Недостатки inline подхода

• Невозможность ограничить текст запроса

• Дублирование логики в запросах и в параметрах

• Часть свойств в тексте запроса, часть как явные параметры

• Сложность разработки и отладки SQL

Как происходит разработка SQL

• Получить SQL из кода

• Заменить «?» на значения

• Внести изменения в SQL редакторе

• Внедрить изменения в код или «оживить» написанный SQL целиком

Что делать?

Шаблонизация FTW

Шаблонизация FTW

Шаблонизация FTW

Стало ли проще?

Стало ли проще?

Что дает шаблонизация?

• Возможность ограничить текст запроса

• Текст первичен

• Многострочные запросы легко копировать

Какой должна быть разработка SQL

• Получить SQL из кода

• Заменить «?» на значения

• Внести изменения в SQL редакторе

• Внедрить изменения в код или «оживить» написанный SQL целиком

Наконец!

Требования к шаблонизатору

• Шаблон должен быть максимально валидным SQL

• Возможность «выключить» ненужный join

• Поддержка типизированных параметров

• Автоматическое включение joinов

Где хранить метаданные чтобы шаблон оставался валидным SQL?

XDoclet

*.hbmJava source

javac .class

И не только

• Javadoc

• Google Closure Compiler for JS

• Eclipse localization (//$NON-NLS-1$)

• UML Doclet

Шаблонизатор

SQL редакторШаблон

Движок Запрос

Parameters

Шаблонизатор в двух словах

• Элемент

• Дерево

Элемент

• Имя

• Тип

• Значение

/*id(int */1045/*)*//*cond(*/inner join orders o…/*)*/

Единственная языковая конструкция

Варианты синтаксиса элемента

• /*{name*/1045/*}*/

• /*<name type=“int”>*/1045/*</name>*/

• /*<name:int*/1045/*>*/

• /*name(int */1045/*)*/

Работа с элементом

• По умолчанию элемент «выключен»

• Явное задание значение и проверка типа

• Использование значения из шаблона

Простой пример

select c.id, c.name

from company c

where c.name ilike ‘2 gis’

Простой пример

select c.id, c.name

from company c

where c.name ilike /*name(*/‘2 gis%’/*)*/

Простой пример

Template t = new Template(“…”);

t.assignValue(“name”, “Sbertech%”);

Простой пример

select c.id, c.name

from company c

where c.name ilike ‘Sbertech%’

Дерево

• Шаблон – дерево текста и элементов (xml)

• По умолчанию элемент «выключен»

• Включение элемента включает элемент родителя

Дерево

/*filter(*/where name = /*name(*/’Jack’/*))*/

• Шаблон

–filter

• name

Пример с join

select distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

where c.name ilike '2 gis%’

and co.country ilike ‘russia%’

Пример с join

select distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

where c.name ilike /*name(*/'2 gis%’/*)*/

and co.country ilike /*country(*/ ‘russia%’/*)*/

Пример с join

select distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

where c.name ilike /*name(*/'2 gis%’/*)*/

/*office(*/and co.country ilike /*country(*/ ‘russia%’/*))*/

Пример с join

select distinct c.id, c.name

from company c

/*office(*/inner join comp_office co on co.comp_id = c.id/*)*/

where c.name ilike /*name(*/'2 gis%’/*)*/

/*office(*/and co.country ilike /*country(*/ ‘russia%’/*))*/

Пример с join

• Шаблон

– office (join)

– name

– office (where)

• country

Пример с join

Template t = new Template(“…”);

t.assignValue(“name”, “Sbertech%”);

Пример с join

select distinct c.id, c.name

from company c

where c.name ilike ‘Sbertech%’

Пример с join

Template t = new Template(“…”);

t.assignValue(“name”, “Sbertech%”);

t.assignValue(“country”, “Rus%”);

Пример с join

select distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

where c.name ilike ‘Sbertech%’

and co.country ilike ‘Rus%’

Реальный примерselect distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

inner join orders o on o.comp_id = c.id

where c.name ilike '2 gis%'

and co.country ilike 'russia%'

and co.city ilike 'novosibirsk%'

and co.street ilike 'marks%'

and o.date > currenttime - '6 month'::interval

limit 10 offset 5

Реальный примерselect distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

inner join orders o on o.comp_id = c.id

where c.name ilike /*name(*/'2 gis%’/*)*/

and co.country ilike /*country(*/'russia%' /*)*/

and co.city ilike /*city(*/'novosibirsk%’/*)*/

and co.street ilike /*street(*/'marks%'/*)*/

and o.date > currenttime - '6 month'::interval

limit /*limit(int*/10/*)*/ offset /*offset(int*/5/*)*/

Реальный примерselect distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

inner join orders o on o.comp_id = c.id

where c.name ilike /*name(*/'2 gis%’/*)*/

and co.country ilike /*country(*/'russia%' /*)*/

and co.city ilike /*city(*/'novosibirsk%’/*)*/

and co.street ilike /*street(*/'marks%'/*)*/

/*active(*/and o.date > currenttime - '6 month'::interval/*)*/

limit /*limit(int*/10/*)*/ offset /*offset(int*/5/*)*/

Реальный примерselect distinct c.id, c.name

from company c

inner join comp_office co on co.comp_id = c.id

/*active(*/inner join orders o on o.comp_id = c.id/*)*/

where c.name ilike /*name(*/'2 gis%’/*)*/

and co.country ilike /*country(*/'russia%' /*)*/

and co.city ilike /*city(*/'novosibirsk%’/*)*/

and co.street ilike /*street(*/'marks%'/*)*/

/*active(*/and o.date > currenttime - '6 month'::interval/*)*/

limit /*limit(int*/10/*)*/ offset /*offset(int*/5/*)*/

Реальный примерselect distinct c.id, c.name

from company c

/*office(*/inner join comp_office co on co.comp_id = c.id/*)*/

/*active(*/inner join orders o on o.comp_id = c.id/*)*/

where c.name ilike /*name(*/'2 gis%’/*)*/

/*office(*/and co.country ilike /*country(*/'russia%' /*)*/

and co.city ilike /*city(*/'novosibirsk%’/*)*/

and co.street ilike /*street(*/'marks%'/*))*/

/*active(*/and o.date > currenttime - '6 month'::interval/*)*/

limit /*limit(int*/10/*)*/ offset /*offset(int*/5/*)*/

Реальный пример

• Шаблон– office (join)– active (join)– name– office (where)

• country• city• street

– active (where)– limit и offset

Реальный пример

Template t = new Template(“…”);

t.assignValue(“name”, “2 gis”);

t.assignValue(“country”, “russia”);

t.assignValue(“city”, “novosibirsk”);

t.assignValue(“street”, “marksa”);

t.assignValue(“limit”, 10);

t.assignValue(“offset”, 5);

t.enable(“active”);

Что дает нам велосипед?

• Ограничение текста запроса

• Позволяет контролировать тип параметров

• Позволяет не удалять значения параметров

• Позволяет разрабатывать и использовать в программе один и тот же шаблон

Что дальше?

• POJO как конфигурация запроса

• Как получить PreparedStatement, а не текст?

• “Reflection” API и автотесты

• IDE plugins

Вместо заключения

• Заимствуйте в смежных областях

• Сфокусируйтесь на узкой задаче

• Придумывайте простое решение

• DSL FTW