LINUX.ORG.RU

Состряпал конструктор SQL на Java

 , , , ,


1

1

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

В связи с чем я решил разработать его аналог на Java: https://github.com/r0ck3r/IQL

Документация доступна там же, а вот некоторые примеры использования:

Connection con = DriverManager.getConnection("jdbc:mysql://server/database", properties);

Вставка данных:

IQL iql = new IQL(con);  
iql.addTable("mytable");  
iql.setInsertRows("name %s", "register_date %d", "level %i");  
iql.insert("User1", "17.05.2017", 4);  
iql.insert("User2", "12.03.2016", 5);  
Statement st = iql.getStatement();

Сгенерирует следующее:

INSERT INTO `mytable`(`name`, `register_date`, `level`) VALUES ('User1', 1494968400, 4), ('User2', 1457730000, 5)

Обновление данных:

IQL iql = new IQL(con);  
iql.addTable("organisations");  
iql.setUpdateRows("name %s", "address %s");  
iql.update("New orgname", "New address");  
iql.whereId(112);  
PreparedStatement ps = iql.getStatement();
Сгенерирует следующее:
UPDATE `organisations` SET `name` = 'New orgname', `address` = 'New address' WHERE `organisations`.`id` = 112
при этом, если для операций обновления или удаления не указан where, то будет сгенерировано исключение

Пример выборки:

IQL iql = new IQL(con);
iql.addTable("domains").select("subdomain subdomain", "domain domain").where("domain %s", IQL.ISNTNULL);
iql.addTable("orgs").select("org_name name", "org_address address").where("org_name %s", IQL.LIKE, "%организация%");
iql.join(2, "id", 1, "org_id"); //присоединить к таблице №2 (orgs) таблицу №1 domains по полям id из orgs к org_id из domains
String SQL = iql.getSQL();

Создаст следующий SQL-код:

SELECT 
`domains`.`subdomain` AS `subdomain`, 
`domains`.`domain` AS `domain`, 
`orgs`.`org_name` AS `name`, 
`orgs`.`org_address` AS `address` 
FROM `orgs` 
JOIN `domains` ON `orgs`.`id` = `domains`.`org_id` 
WHERE 
`domains`.`domain` IS NOT NULL AND 
`orgs`.`org_name` LIKE '%организация%'

В общем, кому надо - используйте

★★★★★

Цельной строкой можно задать-получить? Так запрос читабельней, его можно выдрать и проверить вне Java.

ilovewindows ★★★★★
()
Ответ на: комментарий от anonymous

видел его - не понравилось

r0ck3r ★★★★★
() автор топика
Ответ на: комментарий от r0ck3r

А чего уж задать пожадничал, все равно на сервер строка идет. Имхо, когда множество условий в where например можно делать генератор запроса, тогда код читабельнее, а на простейших запросах красота sql потеряется в куче строк. Но это ,имхо, я так о своем, о девичьем.

ilovewindows ★★★★★
()

Запах не лучшего кодстайла на пыхе так и преследует это дело.

Если не умеет во что-нибудь кроме mysql - ненужное by design.

nihirash ★★★
()
Ответ на: комментарий от nihirash

Запах не лучшего кодстайла на пыхе так и преследует это дело.

Примеры, пожалуйста - буду исправлять, чтоб вам не пахло

Если не умеет во что-нибудь кроме mysql - ненужное by design.

Уметь должен все, кроме создания и таблиц, если подправить которое с «id INTEGER PRIMARY KEY AUTO_INCREMENT» на «id serial primary key» - то все должно быть в порядке. А так да, на чем-то кроме MySQL пока не тестировал ибо вчера-позавчера писал код, а сегодня wiki

r0ck3r ★★★★★
() автор топика
Ответ на: комментарий от ilovewindows

Если задавать код, то там нужно будет его парсить, чтобы определить куда втыкать where, либо что-то еще. Но в общем идея нравится - думаю реализую

r0ck3r ★★★★★
() автор топика
Ответ на: комментарий от r0ck3r

Примеры, пожалуйста - буду исправлять, чтоб вам не пахло

seriously?

Хотя бы SOLID нужно начать соблюдать.

JavaDoc нужен.

Ваш класс IQL, который должен быть, видимо фасадом(но в этом случае он должен быть только фасадом), вызывает просто кровавые слезы и требует разнесения на кучу файлов и глубокий рефакторинг.

nihirash ★★★
()

Есть JOOQ. Есть JPA, в частности Hibernate. Есть MyBatis. Есть liquibase.
Все эти штуки сравнивать мягко говоря глупо, но их существование сводит твою работу на нет, причем с треском. Если ты получил фан, good for you.

bytecode ★★
()

а у тебя тут чейнинга нет? почему addTable сделаны отдельными вызовами, а не в цепочке?

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

чейнинг есть везде, а с addTable - это пример такой, что после него можно сразу делать select, а можно так

iql.addTable("table1", "table2", "table3");
можно так:
iql.addTable("table1").addTable("table2").addTable("table3");

r0ck3r ★★★★★
() автор топика
Ответ на: комментарий от nihirash

SOLID:

На каждый объект должна быть возложена одна единственная обязанность.

где мои объекты выполняют больше одной обязанности?

Принцип открытости/закрытости

вроде выполнено в пределах разумного

Принцип подстановки Барбары Лисков
Объекты в программе могут быть заменены их наследниками без изменения свойств программы.

Разве нет?

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

Ничего лишнего не нужно реализовывать

Принцип инверсии зависимостей

Здесь нет кода, который должен был бы выполняться независимо друг от друга

JavaDoc

Добавил

Ваш класс IQL, который должен быть, видимо фасадом(но в этом случае он должен быть только фасадом), вызывает просто кровавые слезы и требует разнесения на кучу файлов и глубокий рефакторинг.
требует разнесения на кучу файлов

Если вы про вложенные приватные классы, то их имеет смысл выносить в разные классы только тогда, когда они хотя бы теоретически могут использоваться за пределами главного класса. Разве не так? Или вы еще что-то увидели?

r0ck3r ★★★★★
() автор топика
Ответ на: комментарий от bytecode

Получил фан и опыт. На конкурецию с JOOQ не расчитываю, ибо он монстр, JPA, как я уже говорил, это ORM, а он нужен не всем. Что такое MyBatis и liquibase не знаю, но беглый гуглинг показал, что первое - это очередной ORM, а второе - какой-то инструмент миграций

r0ck3r ★★★★★
() автор топика
Ответ на: комментарий от anonymous

плюсую. все уже украдено написано до нас.

bvn13 ★★★★★
()
Ответ на: комментарий от r0ck3r

MyBatis в давние времена имел неблагозвучное название и был совсем не orm, он не управлял объектами, но позволял вынести SQL из исходников. Еще там был тул, который позволял накатывать и откатывать изменения ddl, да и хранить их в vcs. Твой тул просто еще один велосипед коих много, но пусть будет, только он пока гвоздями прибит к одному диалекту sql, наверное mysql. это сразу видно по типам, например boolean не во всех БД.

А вот эта штука по-моему в оракле вообще не заработает:

INSERT INTO `mytable`(`name`, `register_date`, `level`) VALUES ('User1', 1494968400, 4), ('User2', 1457730000, 5)

Aber ★★★★★
()
Ответ на: комментарий от r0ck3r

MyBatis на низком уровне не совсем ORM, его можно использовать как sql template engine. liquibase - это про про управление структурой базы на протяжении жизни проекта, если совсем в двух словах.

bytecode ★★
()

а я написал конструктор запросов sql?

string sSqlTemplate = "select * from {0} where id = '{1}'";
string s1 = string.Format(sSqlTemplate, "dbo.Table1", "1");
string s2 = string.Format(sSqlTemplate, "dbo.Table1", "2");

это не оно?

taker
()

Краем глаза взглянул и есть пара замечаний:

1. Почему доки не на английском?

2.

private static final int RT_S = 1; //varchar
а чего не enum?

3.

public void reset() {
Может всё же стоило делать билдером? Ресет своего рода деструктор, который не совсем имеет смысл в жаве.

Hater ★★
()
Ответ на: комментарий от nihirash

Хотя бы SOLID нужно начать соблюдать.

С чего это? В мечеть нужно ходить совершать намаз.

dzidzitop ★★
()
Ответ на: комментарий от Hater

1. Да, доки со временем переведу, просто знаний немного не хвататет, чтобы вот так с ходу грамотно написать документацию на английском 2. Спасибо, перенес это и тип операций в отдельные enum 3. Не понял. Что значит «стоило делать билдером»? На жаве пишу недавно от слова совсем 4. Пока не делал

r0ck3r ★★★★★
() автор топика

iql.join(2, «id», 1, «org_id»); //присоединить к таблице №2 (orgs) таблицу №1 domains по полям id из orgs к org_id из domains

плохо когда у разработчика нет обоняния, и он не чувствует что дизайн пованивает 8)

Deleted
()
Ответ на: комментарий от bytecode

Есть JOOQ.

https://github.com/jOOQ/jOOQ/blob/ec3d87a712e0b2666dd5c41036a3b6fb2a233502/jO...

тащить такое в продакшен? пустое место если кто не понял вырезанные куски поддержки коммерческих субд, суть в том что ребята не осилили в дизайн и размазали саппорт каждой бд по всему коду

Deleted
()
Ответ на: комментарий от r0ck3r

потом принюхаешься и чужое перестанет

Deleted
()
Ответ на: комментарий от AndreyKl

не, чувак погорит на table alias но пока об этом не знает, впрочем ТС скорее всего тоже

Deleted
()

Искренне поздравляю. Но я бы делал как-то так:

iql.select("table1.field1", "table1.field2")
.from("table1")
.innerJoin("table2", "table2.table1_id", "table1.id")
.where("field1", "1")
.andWhere("field2", "2")
.all();

Но это все из моего плотного опыта работы с различными queryBuilder в PHP. И тут второй момент, Java... На сколько я знаю, там используют лишь проверенные решения, которым очень много лет, Enterprise же, поэтому шанс того, что это будет использовано равно нулю, я бы на PHP все таки сделал, как пакет к composer.

fman2
()

Немного не досмотрел, это лишь конструктор, который данные не вытягивает?

Если так, то честно - SQL код читать гораздо легче, да и вообще его всегда читать легче;)

fman2
()

Мой совет — используй стандартный SQL. От этих конструкторов проблем больше, чем пользы. Единственное, где конструкторство имеет смысл — совсем уж типичные select/insert/update на одну таблицу, да и то ещё надо подумать.

Вот что реально нужно, это удобная работа с результатом запроса и удобная параметризация запроса. В JDBC этого нет. iBatis это даёт, но не совсем до конца и на мой вкус переусложнено, запросы утекли в XML и тд, в общем на любителя. Хочется удобную обёртку над JDBC, которой можно пользоваться из джавы без XML-ей. Хотя в 2017-м наверное уже надо под Kotlin такое делать.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Spring jdbcTemplate, не? Не требует самого спринга, не тащит нисколько XML, поддерживает именованные параметры и 100500 видов оструктуривания результатов, имеет вменяемый батчинг. Я не знаю что еще можно лучше для lightweight jdbc (без ОРМ) придумать.

anonymous
()
Ответ на: комментарий от anonymous

Spring jdbcTemplate

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

Aber ★★★★★
()
Ответ на: комментарий от Aber

Нету спринга да и лень, но по-моему при фейле он кидает эксепшен и делает close который, в свою очередь, делает неявный commit, а обработка фейла происходит только если используется transaction manager.

Aber ★★★★★
()
Ответ на: комментарий от Deleted

Не, это и не нравится. Запрос должен быть там, где он используется, то бишь в Java-файле. А прыгать туда-сюда, удовольствие ниже среднего.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Запрос должен быть там, где он используется, то бишь в Java-файле.

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

Deleted
()
Ответ на: комментарий от Deleted

Ну я сам себе sql девелопер, html верстальщик, centos админщик, кофе варильщик и всё такое, мне все эти разделения только мешают. СУБД естественно только одна. Если тебе запросы пишут специально обученные люди, можно и так, конечно.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Если тебе запросы пишут специально обученные люди

а если нет? html у тебя тоже внутри java сорцов?

Deleted
()
Ответ на: комментарий от Deleted

а если нет? html у тебя тоже внутри java сорцов?

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

Legioner ★★★★★
()
Ответ на: комментарий от Deleted

А какая разница в конечном счёте. Ну интегрируй мне SQL в нормальный язык, я буду только рад. А пока такого не придумали, приходится просто вставлять строками, и так нормально.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от Legioner

А какая разница в конечном счёте

ну да между языком разметки (html) и языком программирования jsx (это js с некоторым dsl) - разницы никакой, ну вааще

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

Больше ада будет когда встанет вопрос тестирования запросов, но кодеров это не волнует, я правильно понимаю?

Deleted
()
Ответ на: комментарий от Jopich1

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

r0ck3r ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.