Разработка слоя работы с базой данных

advertisement
Разработка слоя работы с базой данных
Введение
В этом разделе показывается, как грамотно разработать слой работы с базой данных,
который позволит логически отделить объекты предметной области приложения от их
хранения в базе данных.
Причина важности сохранения объектов предметной области отдельно от механизма
хранения состоит в том, что такое разделение позволяет многократно использовать эти
объекты в приложении, которые применяют различные базы данных.
Если аргумент многократного использования для вас не достаточен, то такое разделение
позволяет также легче понимать объекты и, естественно, поддерживать их
функционирование. Делая объекты понятнее, вы придете к сокращению ошибок и
улучшению возможностей расширения программного обеспечения. Также я убежден, что
это приведет к прозрачности кода и к повышению эффективности его выполнения.
Значительно проще изменить метод, состоящий из 15 строк кода, нежели из 100 строк, что
означает возможность создания улучшенного повторного использования кода внутри
объектов.
Другим существенным преимуществом отделения уровня работы с базой данных является
обеспечение возможности изменения хранилища, в котором вы храните свои данные. В
большинстве приложений вы можете быстро написать слой приложения для работы с
базой данных MySQL и затем заказчик переходит на Oracle для выполнения приложения,
вам требуется замещать некоторые классы. В ситуации, когда вы разрабатываете
программное обеспечение для рынка, вы вряд ли вынуждать пользователя использовать
конкретную базу данных. Слой работы с базой данных позволяет вам обеспечить ваших
заказчиков соответствующим хранилищем для их базы данных.
Надеюсь, что вы заинтересуетесь тем, что представляет собой слой приложения,
ответственный за работу с базой данных. Слой приложения, ответственный за работу с
базой данных, можно представить как соответствующий слой кода. Зачастую, в объектноориентирванных системах говорят о слоях программного обеспечения, имея ввиду нечто
похожее на рисунок ниже. Они содержат слой пользовательского интерфейса (User
Interface Tier), Слой бизнес логики (Domain Tier) и слой управления данными (Data
Management Tier) (иногда существуют и другие слои, и почти всегда используются
различные наименования для различных слоев. Слой доступа к базе данных представляет
собой слой управления данными.
Назначение слоя управления данными состоит в том, чтобы получать объекты Java и
запоминать их в какой-либо форме хранилища, обычно в реляционной базе данных. За
пределы этого слоя не распространяется какая-либо информация о том, каким образом это
выполняется. Слой доступа к базе данных должен иметь удобный интерфейс с помощью
которого вы запрашиваете объекты, а также представляете те объекты, которые вам
необходимо добавить или заменить в базе данных. Кроме того с помощью этого
интерфейса вы должны иметь возможность давать команды удалять объекты, которые
больше не должны храниться б базе данных.
Слой управления данными должен быть образован на ранних стадиях объектноориентированной разработки, и, даже без достаточного опыта проектирования объектноориентированных систем, большинство разработчиков приходят к пониманию такого
решения. Этот слой создается, учитывая всю необходимую функциональность для
взаимодействия с базой данных и выделяя эту функциональность в набор объектов,
который может быть замещен при изменении хранилища. Поскольку такие задачи
возникают достаточно давно, этому слою присваиваются различные имена, образующие
слой доступа к данным: слой управления данными (Data Management Tier), объект доступа
к данным (Data Access Object),отображение данных (Data Mapper), …. Безотносительно
названия этот слой выполняет примерно одни и те же функции.
Далее буде показано как может быть реализован уровень доступа к данным для объекта
Employee.
Один момент, который следует иметь в виду, состоит в том, что вы не захотите
разрабатывать свой уровень доступа к базе данных. Хотя большинство организаций попрежнему разрабатывают свои слои доступа к данным, это является достаточно
трудоемкой работой. Возможно, было бы дешевле приобрести готовый продукт, который
будет заботиться о большинстве ваших потребностей в доступе к данным. Даже если
большинство компаний тратят огромные ресурсы на разработку, тестирование и отладку
их собственных средств работы с базой данных, они продолжают делать это, а не
тратиться на коммерческий продукт, обычно потому что не готовы уделять внимание и и
иные ресурсы на изучение возможных альтернатив.
Бесплатное программное обеспечение является еще одной альтернативой, но оно обычно
уступает коммерческому ПО. Хотя и здесь можно найти достойные разработки, такие,
например, как Hibernate.
Написание собственной программы Data Access Object
(DAO)
Обычно программы, которые обеспечивают уровень доступа к данным, называют Data
Access Objects (DAOs) поскольку именно так Sun называет такие шаблоны (patterns) в
проектах на Java. Это не означает, что данное название является более правильным, чем
другие, однако многие разработчики Java приложений (да и не только) используют эту
терминологию.
По большому счету реализация работы с внешней памятью должна поддерживать
следующую функциональность.







Инкапсулировать механизм работы с внешней памятью. Вам следует только
вызывать методы save, delete, restore для хранимых объектов и слой управления
данными автоматически выполнял действия с вашей базой данных.
Действия с многими объектамиs. Вы можете извлекать данные и удалять по
многим объектам одновременно.
Поддержка связей. Когда вы save, delete или update любой объект, вы можете
выполнить такие же действия со связанными объектами.
Поддержка наследования. Позволяет отображать дерево наследования на схему
базы данных.
Поддержка транзакций. Вы можете объединить несколько действий в одну
транзакцию и commit или rollback как одно действие.
Multiple database support. Различные объекты могут быть отображены на
различные базы данных..
Поддержка Optimistic и pessimistic блокировки
Первым делом при написании вашего первого DAO необходимо определить интерфейс, с
которым вы будете работать. Как правило интерфейс будет включать один или более
методов find, а также методы insert, update и delete.
Даже после описания интерфейса, используемого в данном подходе имейте в виду, что не
все объекты предметной области будут храниться точно таким же образом. Точно также
не все объекты предметной области имеют аналогичные требования в отношении системы
хранения данных.Интерфейс должен быть удобен в процессе использования, а не в
смысле выполнения проверок на этапе компиляции. Создать простой интерфейс и думать,
что все ваши го интерфейса должны его использовать является наивным решением и
зачастую может обнаруживаться позже, что какой-нибудь объект не работает эффективно.
Типы критериев поиска объектов
Поиск объектов, которые хранятся во внешней памяти, является весьма важной частью
слоя
управления данными. По этой причине люди имеют различные способы
устанавливать для DAO в объектно-ориентированного подходе объекты, которые они
хотят.
Ниже мы рассмотрим три типа поиска:



Методы поиска (Search methods)
Поиск по примеру (Search by example)
Поисковые языки (Search languages)
Методы поиска (Search Methods)
Методы поиска – самые простые способы указания для DAO какой объект необходимо
найти. Это приводит, однако, к некоторой хрупкости в коде, который его использует. Для
многих приложений, это достаточно простой, понятный и относительно исчерпывающий
способ поиска. По этой причине этот стиль поиска применяется Sun для спецификации
Enterprise JavaBean.
Процесс вовлекает создание метода, который определяет каждый тип поиска, который
вам необходим. The process involves creating a method that defines each type of search you
need. Любые критерии, которые изменяются от поиска к поиску определяются как
аргументы методу.
Для применения этого способа поиска рекомендуется следовать соглашениям Sun для
Enterprise JavaBeans. Это сделает методы поиска очевидными для разработчиков, которые
знакомы с этой спецификацией.
Для каждого поиска вы создаете метод; имя метода должно начинаться с "find", затем
необязательно включается имя переменной, которая возвращается, и в конце "By" или
"For" с набором понятно именованных аргументов. Например, чтобы найти employee по
уникальному идентификатора объекта, можно создать метод с именем Employee
findFor(long oid).
Тип возвращаемого объекта в имени особенно важен, если вы возвращаете более чем один
объект в коллекции. Например, для поиска всех сотрудников (employee) с одной
фамилией, вы можете создать метод со следующей сигнатурой: List findFor( String
lastName ). Второй способ разделяется когда необходимо осуществлять поиск одного и
того же типа по двум различным критериям. В этом случае следует создать метод с более
точным именем, например, с именем findForLastName.
Используя такие имена, создается ясный и естественный метод поиска. Каждый метод
легко переврдится на английский: В примера "Find Employee By employeeId" или "Find
Employees For lastName."
Применение этих методов может быть достаточно широким. Поскольку эти методы часто
добавляются непосредственно в слой управления данным, соответствующий код JDBC
непосредственно добавляется в метод , который поддерживает потск.
Простой метод find может выглядеть следующим образом:
public Employee findFor( long oid ) throws FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "SELECT employee_id, first_name, last_name, email "
+ "FROM employees "
+ "WHERE employee_id = " + oid;
resultSet = statement.executeQuery( sql );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e.getMessage() );
}
finally {
closeAll( connection, statement, resultSet );
}
}
Аналогичный метод для поиска по фамилии может быть, например, таким:
public Collection findForLastName( String lastName ) throws FindException
{
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
List employees = new ArrayList();
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "SELECT employee_id, first_name, last_name, email "
+ "FROM employees "
+ "WHERE last_name = '" + lastName + "'";
resultSet = statement.executeQuery( sql );
while( resultSet.next() ) {
employees.add( newEmployeeFrom( resultSet ) );
}
return employees;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e.getMessage() );
}
finally {
closeAll( connection, statement, resultSet );
}
}
Поиск по примеру (Search by Example)
Не смотря на то, что реализация методов find проста, эти методы становятся
трудоемкими в сопровождении. Если уникальный указатель становится состоящим из
двух частей, то необходимо изменить все обращения в других методах к измененному
методу. Те же самые проблемы возрастают, если возникает необходимость добавить
новый критерий поиска к любому существующему методу find.
Один из способов предотвратить эти проблемы - создать один объект, который определяет
ваши параметры запроса вместо пакета отдельных параметров запроса.
Как только вы решили, что переходите на целый объект, необходимо решить будете ли вы
использовать строго типизированные или мягко типизированные примеры критериев.
Строго типизированные примеры (Strongly Typed Example)
В строго типизированном примере вы можете использовать компилятор если вы
используете некорректно тип параметра; это происходит поскольку ваши объекты запроса
(query) будут либо объекты предметной области либо объекты запроса тесно отражать
объекты предметной области.
Самая простая реализация состоит в том, чтобы использовать простые объекты
предметной области для определения того, что следует найти. Игнорируются любые
свойства объекта, которые не определены. Любые свойства включаются как часть запроса.
Для этого типа запроса вам следует написать два метода для всех ваших поисковых
операций.
Первый метод определяет существует ли хотя бы один экземпляр,
удовлетворяющий критерию и возвращает первый объект. Сигнатура для такого метода
может иметь следующий вид: Employee findFor( Employee exampleCriteria ).
Сигнатура для метода, который возвращает много экземпляров может быть Collection
findAllFor( Employee exampleCriteria ).
public Employee findFor( Employee exampleCriteria ) throws FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
StringBuffer sql = new StringBuffer( "SELECT employee_id, first_name,
"
+"last_name, email "
+ "FROM employees "
+ "WHERE " );
if( exampleCriteria.getOid() != 0 ) {
sql.append( "employee_id = " );
sql.append( exampleCriteria.getOid() );
sql.append( " AND " );
}
if( exampleCriteria.getFirstName() != null ) {
sql.append( "first_name = '" );
sql.append( exampleCriteria.getFirstName() );
sql.append( "' AND " );
}
if( exampleCriteria.getLastName() != null ) {
sql.append( "last_name = '" );
sql.append( exampleCriteria.getLastName() );
sql.append( "' AND " );
}
if( exampleCriteria.getEmail() != null ) {
sql.append( "email = '" );
sql.append( exampleCriteria.getEmail() );
sql.append( "' AND " );
}
sql.append( "'A' = 'A'" );
resultSet = statement.executeQuery( sql.toString() );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e.getMessage() );
}
finally {
closeAll( connection, statement, resultSet );
}
}
Вторая реализация может содержать параллельную иерархию. Например, вы можете
иметь класс Employee и класс EmployeeCriteria, который содержит много похожих
методов. Иерархия является параллельной, поскольку вам, как правило, необходим один
объект критерия (Criteria) для каждого значимого объекта предметной области.
Преимуществом параллельной иерархии является то, что вы можете добавлять методы,
которые имеют смысл для запросов, которые могут не быть частью объектов предметной
области. Например, по фамилии может иметь метод setLastNameEquals и метод
Первый ищет все точные совпадения, в то время как второй выполняет
поиск по нечеткому совпадению с шаблоном.
setLastNameLike.
Это также позволяет искать по значениям null. Если вы используете реальные объекты
бизнеса для запроса, то любые значения null игнорируются. Используя объект Criteria,
вы можете найти всех служащих, чьи фамилии не определены, используя вызов
setLastNameNull(). Этот метод может установить флаг, который ваш DAO может
использовать для обозначений.
public class EmployeeCriteria {
String lastNameEquals = null;
String lastNameLike = null;
boolean lastNameNull = false;
public String getLastNameEquals() {
return lastNameEquals;
}
public void setLastNameEquals(String lastNameEquals) {
this.lastNameEquals = lastNameEquals;
}
public String getLastNameLike() {
return lastNameLike;
}
public void setLastNameLike(String lastNameLike) {
this.lastNameLike = lastNameLike;
}
public boolean isLastNameNull() {
return lastNameNull;
}
public void setLastNameNull(boolean lastNameNull) {
this.lastNameNull = lastNameNull;
}
}
Код DAO может создать SQL как и ранее и может проверить на очень ясные указатели
того, что является преметом поиска. Если свойство lastNameEquals установлено, то вы
можете добавить в ваш запрос last_name = ‘value’. Если свойство lastNameLike
установлено, то вам необходимо добавить в ваш запрос last_name LIKE ‘%value%’. Если
lastNameNull установлено равным true, то добавьте last_name IS NULL в ваш запрос.
public Employee findFor( EmployeeCriteria exampleCriteria ) throws
FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
StringBuffer sql = new StringBuffer( "SELECT employee_id, first_name,
"
+ "last_name, email "
+ "FROM employees "
+ "WHERE " );
if( exampleCriteria.getLastNameEquals() != null ) {
sql.append( "last_name = '" );
sql.append( exampleCriteria.getLastNameEquals() );
sql.append( "' AND " );
}
if( exampleCriteria.getLastNameLike() != null ) {
sql.append( "last_name LIKE '%" );
sql.append( exampleCriteria.getLastNameLike() );
sql.append( "%' AND " );
}
if( exampleCriteria.isLastNameNull() ) {
sql.append( "last_name IS NULL AND " );
}
sql.append( "'A' = 'A'" );
resultSet = statement.executeQuery( sql.toString() );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e.getMessage() );
}
finally {
closeAll( connection, statement, resultSet );
}
}
Мягкая типизация примера (Softly Typed Example)
Другой тип поиска по примеру использует мягкую типизацию объекта критерия. В этом
случае вы используете схему пар значений имен, которые передаются в DAO для поиска.
Такая реализация предоставляет достаточную гибкость в том, что может быть добавлено в
качестве критерия. Вы можете добавлять новые поисковые поля без какого бы то ни было
изменения самого объекта критерия. Обратная сторона такой возможности состоит в том,
что не выполняется проверки во время компиляции и появляется возможность установить
имена, которые DAO не может использовать при поиске; эта проблема может быть
разрешена использованием static переменных.
Как и со всеми реализациями, вы можете выполнить как простую так и более полную
версии программы. Для простых критериев каждое значение, находящееся в критерии
является совпадением по знаку "=" в SQL; другими словами если значение включается, то
оно должно точно совпадать. Для того чтобы иметь дело с нечеткими совпадениями или
со значением nulls, потребуется более полная реализация.
Для простой реализации вам следует просто выполнить extend a map, добавляя public
final static constants.
public class SimpleSoftEmployeeCriteria extends HashMap {
public final static String OID = "oid";
public final static String LAST_NAME = "lastName";
public final static String FIRST_NAME = "firstName";
public final static String EMAIL = "email";
}
Более сложная реализация должна принимать во внимание такие понятия как
установление значения, которое будет совпадать с NULL или выдача коллекции значений,
которые должны быть частью. Нечеткая логика позволяется тоже достаточно просто. Вы
можете использовать спецификаторы LIKE и BETWEEN. Все это может быть сделано во
время использования переменной criteria.
public interface Criteria {
/**
* Adds a value that the attribute must match
*
*@param attribute Attribute to compare
*@param value Value it must be equal to
*/
public void addEquals(String attribute, Object value);
/**
* Adds a value that the attribute must be like (globbing with % for sql)
*
*@param attribute Attribute to compare
*@param value Value to compare to
*/
public void addLike(String attribute, Object value);
/**
* Sets values that the attribute must be between
*
*@param attribute Attribute to compare
*@param from Low end of comparison
*@param to High end of comparison
*/
public void addBetween(String attribute, Object from, Object to);
/**
* Sets a list of values that the attribute must be contained in
*
*@param attribute Attribute to compare
*@param values Attribute must match one of the values included
*/
public void addIn(String attribute, Collection values);
/**
* Adds a feature to the Null attribute of the Criteria object
*
*@param attribute The feature to be added to the Null attribute
*/
public void addNull(String attribute);
}
Создание конкретной реализации такого типа критерия может позволить вам выполнять
некоторую проверку во время компиляции посредством значений констант. Во многом
аналогично прежде продемонстрированным критериям вы можете расширить критерии и
добавить константы.
public class EmployeeCriteria extends AbstractCriteria {
public final static String OID = "oid";
public final static String LAST_NAME = "lastName";
public final static String FIRST_NAME = "firstName";
public final static String EMAIL = "email";
}
Окончательное добавление к такому критерию состоит в том, что вы можете добавить
правила коллекций. Вы можете добавить правила для каждого атрибута, который
устанавливается, что позволит выполнять проверку устанавливаемых значений во время
выполнения. Другими словами, если вы захотели написать правило для numDependants и
решили усилить его тем, что только целые добавляются к критерию, вы можете написать
правило, что только целым значениям разрешено добавляться к критерию. Если
пользователь вашего критерия пытается добавить атрибут, который не перечислен в
правилах, то вы можете вызывать управляемую исключительную ситуацию.
Поисковые языки (Search Languages)
Последний способ описания информационных потребностей
для
запроса DAO
выполняется через поисковые языки. В этой ситуации вы определяете язык, отличный от
SQL, который позволяет потребителю запрашивать объекты, которые ему необходимы.
Причиной создания нового языка является отделение потребителя от слоя хранения
данных. Если разрешается непосредственно манипулировать SQL, потребитель может
непосредственно обратиться к серверу, а вы можете многое сократить или вообще убрать
отдельный уровень управления данными.
Как вы будете реализовывать поисковый язык решать вам. Вы можете использовать
шаблон интерпретатора, который представлен в Design Patterns: Elements of Reusable
Object-Oriented Software (Gamma, Helm, Johnson, and Vlissides. Addison-Wesley Publishing
Company. ISBN: 0-20163-361-2). Вы можете грубо усилить язык используя
java.util.StringTokenizer и выполнить большую работу.
Другое возможное решение поискового языка состоит в использовании XML. Определяя
поисковый язык на основе XML, вы можете расширить один из многих парсеров,
которые уже написаны. Это позволяет не заботиться о чтении языка, поскольку язык
построен.
Основа CRUD
Большое количество взаимодействий с базой данных состоит из процессов Create, Read,
Update и Delete (CRUD). Чтение (reading) является наиболее сложным из четырех
операций. Реализация других процессов будет почти всегда состоять в создании
подключения к базе данных, чтении из объекта и записи в объект и общении с базой
данных. Это не различается в значительной степени между различными вариантами
реализации слоя управления данными.
В процесса Create, Update и Delete из CRUD, создается утверждение JDBC, присваиваются
значения и утверждение выполняется.
Вставка новых объектов (Inserting New Objects)
Вставка новых объектов состоит из создания нового объекта, взаимодействия с ним
(обычно присвоения значений), выполнения бизнес методов и сохранении объекта в базе
данных - все из перечисленного выполняется довольно легко.
Одной из проблем уровня хранения данных состоит в том, что вам необходимо
идентифицировать какие объекты являются одинаковыми, если даже они содержат
различные значения. Например, одна часть приложения может читать запись о служащем
Dave Glyzewski и корректно изменить его имя на Homer Simpson. Другая часть
приложения может иметь отдельную копию записи Dave Glyzewski без изменений. Часто
необходимо уникально идентифицировать объект без обращения внимания на значение
атрибутов.
Вы можете изменить имя Dave Glyzewski's, его код иждивенчества , адрес e-mail и номер
телефона. И если это возможно, изначально не имеющийся и позже полученный номер
Social Security. Поскольку все эти атрибуты изменяемые, но не изменяется сущность
объекта с которой они связаны, предлагается рассмотреть простой уникальный
идентификатор. Он обычно называется идентификатор объекта - object identifier (OID).
Во многих случаях вам не понадобится выпускать the OID до тех пор пока вы не
сохраняете объект во внешней памяти. Это позволяет выполнить генерация временных
объектов без чрезмерной работы.
Идентификаторы Generating Universally Unique Object
Существует несколько способов генерации
OID. Вы можете использовать
последовательности из базы данных, которые отслеживают выпущенные
OID и
увеличить последовательность, когда выпускается последующий. Можно организовать
аналогичный собственный процесс путем создания таблицы, которая будет содержать
строки для каждого бизнес-объекта и столбцы, содержащие последний выпущенный ID
класса.
Другая идея состоит в создании Global Unique Identifier (GUID) или Universal Unique
Identifier (UUID). Оба понятия учитывают адрес компьютера, на котором идентификатор
генерируется и момент времени генерации. Часто другие составляющие входят в смесь
для увеличения безопасности сгенерированного числа, аналогично хеш соду объекта в
VM для обеспечения того, что два объекта не могут быть созданы в одно и то же время
Аналогично GUID и легче получить из объекта java.rmi.dgc.VMID, который предназначен
для получения уникального номера внутри всех Java Virtual Machine. Условием того,
чтобы VMID был уникальным в системе является то, что система генерируется так, что
требуется более чем одна миллисекунда для загрузки, время не возвращается вспять и IPадрес полученный из Java не изменяется во время жизни объекта.
В то время как VMID обещает быть уникальным, он не соответствует определению UUID
или GUID, которые требуют использования идентификатора оборудования сетевой карты.
Доступ к такой информации отсутствует из Java в это время. Таким образом, VMID
является наилучшим действующим решением в
Java без реализации вызовов
операционной системы.
Код для генерации VMID очень простой. К сожалению, отсутствует другой простой тип,
для преобразования его для хранения в базе данных отличный от String. По этой причине
ваши бизнес-объекты требуют чтобы их OID были типа string.
String vmid = new VMID().toString();
Исходя из всего того, что было сказано, большинство приложений не нуждаются в GUID.
Один из аргументов состоит в том, что они позволяют разделять объекты между
системами. В то время как Web services делают это более вероятным чем когда либо,
большинство систем не доверяют даже GUID и будут найдены другие способы
идентифицировать объекты приходящие из других систем нежели полагаться на
обещания других систем.
Другой простой способ генерации OID состоит в получении наибольшего текущего
заполненного OID и увеличении его на единицу. Для того, чтобы это сработало
необходимо выполнить некоторую обработку путем синхронизации метода nextOid и
метода insert.
private synchronized long nextOid( Statement statement ) throws
SQLException {
String sql = "SELECT MAX( employee_id ) FROM employees";
ResultSet resultSet = statement.executeQuery( sql );
resultSet.next();
long lastOid = resultSet.getLong( 1 );
return lastOid + 1;
}
Разновидностью этого метода является создание отдельной таблицы для хранения всех
OID и затем выпуска по одному по запросу. Этот способ не требует синхронизации ваших
методов. Недостатком является выпуск неиспользуемых значений.
Вставка объектов (Inserting the Object)
Теперь, когда у вас есть уникальный идентификатор для объекта, давайте рассмотрим
вставку объекта в базу данных. Это весьма простое занятие. Вы просто создаете
утверждение INSERT SQL и заполняете его значениями из объекта, который вставляется.
В ситуации аналогичной вставке объекта employee вы создаете утверждение INSERT для
таблицы Employees, которая содержит все атрибуты, которые вы сохраняете.
Часто при вставке объекта вы хотите вернуть объект отличный от того, с которым вы
имели дело. Это происходит благодаря побочному эффекту, вызываемому вставкой
объекта. Примером является создание OID. Объект employee, который обрабатывается
методом insert не имеет OID, но он должен быть возвращен..В следующем примере мы
рассмотрим как создается новый объект employee:
/**
* This method inserts an employee into the database. It is a different
* employee object that is returned from this method. The new employee object
* contains all the data of the original as well as a unique object
identifier.
*/
public synchronized Employee insert( Employee employee )
throws PersistenceException {
Connection connection = null;
Statement statement = null;
long oid = 0;
int results = 0;
try {
connection = getConnection();
statement = connection.createStatement();
oid = nextOid( statement );
String sql = "INSERT INTO employees "
+ "(employee_id, first_name, last_name, email) "
+ "VALUES "
+ "("
+ oid + ", "
+ "'" + employee.getFirstName() + "', "
+ "'" + employee.getLastName() + "', "
+ "'" + employee.getEmail() + "' "
+ ")";
results = statement.executeUpdate( sql );
if( results == 1 ) {
Employee newEmployee = new Employee( oid );
newEmployee.setFirstName( employee.getFirstName() );
newEmployee.setLastName( employee.getLastName() );
newEmployee.setEmail( employee.getEmail() );
return newEmployee;
}
else {
throw new PersistenceException( "Unable to insert Employee" );
}
}
catch( SQLException e ) {
e.printStackTrace();
throw new PersistenceException( e.getMessage() );
}
finally {
closeAll( connection, statement, null );
}
}
Одно последнее замечание по рассмотренному коду состоит в том, что код не активирует
an SQLException. Вместо этого создается PersistenceException. Рекомендуется иметь
PersistenceException как оболочку для SQLException для того, чтобы сохранить
полезные данные, вызвавшие проблему.
Изменение данных (Updating the Business Object)
Зачастую значения бизнес-объектов изменяются. Так в нашем HR приложении изменения
могут произойти, когда служащие изменяют количество иждивенцев или изменяют свое
имя после замужества. Из-за взаимозависимости объектов обычной практикой является
изменение данных объекта вместо удаления и повторной ставки объекта.
Изменение данных объекта очень похоже на вставку объекта – используется только
другой SQL. Вместо утверждения insert, вы используете update. Поскольку вы
замещаете данные объекта в базе данных, вам нет необходимости выпускать новый OID,
также как и нет необходимости возвращать объект т.к. отсутствует сторонние при
изменении.
public void update( Employee employee ) throws PersistenceException {
Connection connection = null;
Statement statement = null;
int results = 0;
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "UPDATE employees SET "
+ "first_name = '" + employee.getFirstName() + "', "
+ "last_name = '" + employee.getLastName() + "', "
+ "email = '" + employee.getEmail() + "' "
+ "WHERE employee_id = " + employee.getOid();
results = statement.executeUpdate( sql );
if( results != 1 ) {
throw new PersistenceException( "Unable to update Employee" );
}
}
catch( SQLException e ) {
e.printStackTrace();
throw new PersistenceException( e.getMessage() );
}
finally {
closeAll( connection, statement, null );
}
}
Как и с методом insert, этот метод вызывает PersistenceException. Это позволяет
сохранить оставшуюся часть вашего приложения независимой от механизма хранения
данных во внешней памяти. Другой момент для замечаний состоит в том, что вы
вызываете исключительное состояние (exception), если количество обработанных строк,
возвращенных вашим executeUpdate не равняется 1. Это потому, что только одна запись
должна быть обработана; если это не так, то либо объект был удален, дибо никогда не
существовал.
Удаление (Deleting Business Objects)
Самая простая функция CRUD это удаление. SQL простой и метод такой же. Вы просто
берете объект, который хотите удалить из базы данных; как только это сделано вы идете
дальше. Метод delete будет удалять все значения связанные с OID для объекта, который
передан в качестве параметра.
public void delete( Employee employee ) throws PersistenceException {
if( employee == null ) {
return;
}
Connection connection = null;
Statement statement = null;
int results = 0;
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "DELETE FROM employees WHERE employee_id = "
+ employee.getOid();
results = statement.executeUpdate( sql );
if( results != 1 ) {
throw new PersistenceException( "Unable to delete Employee" );
}
}
catch( SQLException e ) {
e.printStackTrace();
throw new PersistenceException( e.getMessage() );
}
finally {
closeAll( connection, statement, null );
}
}
Как и с методом update, лишь одна строка должна быть удалена методом delete.В
противном случае требуется вызов исключительной ситуации. После завершения работы
подключение и утверждение закрываются в finally.
Реализация приведенных выше рассуждений
Ниже приводится исходные тексты программ, использующих выше приведенные
рассуждения. Классы находятся в соответствующих пакетах. Ваша задача состоит в том,
что разобраться в работе каждого класса и воспроизвести выполнении этого приложения
применительно к базе данных DB Derby (как представлено в программе) или MS ACCESS
и затем к своей предметной области.
_______________________________________________________________
/*
* ExerciseDAO.java
*
* Created on August 21, 2009, 9:50 PM
*/
import java.util.*;
import bo.Employee;
import dao.*;
/**
*
* @author aad
*/
public class ExecuteDAO {
private static EmployeeDAO dao = new EmployeeDAO();
public static void main( String[] args ) {
if( args.length < 2 ) {
System.out.println( "usage: ExercuteDAO username password"
);
return;
}
System.setProperty( "username", args[ 0 ] );
System.setProperty( "password", args[ 1 ] );
findEmployee();
findEmployees();
insertEmployee();
updateEmployee();
deleteEmployee();
findEmployeeByExample();
findEmployeeByExampleCriteria();
findEmployeeBySoftCriteria();
}
private static void findEmployee() {
System.out.println( "\n\n**** findEmployee ****" );
try {
Employee employee = dao.findFor( 1 );
System.out.println( employee );
}
catch( FindException e ) {
e.printStackTrace();
}
}
private static void findEmployees() {
System.out.println( "\n\n**** findEmployees ****" );
try {
Employee employee = null;
Collection employees = dao.findForLastName( "Glyzewski" );
for( Iterator i = employees.iterator(); i.hasNext(); ) {
employee = (Employee) i.next();
System.out.println( employee );
}
}
catch( FindException e ) {
e.printStackTrace();
}
}
private static void findEmployeeByExample() {
System.out.println( "\n\n**** findEmployeesByExample ****" );
try {
Employee exampleCriteria = new Employee();
exampleCriteria.setLastName( "Chaltry" );
Employee employee = dao.findFor( exampleCriteria );
System.out.println( employee );
}
catch( FindException e ) {
e.printStackTrace();
}
}
private static void findEmployeeByExampleCriteria() {
System.out.println( "\n\n**** findEmployeeByExampleCriteria
****" );
try {
EmployeeCriteria exampleCriteria = new EmployeeCriteria();
exampleCriteria.setLastNameEquals( "Chaltry" );
Employee employee = dao.findFor( exampleCriteria );
System.out.println( employee );
exampleCriteria = new EmployeeCriteria();
exampleCriteria.setLastNameLike( "haltr" );
employee = dao.findFor( exampleCriteria );
System.out.println( employee );
exampleCriteria = new EmployeeCriteria();
exampleCriteria.setLastNameNull(true);
employee = dao.findFor( exampleCriteria );
System.out.println( employee );
}
catch( FindException e ) {
e.printStackTrace();
}
}
private static void findEmployeeBySoftCriteria() {
System.out.println( "\n\n**** findEmployeeBySoftCriteria ****"
);
try {
SimpleSoftEmployeeCriteria criteria
= new SimpleSoftEmployeeCriteria();
criteria.put( SimpleSoftEmployeeCriteria.FIRST_NAME, "Dale"
);
Employee employee = dao.findFor( criteria );
System.out.println( employee );
}
catch( FindException e ) {
e.printStackTrace();
}
}
private static void insertEmployee() {
System.out.println( "\n\n**** insertEmployee ****" );
Employee newEmployee = new Employee();
Employee insertedEmployee = null;
newEmployee.setFirstName( "Wilbur" );
newEmployee.setLastName( "Surfer" );
newEmployee.setEmail( "wilbur.surfer@centare.com" );
System.out.println( newEmployee );
try {
insertedEmployee = dao.insert( newEmployee );
System.out.println( insertedEmployee );
}
catch( PersistenceException e ) {
e.printStackTrace();
}
}
private static void updateEmployee() {
System.out.println( "\n\n**** updateEmployee ****" );
Employee employee = null;
try {
employee = dao.findFor( 1 );
System.out.println( employee );
employee.setEmail( "dglyzewski@centare.com" );
dao.update( employee );
employee = dao.findFor( 1 );
System.out.println( employee );
}
catch( PersistenceException e ) {
e.printStackTrace();
}
catch( FindException e ) {
e.printStackTrace();
}
}
private static void deleteEmployee() {
System.out.println( "\n\n**** deleteEmployee ****" );
Employee employee = null;
try {
employee = dao.findFor( 4 );
System.out.println( employee );
dao.delete( employee );
employee = dao.findFor( 4 );
System.out.println( employee );
}
catch( PersistenceException e ) {
e.printStackTrace();
}
catch( FindException e ) {
e.printStackTrace();
}
}
}
_______________________________________________________________
/*
* Employee.java
*
* Created on August 21, 2009, 9:13 PM
*/
package bo;
/**
*
* @author aad
*/
public class Employee {
/** Holds value of property oid. */
private long oid;
/** Holds value of property firstName. */
private String firstName;
/** Holds value of property lastName. */
private String lastName;
/** Holds value of property email. */
private String email;
/** Creates a new instance of Employee */
public Employee() {
}
/** Creates a new instance of Employee */
public Employee( long oid ) {
this.oid = oid;
}
/** Getter for property oid.
* @return Value of property oid.
*/
public long getOid() {
return this.oid;
}
/** Getter for property firstName.
* @return Value of property firstName.
*/
public String getFirstName() {
return this.firstName;
}
/** Setter for property firstName.
* @param firstName New value of property firstName.
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/** Getter for property lastName.
* @return Value of property lastName.
*/
public String getLastName() {
return this.lastName;
}
/** Setter for property lastName.
* @param lastName New value of property lastName.
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
/** Getter for property email.
* @return Value of property email.
*/
public String getEmail() {
return this.email;
}
/** Setter for property email.
* @param email New value of property email.
*/
public void setEmail(String email) {
this.email = email;
}
public String toString() {
return "oid: " + oid + "; "
+ "firstName: " + firstName + "; "
+ "lastName: " + lastName + "; "
+ "email: " + email;
}
}
_______________________________________________________________
package dao;
/*
* AbstractCriteria.java
*
* Created on September 4, 2009, 9:22 PM
*/
import java.util.*;
/**
*
* @author aad
*/
public class AbstractSoftCriteria {
public static final int EQUALS = 1;
public static final int LIKE = 2;
public static final int BETWEEN = 3;
public static final int NULL = 4;
private Map criteria = new HashMap();
public void addEquals(String attribute, Object value) {
criteria.put( attribute, new CriteriaEntry(EQUALS, value) );
}
public void addLike(String attribute, Object value) {
criteria.put( attribute, new CriteriaEntry(LIKE, value) );
}
public void addBetween(String attribute, Object value1, Object value2) {
criteria.put( attribute,
new CriteriaEntry(BETWEEN,
new Object[]{ value1, value2 } ) );
}
public void addNull(String attribute) {
criteria.put( attribute, new CriteriaEntry(NULL, null) );
}
public Set getAttributes() {
return Collections.unmodifiableSet(criteria.keySet());
}
public Object getValue(String attribute) {
return ((CriteriaEntry) criteria.get(attribute)).getValue();
}
public int getType(String attribute) {
return ((CriteriaEntry) criteria.get(attribute)).getType();
}
public static class CriteriaEntry {
private int type;
private Object value;
private CriteriaEntry(int type, Object value) {
this.type = type;
this.value = value;
}
public int getType() {
return type;
}
public Object getValue() {
return value;
}
}
}
_______________________________________________________________
package dao;
/*
* EmployeeCriteria.java
*
* Created on August 28, 2009, 8:59 PM
*/
/**
*
* @author aad
*/
public class EmployeeCriteria {
String lastNameEquals = null;
String lastNameLike = null;
boolean lastNameNull = false;
/** Creates a new instance of EmployeeCriteria */
public EmployeeCriteria() {
}
/** Getter for property lastNameEquals.
* @return Value of property lastNameEquals.
*/
public String getLastNameEquals() {
return lastNameEquals;
}
/** Setter for property lastNameEquals.
* @param lastNameEquals New value of property lastNameEquals.
*/
public void setLastNameEquals(String lastNameEquals) {
this.lastNameEquals = lastNameEquals;
}
/** Getter for property lastNameLike.
* @return Value of property lastNameLike.
*/
public String getLastNameLike() {
return lastNameLike;
}
/** Setter for property lastNameLike.
* @param lastNameLike New value of property lastNameLike.
*/
public void setLastNameLike(String lastNameLike) {
this.lastNameLike = lastNameLike;
}
/** Getter for property lastNameNull.
* @return Value of property lastNameNull.
*/
public boolean isLastNameNull() {
return lastNameNull;
}
/** Setter for property lastNameNull.
* @param lastNameNull New value of property lastNameNull.
*/
public void setLastNameNull(boolean lastNameNull) {
this.lastNameNull = lastNameNull;
}
}
_______________________________________________________________
/*
* EmployeeDAO.java
*
* Created on August 21, 2009, 8:33 PM
*/
package dao;
import bo.Employee;
import java.util.*;
import java.sql.*;
/**
*
* @author
*/
aad
public class EmployeeDAO {
static {
try {
Class.forName( "org.apache.derby.jdbc.ClientDriver"
).newInstance();
}
catch( Exception e ) {
e.printStackTrace();
}
}
public Employee findFor( Employee exampleCriteria ) throws FindException
{
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
StringBuffer sql = new StringBuffer( "SELECT employee_id,
first_name, "
+"last_name, email "
+ "FROM employees "
+ "WHERE " );
if( exampleCriteria.getOid() != 0 ) {
sql.append( "employee_id = " );
sql.append( exampleCriteria.getOid() );
sql.append( " AND " );
}
if( exampleCriteria.getFirstName() != null ) {
sql.append( "first_name = '" );
sql.append( exampleCriteria.getFirstName() );
sql.append( "' AND " );
}
if( exampleCriteria.getLastName() != null ) {
sql.append( "last_name = '" );
sql.append( exampleCriteria.getLastName() );
sql.append( "' AND " );
}
if( exampleCriteria.getEmail() != null ) {
sql.append( "email = '" );
sql.append( exampleCriteria.getEmail() );
sql.append( "' AND " );
}
sql.append( "'A' = 'A'" );
resultSet = statement.executeQuery( sql.toString() );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e, "finding Employee" );
}
finally {
closeAll( connection, statement, resultSet );
}
}
public Employee findFor( EmployeeCriteria exampleCriteria ) throws
FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
StringBuffer sql = new StringBuffer( "SELECT employee_id,
first_name, "
+ "last_name, email "
+ "FROM employees "
+ "WHERE " );
if( exampleCriteria.getLastNameEquals() != null ) {
sql.append( "last_name = '" );
sql.append( exampleCriteria.getLastNameEquals() );
sql.append( "' AND " );
}
if( exampleCriteria.getLastNameLike() != null ) {
sql.append( "last_name LIKE '%" );
sql.append( exampleCriteria.getLastNameLike() );
sql.append( "%' AND " );
}
if( exampleCriteria.isLastNameNull() ) {
sql.append( "last_name IS NULL AND " );
}
sql.append( "'A' = 'A'" );
resultSet = statement.executeQuery( sql.toString() );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
throw new FindException( e, "finding employee");
}
finally {
closeAll( connection, statement, resultSet );
}
}
public Employee findFor( SimpleSoftEmployeeCriteria criteria ) throws
FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
StringBuffer sql = new StringBuffer( "SELECT employee_id,
first_name, "
+"last_name, email "
+ "FROM employees "
+ "WHERE " );
String attribute = null;
Object value = null;
for( Iterator i = criteria.keySet().iterator(); i.hasNext(); ) {
attribute = (String) i.next();
value = criteria.get( attribute );
if( SimpleSoftEmployeeCriteria.OID.equals( attribute ) )
sql.append( "employee_id = " + value + " AND " );
else if( SimpleSoftEmployeeCriteria.FIRST_NAME.equals(
attribute ) )
sql.append( "first_name = '" + value + "' AND " );
else if( SimpleSoftEmployeeCriteria.LAST_NAME.equals(
attribute ) )
sql.append( "last_name = '" + value + "' AND " );
else if( SimpleSoftEmployeeCriteria.EMAIL.equals( attribute )
)
sql.append( "email = '" + value + "' AND " );
}
sql.append( "'A' = 'A'" );
resultSet = statement.executeQuery( sql.toString() );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e, "Finding employee" );
}
finally {
closeAll( connection, statement, resultSet );
}
}
public Employee findFor( SoftEmployeeCriteria criteria ) throws
FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
StringBuffer sql = new StringBuffer( "SELECT employee_id,
first_name, "
+"last_name, email "
+ "FROM employees "
+ "WHERE " );
String attribute = null;
Object value = null;
for( Iterator i = criteria.getAttributes().iterator();
i.hasNext(); ) {
attribute = (String) i.next();
value = criteria.getValue( attribute );
if( SimpleSoftEmployeeCriteria.OID.equals( attribute ) )
sql.append( "employee_id = " + value + " AND " );
else if( SimpleSoftEmployeeCriteria.FIRST_NAME.equals(
attribute ) )
sql.append( "first_name = '" + value + "' AND " );
else if( SimpleSoftEmployeeCriteria.LAST_NAME.equals(
attribute ) )
sql.append( "last_name = '" + value + "' AND " );
else if( SimpleSoftEmployeeCriteria.EMAIL.equals( attribute )
)
sql.append( "email = '" + value + "' AND " );
}
sql.append( "'A' = 'A'" );
resultSet = statement.executeQuery( sql.toString() );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e, "finding employee" );
}
finally {
closeAll( connection, statement, resultSet );
}
}
private String formatEntry(String attribute, int type, Object value) {
StringBuffer buf = new StringBuffer(attribute);
switch( type ) {
case AbstractSoftCriteria.EQUALS:
buf.append(" = ");
buf.append( value );
case AbstractSoftCriteria.LIKE:
buf.append(" LIKE ");
}
return buf.toString();
}
public Employee findFor( long oid ) throws FindException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Employee employee = null;
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "SELECT employee_id, first_name, last_name, email "
+ "FROM employees "
+ "WHERE employee_id = " + oid;
resultSet = statement.executeQuery( sql );
if( resultSet.next() ) {
employee = newEmployeeFrom( resultSet );
}
return employee;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e, "finding employee" );
}
finally {
closeAll( connection, statement, resultSet );
}
}
public Collection findForLastName( String lastName ) throws FindException
{
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
List employees = new ArrayList();
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "SELECT employee_id, first_name, last_name, email "
+ "FROM employees "
+ "WHERE last_name = '" + lastName + "'";
resultSet = statement.executeQuery( sql );
while( resultSet.next() ) {
employees.add( newEmployeeFrom( resultSet ) );
}
return employees;
}
catch( SQLException e ) {
e.printStackTrace();
throw new FindException( e, "finding employee" );
}
finally {
closeAll( connection, statement, resultSet );
}
}
private Employee newEmployeeFrom( ResultSet resultSet ) throws
SQLException {
long oid = resultSet.getLong( "employee_id" );
Employee employee = new Employee( oid );
employee.setFirstName( resultSet.getString( "first_name" ) );
employee.setLastName( resultSet.getString( "last_name" ) );
employee.setEmail( resultSet.getString( "email" ) );
return employee;
}
public synchronized Employee insert( Employee employee )
throws PersistenceException {
Connection connection = null;
Statement statement = null;
long oid = 0;
int results = 0;
try {
connection = getConnection();
statement = connection.createStatement();
oid = nextOid( statement );
String sql = "INSERT INTO employees "
+ "(employee_id, first_name, last_name, email) "
+ "VALUES "
+ "("
+ oid + ", "
+ "'" + employee.getFirstName() + "', "
+ "'" + employee.getLastName() + "', "
+ "'" + employee.getEmail() + "' "
+ ")";
results = statement.executeUpdate( sql );
if( results == 1 ) {
Employee newEmployee = new Employee( oid );
newEmployee.setFirstName( employee.getFirstName() );
newEmployee.setLastName( employee.getLastName() );
newEmployee.setEmail( employee.getEmail() );
return newEmployee;
}
else {
throw new PersistenceException( "Unable to insert Employee"
);
}
}
catch( SQLException e ) {
e.printStackTrace();
throw new PersistenceException( e.getMessage() );
}
finally {
closeAll( connection, statement, null );
}
}
private synchronized long nextOid( Statement statement ) throws
SQLException {
String sql = "SELECT MAX( employee_id ) FROM employees";
ResultSet resultSet = statement.executeQuery( sql );
resultSet.next();
long lastOid = resultSet.getLong( 1 );
return lastOid + 1;
}
public void update( Employee employee ) throws PersistenceException {
Connection connection = null;
Statement statement = null;
int results = 0;
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "UPDATE employees SET "
+ "first_name = '" + employee.getFirstName() + "', "
+ "last_name = '" + employee.getLastName() + "', "
+ "email = '" + employee.getEmail() + "' "
+ "WHERE employee_id = " + employee.getOid();
results = statement.executeUpdate( sql );
if( results != 1 ) {
throw new PersistenceException( "Unable to delete Employee"
);
}
}
catch( SQLException e ) {
e.printStackTrace();
throw new PersistenceException( e.getMessage() );
}
finally {
closeAll( connection, statement, null );
}
}
public void delete( Employee employee ) throws PersistenceException {
if( employee == null ) {
return;
}
Connection connection = null;
Statement statement = null;
int results = 0;
try {
connection = getConnection();
statement = connection.createStatement();
String sql = "DELETE FROM employees WHERE employee_id = "
+ employee.getOid();
results = statement.executeUpdate( sql );
if( results != 1 ) {
throw new PersistenceException( "Unable to delete Employee"
);
}
}
catch( SQLException e ) {
e.printStackTrace();
throw new PersistenceException( e.getMessage() );
}
finally {
closeAll( connection, statement, null );
}
}
private Connection getConnection() throws SQLException {
String url = "jdbc:derby://localhost:1527/customer";
String username = System.getProperty( "username" );
String password = System.getProperty( "password" );
return DriverManager.getConnection( url, username, password );
}
private void closeAll( Connection connection,
Statement statement,
ResultSet resultSet ) {
if( resultSet != null ) {
try { resultSet.close(); }
catch( SQLException e ){}
}
if( statement != null ) {
try { statement.close(); }
catch( SQLException e ){}
}
if( connection != null ) {
try { connection.close(); }
catch( SQLException e ){}
}
}
}
_______________________________________________________________
/*
* StoreException.java
*
* Created on September 09, 2009, 9:44 PM
*/
package dao;
/**
*
* @author aad
*/
public class FindException extends dao.WrapperException {
/**
* Creates a new instance of <code>StoreException</code> without detail
message.
*/
public FindException() {
}
/**
* Constructs an instance of <code>StoreException</code> with the
specified detail message.
* @param msg the detail message.
*/
public FindException(Throwable wrapped, String msg) {
super(wrapped, msg);
}
}
_______________________________________________________________
package dao;
/*
* PersistenceException.java
*
* Created on August 21, 2009, 8:35 PM
*/
/**
*
* @author aad
*/
public class PersistenceException extends java.lang.Exception {
/**
* Creates a new instance of <code>StoreException</code> without detail
message.
*/
public PersistenceException() {
}
/**
* Constructs an instance of <code>StoreException</code> with the
specified detail message.
* @param msg the detail message.
*/
public PersistenceException(String msg) {
super(msg);
}
}
_______________________________________________________________
package dao;
/*
* SimpleSoftCriteria.java
*
* Created on August 28, 2009, 9:28 PM
*/
import java.util.*;
/**
*
* @author aad
*/
public class SimpleSoftEmployeeCriteria extends HashMap {
public final static String OID = "oid";
public final static String LAST_NAME = "lastName";
public final static String FIRST_NAME = "firstName";
public final static String EMAIL = "email";
}
_______________________________________________________________
package dao;
/*
* SoftEmployeeCriteria.java
*
* Created on September 4, 2002, 9:53 PM
*/
/**
*
* @author aad
*/
public class SoftEmployeeCriteria extends AbstractSoftCriteria {
public final static String OID = "oid";
public final static String LAST_NAME = "lastName";
public final static String FIRST_NAME = "firstName";
public final static String EMAIL = "email";
}
_______________________________________________________________
/*
* StoreException.java
*
* Created on September 18, 2009, 9:44 PM
*/
/**
*
* @author aad
*/
public class StoreException extends dao.WrapperException {
/**
* Creates a new instance of <code>StoreException</code> without detail
message.
*/
public StoreException() {
}
/**
* Constructs an instance of <code>StoreException</code> with the
specified detail message.
* @param msg the detail message.
*/
public StoreException(Throwable wrapped, String msg) {
super(wrapped, msg);
}
}
_______________________________________________________________
/* WrapperException.java
*
* Created on September 16, 2009, 9:40 PM
*/
package dao;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
*
* @author aad
*/
public class WrapperException extends java.lang.Exception {
Throwable wrapped;
/**
* Creates a new instance of <code>WrapperException</code> without detail
message.
*/
public WrapperException() {
}
/**
* Constructs an instance of <code>WrapperException</code> with the
specified detail message.
* @param msg the detail message.
*/
public WrapperException(String msg) {
super(msg);
}
/**
* Constructs an instance of <code>WrapperException</code> with the
specified detail message.
* @param msg the detail message.
*/
public WrapperException(Throwable wrapped) {
super(wrapped.getMessage());
this.wrapped = wrapped;
}
/**
* Constructs an instance of <code>WrapperException</code> with the
specified detail message.
* @param msg the detail message.
*/
public WrapperException(Throwable wrapped, String msg) {
super(msg);
this.wrapped = wrapped;
}
/** Prints this throwable and its backtrace to the
* standard error stream. This method prints a stack trace for this
* <code>Throwable</code> object on the error output stream that is
* the value of the field <code>System.err</code>. The first line of
* output contains the result of the {@link #toString()} method for
* this object. Remaining lines represent data previously recorded by
* the method {@link #fillInStackTrace()}. The format of this
* information depends on the implementation, but the following
* example may be regarded as typical:
* <blockquote><pre>
* java.lang.NullPointerException
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
at MyClass.mash(MyClass.java:9)
at MyClass.crunch(MyClass.java:6)
at MyClass.main(MyClass.java:3)
</pre></blockquote>
This example was produced by running the program:
<pre>
class MyClass {
public static void main(String[] args) {
crunch(null);
}
static void crunch(int[] a) {
mash(a);
}
static void mash(int[] b) {
System.out.println(b[0]);
}
}
</pre>
The backtrace for a throwable with an initialized, non-null cause
should generally include the backtrace for the cause. The format
of this information depends on the implementation, but the following
example may be regarded as typical:
<pre>
HighLevelException: MidLevelException: LowLevelException
at Junk.a(Junk.java:13)
at Junk.main(Junk.java:4)
Caused by: MidLevelException: LowLevelException
at Junk.c(Junk.java:23)
at Junk.b(Junk.java:17)
at Junk.a(Junk.java:11)
... 1 more
Caused by: LowLevelException
at Junk.e(Junk.java:30)
at Junk.d(Junk.java:27)
at Junk.c(Junk.java:21)
... 3 more
</pre>
Note the presence of lines containing the characters <tt>"..."</tt>.
These lines indicate that the remainder of the stack trace for this
exception matches the indicated number of frames from the bottom of
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
stack trace of the exception that was caused by this exception (the
"enclosing" exception). This shorthand can greatly reduce the length
of the output in the common case where a wrapped exception is thrown
from same method as the "causative exception" is caught. The above
example was produced by running the program:
<pre>
public class Junk {
public static void main(String args[]) {
try {
a();
} catch(HighLevelException e) {
e.printStackTrace();
}
}
static void a() throws HighLevelException {
try {
b();
} catch(MidLevelException e) {
throw new HighLevelException(e);
}
}
static void b() throws MidLevelException {
c();
the
*
}
*
static void c() throws MidLevelException {
*
try {
*
d();
*
} catch(LowLevelException e) {
*
throw new MidLevelException(e);
*
}
*
}
*
static void d() throws LowLevelException {
*
e();
*
}
*
static void e() throws LowLevelException {
*
throw new LowLevelException();
*
}
* }
*
* class HighLevelException extends Exception {
*
HighLevelException(Throwable cause) { super(cause); }
* }
*
* class MidLevelException extends Exception {
*
MidLevelException(Throwable cause) { super(cause); }
* }
*
* class LowLevelException extends Exception {
* }
* </pre>
*
*/
public void printStackTrace() {
super.printStackTrace();
wrapped.printStackTrace();
}
/** Prints this throwable and its backtrace to the specified print
stream.
*
* @param s <code>PrintStream</code> to use for output
*
*/
public void printStackTrace(PrintStream s) {
super.printStackTrace(s);
wrapped.printStackTrace(s);
}
/** Prints this throwable and its backtrace to the specified
* print writer.
*
* @param s <code>PrintWriter</code> to use for output
* @since
JDK1.1
*
*/
public void printStackTrace(PrintWriter s) {
super.printStackTrace(s);
wrapped.printStackTrace(s);
}
}
_______________________________________________________________
Структура таблицы для DBDerby создается с помощью следующих команд SQL
CREATE DATABASE chapter05_dao;
USE chapter05_dao;
CREATE TABLE employees (
employee_id varchar(50) NOT NULL,
first_name varchar(100) default NULL,
last_name varchar(100) default NULL,
email varchar(100) default NULL,
PRIMARY KEY (employee_id)
) TYPE=MyISAM;
INSERT INTO employees VALUES
(1,'Dave','Glyzewski','dave.glyzewski@centare.com');
INSERT INTO employees VALUES (2,'Ed','Chaltry','ed.chaltry@centare.com');
INSERT INTO employees VALUES (3,'Dustin','Smith','dustin.smith@centare.com');
INSERT INTO employees VALUES (4,'Phil','Jones','phil.jones@centare.com');
INSERT INTO employees VALUES
(5,'Kevin','Williams','kevin.williams@centare.com');
INSERT INTO employees VALUES (6,'Dale','Busse','dale.busse@centare.com');
INSERT INTO employees VALUES (7,'Joe','Bookman','joe.bookman@centare.com');
INSERT INTO employees VALUES
(8,'Sharon','Stillman','sharon.stillman@centare.com');
INSERT INTO employees VALUES (9,'Scott','Knaffla','scott.knaffla@centare.com');
INSERT INTO employees VALUES
(10,'Becky','Glyzewski','becky.glyzewski@centare.com');
INSERT INTO employees VALUES
(11,'Matt','Bumgardner','matt.bumgardner@centare.com');
INSERT INTO employees VALUES (12,'Aimee','Ukasick','aimee.ukasick@centare.com');
INSERT INTO employees VALUES (13,'Rick','Sperko','rick.sperko@centare.com');
INSERT INTO employees VALUES
(14,'John','Henderson','john.henderson@centare.com');
INSERT INTO employees VALUES (15,'Susan','Tomczak','susan.tomczak@centare.com');
INSERT INTO employees VALUES (16,'John','Carnell','john.carnell@centare.com');
INSERT INTO employees VALUES (17,'Andrew','Patzer','andrew.patzer@centare.com');
INSERT INTO employees VALUES (18,'Jay','Marshall','jay.marshall@centare.com');
INSERT INTO employees VALUES
(19,'George','Kowalski','george.kowalski@centare.com');
INSERT INTO employees VALUES (20,'Damon','Payne','damon.payne@centare.com');
Download