Главная » Spring » Работа с шаблонами JDBC Spring

0

Модуль JDBC в Spring освобождает от необходимости управле- ния ресурсами и обработки исключений. Он дает свободу писать только тот код, который необходим для перемещения данных в базу данных и обратно.

Как говорилось выше, в разделе 6.3.1, фреймворк Spring скрывает весь вспомогательный код доступа к данным за классами шаблонов. Для работы с JDBC фреймворк Spring предоставляет три класса ша- блонов, на выбор:

# JdbcTemplate – самый основной шаблон JDBC, этот класс предо-

ставляет простой доступ к базе данных через JDBC и простые запросы с индексированными параметрами;

# NamedParameterJdbcTemplate – этот шаблон JDBC позволяет соз-

давать запросы с именованными параметрами вместо индек- сированных;

#  SimpleJdbcTemplate – эта версия шаблона JDBC использует но-

вые возможности Java 5, такие как автоматическая упаковка и распаковка (autoboxing), шаблонные классы (generics) и спис- ки аргументов переменной длины (varargs) для упрощения ра- боты с шаблоном.

В прошлом приходилось тщательно взвешивать выбор того или иного шаблона JDBC. Но в последних версиях Spring сделать выбор намного проще. В версии Spring 2.5 поддержка именованных парамет- ров из NamedParameterJdbcTemplate была добавлена в SimpleJdbcTemplate. А в версии Spring 3.0 была ликвидирована поддержка старых вер- сий Java (ниже версии Java 5), поэтому выбор шаблона JdbcTemplate не дает никаких преимуществ перед SimpleJdbcTemplate. В свете этих изменений в данной главе мы сосредоточимся исключительно на использовании SimpleJdbcTemplate.

Доступ к данным с использованием SimpleJdbcTemplate

Для выполнения своей работы шаблону SimpleJdbcTemplate необ- ходим только компонент, реализующий интерфейс DataSource. Это делает компонент типа SimpleJdbcTemplate достаточно простым в на- стройке, как показано ниже:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">

<constructor-arg  ref="dataSource"  />

</bean>

Фактически компонентом типа DataSource, на который ссыла- ется свойство dataSource, может быть любая реализация javax.sql. DataSource, включая те, что представлены в разделе 6.2.

Теперь можно внедрить SimleJdbcTemplate в наш объект DAO и ис- пользовать его для доступа к базе данных. Например, предположим, что Spitter DAO основан на SimleJdbcTemplate:

public class JdbcSpitterDAO implements SpitterDAO {

private  SimpleJdbcTemplate  jdbcTemplate;

public  void  setJdbcTemplate(SimpleJdbcTemplate  jdbcTemplate)  { this.jdbcTemplate   =   jdbcTemplate;

}

}

Тогда свойство jdbcTemplate компонента JdbcSpitterDAO можно было бы связать, как показано ниже:

<bean id="spitterDao" class="com.habuma.spitter.persistence.SimpleJdbcTemplateSpitterDao">

<property  name="jdbcTemplate"  ref="jdbcTemplate"  />

</bean>

Передав в распоряжение нашего объекта DAO шаблон Simple- JdbcTemplate, можно существенно упростить метод addSpitter() из листинга 6.1. Новый метод addSpitter(), основанный на применении шаблона SimpleJdbcTemplate, представлен в листинге 6.4.

Листинг 6.4. Метод addSpitter(), основанный на применении шаблона SimpleJdbcTemplate

public   void   addSpitter(Spitter   spitter)  { jdbcTemplate.update(SQL_INSERT_SPITTER,   // Добавить запись

spitter.getUsername(), spitter.getPassword(), spitter.getFullName(), spitter.getEmail(), spitter.isUpdateByEmail());

spitter.setId(queryForIdentity());

}

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

Только потому, что вы не видите здесь шаблонного кода, это не означает, что его нет на самом деле. Он просто грамотно спря- тан внутри класса шаблона. Когда вызывается метод update(), SimpleJdbcTemplate автоматически получает соединение, создает за- прос и выполняет его.

Здесь также не видно, как обрабатываются исключения SQLException. Внутри класс SimpleJdbcTemplate перехватывает все исключения SQLException и преобразует обобщенное исключение SQLException в одно из более конкретных исключений доступа к данным, пере- численных в табл. 6.1, и повторно возбуждает их. Так как в Spring все исключения доступа к данным являются исключениями времени выполнения, их необязательно перехватывать в методе addSpitter().

Чтение данных с использованием SimpleJdbcTemplate также упро- щается. В листинге 6.5 представлена новая версия getSpitterById(), где используются обратные вызовы SimpleJdbcTemplate для отображе- ния возвращаемого набора данных в объекты предметной области.

Листинг 6.5. Выборка данных с помощью SimpleJdbcTemplate

public  Spitter  getSpitterById(long  id)  {

return   jdbcTemplate.queryForObject(             //  Запрос   на   получение   данных SQL_SELECT_SPITTER_BY_ID,

new  ParameterizedRowMapper<Spitter>()  {

public Spitter mapRow(ResultSet rs, int rowNum) throws SQLException {

Spitter  spitter  =  new  Spitter();             // Отображение

spitter.setId(rs.getLong(1));              // результатов spitter.setUsername(rs.getString(2));  // в объект spitter.setPassword(rs.getString(3)); spitter.setFullName(rs.getString(4));

return spitter;

}

},

id                                                                       // Связывание  параметров

);

}

Этот метод getSpitterById() использует для запроса данных метод queryForObject() класса SimpleJdbcTemplate. Метод queryForObject() при- нимает три параметра:

#  значение типа String, содержащее строку SQL-запроса для вы-

борки данных;

# объект ParameterizedRowMapper, извлекающий значения из объ- екта ResultSet и конструирующий объект предметной области (в данном случае – объект Spitter);

# список аргументов переменной длины со значениями для свя-

зывания с индексированными параметрами запроса. Настоящее волшебство творится в объекте ParameterizedRowMapper.

Для каждой строки в наборе данных, возвращаемом запросом,

SimpleJdbcTemplate будет вызывать метод mapRow() объекта RowMapper. Внутри ParameterizedRowMapper мы добавили код, создающий объект Spitter   и заполняющий его свойства значениями из ResultSet.

Подобно методу addSpitter(), метод getSpitterById() не содержит шаблонного кода. В отличие от традиционного использования JDBC, здесь отсутствует код, реализующий управление ресурсами или об- работку исключений. Методы, использующие шаблон SimpleJdbc- Template, сфокусированы исключительно на извлечении объектов Spitter из базы данных.

Использование именованных параметров

Метод addSpitter(), представленный в листинге 6.4, использует индексированные параметры. Это означает необходимость обращать внимание на порядок следования параметров в запросе и при вызове метода update() передавать их значения в таком же порядке. Если позднее потребуется изменить SQL-запрос так, что при этом изме- нится порядок следования параметров, нам также придется привес- ти в соответствие список значений, передаваемых методу.

При желании можно было бы использовать только именован- ные параметры. Они позволяют присвоить имя каждому парамет- ру в SQL-запросе и впоследствии обращаться к параметрам по их именам, реализуя связывание передаваемых значений. Например, рассмотрим запрос SQL_INSERT_SPITTER, объявленный следующим об- разом:

private static final String SQL_INSERT_SPITTER =

"insert into spitter (username, password, fullname) " + "values (:username, :password, :fullname)";

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

Для использования именованных параметров в Spring 2.0 необхо- димо было использовать специальный класс шаблона NamedParameter- JdbcTemplate. А в более ранних версиях Spring это вообще было невоз- можно. Но в версии Spring 2.5 поддержка именованных параметров, реализованная в шаблоне NamedParameterJdbcTemplate, была добавлена в шаблон SimpleJdbcTemplate, поэтому у нас уже имеется все необ- ходимое, чтобы задействовать именованные параметры в методе addSpitter(). В листинге 6.6 приводится новая версия addSpitter(), использующая именованные параметры.

Листинг 6.6. Использование именованных параметров при работе с шаблонами JDBC в Spring

public  void  addSpitter(Spitter  spitter)  {

Map<String, Object> params = new HashMap<String, Object>(); params.put("username", spitter.getUsername()); // Связывание параметров params.put("password",    spitter.getPassword());

params.put("fullname", spitter.getFullName());

jdbcTemplate.update(SQL_INSERT_SPITTER, params); // Вставка spitter.setId(queryForIdentity());

}

Первое, на что следует обратить внимание в этой версии метода addSpitter(), – эта версия получилась немного длиннее предыдущей. Это обусловлено тем, что связывание именованных параметров про-

изводится посредством java.util.Map. Несмотря на это, каждая строка подчинена главной цели – вставке объекта Spitter в базу данных. Здесь также отсутствует код управления ресурсами или обработки исключений, загромождающий главную цель метода.

Использование классов поддержки DAO при работе с JDBC

При создании любых прикладных классов, основанных на JDBC DAO, следует обязательно добавлять свойство типа SimpleJdbcTemplate и соответствующий ему метод записи. А затем связывать компонент SimpleJdbcTemplate со свойством типа SimpleJdbcTemplate каждого объ- екта DAO. Это несложно, когда приложение содержит единствен- ный объект DAO, но при большом их количестве потребуется писать массу повторяющегося кода.

Для решения этой проблемы можно было бы создать общий роди- тельский класс для всех объектов DAO и разместить в нем свойство SimpleJdbcTemplate. После этого во всех классах DAO можно было бы просто наследовать этот класс и использовать для доступа к дан- ным свойство SimpleJdbcTemplate родительского класса. Рисунок 6.4 иллюстрирует предлагаемую схему отношений между прикладными и базовым классами DAO.

Рис. 6.4. Классы поддержки DAO в Spring предусматривают свойство для хранения объектов шаблонов JDBC,

чтобы их подклассам не требовалось создавать собственные экземпляры шаблонов JDBC

Идея создания базового класса DAO, который включал бы в се- бя шаблон JDBC, выглядит весьма привлекательно, особенно если учесть, что фреймворк Spring уже содержит такой базовый класс. В действительности фреймворк содержит три таких класса: JdbcDao- Support, SimpleJdbcDaoSupport и NamedParameterJdbcDaoSupport – по одному на каждый шаблон JDBC в Spring. Чтобы задействовать любой из этих классов поддержки DAO, достаточно наследовать свой класс от него. Например:

public  class  JdbcSpitterDao  extends  SimpleJdbcDaoSupport implements  SpitterDao  {

}

Класс SimpleJdbcDaoSupport обеспечивает удобный доступ к шабло- ну SimpleJdbcTemplate через метод getJdbcTemplate(). Например, метод addSpitter()  можно было бы переписать, как показано ниже:

public   void   addSpitter(Spitter   spitter)   { getSimpleJdbcTemplate().update(SQL_INSERT_SPITTER,

spitter.getUsername(), spitter.getPassword(), spitter.getFullName(), spitter.getEmail(), spitter.isUpdateByEmail());

spitter.setId(queryForIdentity());

}

При настройке класса DAO в Spring можно непосредственно свя- зать компонент SimpleJdbcTemplate со свойством jdbcTemplate, как по- казано ниже:

<bean id="spitterDao" class="com.habuma.spitter.persistence.JdbcSpitterDao">

<property  name="jdbcTemplate"  ref="jdbcTemplate"  />

</bean>

Эта настройка будет работать, но она мало чем отличается от прежней настройки объекта DAO, не наследующего класс Simple- JdbcDaoSupport. При желании можно опустить компонент-посредник и связать источник данных непосредственно со свойством dataSource, которое класс JdbcSpitterDao наследует от класса SimpleJdbcDaoSupport:

<bean id="spitterDao" class="com.habuma.spitter.persistence.JdbcSpitterDao">

<property name="dataSource" ref="dataSource" />

</bean>

При настройке свойства dataSource компонента JdbcSpitterDao не- явно создается экземпляр класса SimpleJdbcTemplate, что избавляет от необходимости явно объявлять компонент SimpleJdbcTemplate в кон- фигурации Spring.

JDBC – это самый низкоуровневый способ доступа к данным в реляционных базах данных. Шаблоны JDBC в Spring избавляют от мучений писать шаблонный код управления ресурсами соединений и обработки исключений, давая возможность сосредоточиться на основной работе по извлечению, сохранению и обновлению данных.

Даже при том, что Spring снимает большую часть головной боли при работе с JDBC, с увеличением размера и сложности приложения этот путь все равно может стать слишком сложным. Чтобы упрос- тить решение проблем доступа к данным в больших приложениях, можно задействовать такие фреймворки доступа к базам данных, как Hibernate. Посмотрим, как задействовать Hibernate в слое хранения данных приложения на основе Spring.

Источник:   Уоллс К., Spring в действии. – М.: ДМК Пресс, 2013. – 752 с.: ил.

По теме:

  • Комментарии