Требуется еще более простая передача данных Java / SQL - PullRequest
4 голосов
/ 30 декабря 2008

Итак, я использую jdbc для общения с БД MySQL. Для многих таблиц и для многих запросов / представлений я создал один класс, который инкапсулирует одну строку таблицы или результат запроса / таблицы. Доступ к БД возвращает один объект такого класса (когда я точно знаю, что есть только одна подходящая строка) или вектор таких объектов.

В каждом классе есть фабричный метод, который создает объект из строки ResultSet. Требуется множество методов ResultSet.getXXX (), а также аккуратная бухгалтерия относительно того, какое значение находится в каком столбце, особенно после изменения макета таблицы / запроса / представления.

Создание и поддержка этих объектов - скучная, трудоемкая и утомительная задача. Другими словами, это задача, которую выполняет инструмент. Он должен читать SQL (увы, вариант MySQL) и генерировать код Java. Или, по крайней мере, дайте мне представление (XML? DOM?) Таблицы / запроса / представления, что позволит мне самому генерировать код Java.

Можете назвать этот инструмент?

Ответы [ 5 ]

5 голосов
/ 30 декабря 2008

Я немного озадачен вашими вопросами. Почему бы вам не использовать инфраструктуру объектно-реляционного отображения, такую ​​как Hibernate?

Раньше у меня была та же проблема, связанная с чтением и написанием большого количества SQL напрямую. В конце концов я начал писать новые проекты с Hibernate и не оглядывался назад. Система позаботится о том, чтобы я создавал реальные таблицы и запускал SQL в фоновом режиме, и я в основном могу работать с объектами Java.

2 голосов
/ 30 декабря 2008

Если вы ищете простой фреймворк, который поможет дружественной работе при написании sql, я бы порекомендовал ibatis sql maps . Эта структура в основном делает именно то, что вы хотите.

Hibernate также является хорошим вариантом, но он кажется слишком большим для такой простой проблемы, как ваша.

Вы также можете взглянуть на пружинный каркас . Это нацелено на создание простой среды для написания Java-приложений, а также имеет очень удобную SQL-абстракцию. Но будьте осторожны с весной, вам может понравиться этот каркас и вы проведете слишком много счастливых часов с ним 8)

Что касается вашей заботы об отражении. У Java больше нет серьезных проблем с производительностью отражения (по крайней мере, начиная с версии 1.4 и с инструментами отображения O / R).

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

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

0 голосов
/ 28 декабря 2009

Редактировать: Бессмысленно. При поиске решения моей собственной проблемы я забыл проверить дату на этой вещи. Сожалею. Вы можете игнорировать следующее.

@ millermj - Вы делаете это ради развлечения или потому, что в этом есть необходимость? Просто любопытно, потому что это звучит так же, как то, что Java IDE, такие как Eclipse и NetBeans, уже предоставляют (используя Java Persistence API) с функциональностью New-> JPA-> Entity Classes из таблиц.

Я мог бы упустить момент, но если кому-то просто нужны классы, которые соответствуют их таблицам и являются устойчивыми, JPA плюс некоторая "магия" IDE может быть достаточно.

0 голосов
/ 30 декабря 2008

Помимо ОРМ ...

Если вы используете подпрограммы rs.getString и rs.getInt, то вы, безусловно, сможете облегчить бремя обслуживания, если будете полагаться на именованные столбцы, а не на пронумерованные столбцы.

В частности, rs.getInt ("id"), а не rs.getInt (1), например.

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

Затем вы берете эту идиому использования имен столбцов и расширяете ее до плана использования согласованных имен и, в то же время, «уникальных» имен. Предполагается, что с каждым столбцом в вашей базе данных связано уникальное имя. Теоретически он может быть таким же простым (хотя и многословным), как tablename_columnname, поэтому, если у вас есть таблица "member", для столбца id имя столбца будет "member_id".

Что это тебе покупает?

Это дает вам возможность использовать универсальные DAO для любого «действительного» набора результатов.

«Действительный» набор результатов - это набор результатов с именами столбцов с использованием уникальной спецификации именования.

Итак, вы получаете «выбрать идентификатор member_id, имя member_name от члена, где id = 1».

Почему вы хотите это сделать? Зачем идти к этому беспокойству?

Потому что тогда ваши объединения становятся тривиальными.

PreparedStatement = con.prepareStatement("select m.id member_id, m.name member_name, p.id post_id, p.date post_date, p.subject post_subject from member m, post p where m.id = p.member_id and m.id = 123");
ResultSet rs = ps.executeQuery();
Member m = null;
Post p = null;
while(rs.next()) {
    if (m == null) {
        m = MemberDAO.createFromResultSet(rs);
    }
    p = PostDAO.createFromResultSet(rs);
    m.addPost(p);
}

Смотрите, здесь логика привязки не заботится о содержимом результирующего набора, поскольку она интересует только те столбцы, о которых она заботится.

В ваших DAO вы делаете их немного умнее насчет ResultSet. Оказывается, если вы выполните 'rs.getInt ("member_id") ", а member_id на самом деле не окажется в наборе результатов BE, вы получите исключение SQLException.

Но, немного поработав, используя ResultSetMetaData, вы можете выполнить быструю предварительную проверку (извлекая все имена столбцов заранее), затем вместо вызова rs.getInt вы можете вызвать baseDAO.getInt, который обрабатывает эти детали для вас, чтобы не получить исключение.

Прелесть здесь в том, что как только вы это сделаете, вы можете легко получить неполные DAO.

PreparedStatement = con.prepareStatement("select m.id member_id from member m where m.id = 123");
ResultSet rs = ps.executeQuery();
Member m = null;
if (rs.next()) {
    m = MemberDAO.createFromResultSet(rs);
}

Наконец, это действительно (на самом деле) тривиальный сценарий (с использованием, скажем, AWK), который может взять свойства bean-компонента и преобразовать его в надлежащий двоичный код связующего кода для исходного DAO. Подобный сценарий может легко взять оператор таблицы SQL и преобразовать его в Java Bean (по крайней мере, базовые члены), который затем преобразуется в IDE в поток геттеров / сеттеров.

За счет централизации кода привязки в DAO обслуживание на самом деле практически ничего не происходит, поскольку оно изменилось в одном месте. Используя частичное связывание, вы можете безжалостно злоупотреблять им.

PreparedStatement = con.prepareStatement("select m.name member_name, max(p.date) post_date from member m, post p where post.member_id = m.id and m.id = 123");
ResultSet rs = ps.executeQuery();
Member m = null;
Post p = null;
if (rs.next()) {
    m = MemberDAO.createFromResultSet(rs);
    p = MemberDAO.craateFromResultSet(rs);
}
System.out.println(m.getName() + " latest post was on " + p.getDate());

Ваше бремя, продвигающееся вперед, состоит в основном в написании SQL, но даже это не ужасно Между написанием SQL и EQL нет большой разницы. Имейте в виду, это отчасти отстой, когда приходится писать оператор select с миллиардом столбцов, так как вы не можете (и не должны) использовать «select * from ...» (select * всегда (ВСЕГДА) приводит к беда, IME).

Но это только реальность. Я обнаружил, однако, что (если вы не делаете репортажи), эта проблема просто не бывает много. Это происходит, по крайней мере, один раз для большинства каждой таблицы, но это не происходит снова и снова. И, разумеется, когда он у вас есть один раз, вы можете либо «вырезать и вставить» свой путь к славе, либо изменить его (т.е. "от участника m, сообщение p").

Теперь мне нравятся JPA и ORM, я нахожу их полезными, но я также нахожу их PITA. Там происходят определенные отношения любви / ненависти. И когда дела идут гладко, мальчик, это гладко. Но когда это становится каменистым - ху мальчик. Тогда это может стать ужасным. В целом, однако, я рекомендую их.

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

0 голосов
/ 30 декабря 2008

Я создал мини-фреймворк, подобный тому, что много лет назад, но он был предназначен для создания прототипов, а не для производства.

Идея следует, и это ОЧЕНЬ очень просто сделать. Компромисс - это стоимость использования отражения. Хотя Hibernate и другие инструменты ORM также оплачивают эту стоимость.

Идея очень проста.

  • У вас есть класс Dao, где вы выполняете запрос.

  • Прочитайте метаданные ResultSet, и там вы сможете получить имя таблицы, поля, типы и т. Д.

  • Найдите в пути к классам класс, который соответствует имени таблицы и / или имеет одинаковое количество / типы полей.

  • Установите значения с помощью отражения.

  • Верните этот объект и бросьте его на другую сторону, и все готово.

Может показаться абсурдным найти класс во время выполнения. И может выглядеть слишком рискованно, потому что запрос может измениться или структура таблицы может измениться. Но подумай об этом. Когда это происходит, вы должны в любом случае обновить сопоставления, чтобы они соответствовали новой структуре. Таким образом, вместо этого вы просто обновляете соответствующий класс и довольны этим.

Я не знаю, как инструменты ORM работают для снижения стоимости вызовов на отражение (поскольку единственное, что он делает для сопоставления, это помогает вам найти подходящий класс) В моей версии поиск среди около 30 000 классов (я добавил jar из других мест, чтобы проверить это) заняло только .30 мс или что-то подобное. Я сохранил в кэше этот класс, и во второй раз мне не пришлось искать его.

Если вам интересно (и все еще читаете), я постараюсь найти библиотеку на моем старом ПК.

В конце мой код был примерно таким:

 Employee e = ( Employee ) MagicDataSource.find( "select * from employee where id = 1 ");

или

Employee[] emps = ( Employee[] ) MagicDataSource.findAll("select * from employee ");

Внутри это было похоже:

Object[] findAll( String query ) {
     ResultSet rs = getConnection().prepareStatemet( query ).executeQuery();
     ResultSetMetaData md = rs.getMetadata();
     String tableName = md.getTableName();

     String clazz = findClass( toCamelCase( tableName ) ); // search in a list where all the class names where loaded.
     Class.forName( clazz );

     while( rs.next() ) {

         for each attribute etc. etc. 
             setter...
         end

         result.append( object );
     }

    return result.toArray();
 }

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

В конце это позволило мне создавать быстрые небольшие программы для мониторинга системы или тому подобное. Я больше не делаю эту работу, и эта библиотека сейчас забыта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...