RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Глава 14. Connector/J и Spring

Spring Framework это Java-окружение, разработанное для помощи в проектировании приложений, обеспечивая способ формировать компоненты. Техника, используемая в Spring, является известным шаблоном разработки под названием Dependency Injection (см. Inversion of Control Containers and the Dependency Injection pattern). Эта статья сосредоточится на ориентированном на Java доступе к базам данных MySQL с Spring 2.0. Есть также .NET-порт Spring под названием Spring.NET.

Spring не только система для формирования компонентов, но также включает поддержку аспектно-ориентированного программирования (AOP). Это одни из главных преимуществ для управления ресурсами Spring и управления транзакциями. Spring также обеспечивает утилиты для интеграции управления ресурсами с JDBC и Hibernate.

Для примеров в этой секции будет использоваться база данных-образец world. Первая задача состоит в том, чтобы настроить источник данных MySQL из Spring. Компоненты в Spring используют терминологию bean. Например, чтобы формировать связь с сервером MySQL, поддерживающим базу данных-образец, вы могли бы использовать:

<util:map id="dbProps">
  <entry key="db.driver" value="com.mysql.cj.jdbc.Driver"/>
  <entry key="db.jdbcurl" value="jdbc:mysql://localhost/world"/>
  <entry key="db.username" value="myuser"/>
  <entry key="db.password" value="mypass"/>
</util:map>

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

<bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="${db.driver}"/>
  <property name="url" value="${db.jdbcurl}"/>
  <property name="username" value="${db.username}"/>
  <property name="password" value="${db.password}"/>
</bean>

Заполнители используются, чтобы предоставить значения для свойств этого блока. Это означает, что мы можем определить все свойства конфигурации в одном месте вместо того, чтобы задавать значения для каждого свойства. Нам действительно, однако, нужен еще один блок, чтобы собрать все это вместе. Последний блок ответственен за то, чтобы заменить заполнители значениями свойств.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="properties" ref="dbProps"/>
</bean>

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

// Create a new application context. this processes the Spring config
ApplicationContext ctx = new ClassPathXmlApplicationContext("ex1appContext.xml");
// Retrieve the data source from the application context
DataSource ds = (DataSource) ctx.getBean("dataSource");
// Open a database connection using Spring's DataSourceUtils
Connection c = DataSourceUtils.getConnection(ds);
try {
  // retrieve a list of three random cities
  PreparedStatement ps = c.prepareStatement(
    "select City.Name as 'City', Country.Name as 'Country' " +
    "from City inner join Country on City.CountryCode = Country.Code " +
    "order by rand() limit 3");
  ResultSet rs = ps.executeQuery();
  while(rs.next()) {
    String city = rs.getString("City");
    String country = rs.getString("Country");
    System.out.printf("The city %s is in %s%n", city, country);
  }
} catch (SQLException ex) {
  // something has failed and we print a stack trace to analyse the error
  ex.printStackTrace();
  // ignore failure closing connection
  try { c.close(); } catch (SQLException e) { }
} finally {
  // properly release our connection
  DataSourceUtils.releaseConnection(c, ds);
}

Это очень похоже на нормальный доступ JDBC к MySQL с основным различием в том, что мы используем DataSourceUtils вместо DriverManager, чтобы создать связь.

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

14.1. Использование JdbcTemplate

Spring широко применяет шаблон разработки метода Template (см. Template Method Pattern). Нашим непосредственным центром будет JdbcTemplate и связанные классы, определенно NamedParameterJdbcTemplate. Получение обработчика шаблонных классов и наличие связи для доступа к данным, когда это необходимо.

Следующий пример показывает, как использовать NamedParameterJdbcTemplate в классе DAO (Data Access Object), чтобы получить случайный город по коду страны.

public class Ex2JdbcDao {
  /**
  * Data source reference which will be provided by Spring.
  */
  private DataSource dataSource;

  /**
  * Our query to find a random city given a country code. Notice
  * the ":country" parameter toward the end. This is called a
  * named parameter.
  */
  private String queryString = "select Name from City " +
          "where CountryCode = :country order by rand() limit 1";

  /**
  * Retrieve a random city using Spring JDBC access classes.
  */
  public String getRandomCityByCountryCode(String cntryCode) {
    // A template that permits using queries with named parameters
    NamedParameterJdbcTemplate template =
      new NamedParameterJdbcTemplate(dataSource);
    // A java.util.Map is used to provide values for the parameters
    Map params = new HashMap();
    params.put("country", cntryCode);
    // We query for an Object and specify what class we are expecting
    return (String)template.queryForObject(queryString, params, String.class);
  }
  /**
  * A JavaBean setter-style method to allow Spring to inject the data source.
  * @param dataSource
  */
  public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
  }
}

Центром в вышеупомянутом коде является метод getRandomCityByCountryCode(). Мы передаем код страны и используем NamedParameterJdbcTemplate для запроса города. Код страны помещается в Map с ключем "country", который является параметром, названным в SQL-запросе.

Чтобы получить доступ к этому коду, необходимо формировать его в Spring, обеспечивая ссылку на источник данных.

<bean id="dao" class="code.Ex2JdbcDao">
  <property name="dataSource" ref="dataSource"/>
</bean>

В этом пункте мы можем просто захватить ссылку на DAO из Spring и вызвать getRandomCityByCountryCode().

// Create the application context
  ApplicationContext ctx =
    new ClassPathXmlApplicationContext("ex2appContext.xml");
  // Obtain a reference to our DAO
  Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao");
  String countryCode = "USA";
  // Find a few random cities in the US
  for (int i = 0; i < 4; ++i)
    System.out.printf("A random city in %s is %s%n", countryCode,
                      dao.getRandomCityByCountryCode(countryCode));

Этот пример показывает, как использовать классы Spring JDBC для абстрактного использования традиционных классов JDBC, включая Connection и PreparedStatement.

14.2. Транзакционный доступ JDBC

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

Чтобы использовать транзакционный доступ к базе данных, мы должны будем изменить механизм хранения таблиц в базе данных. Загруженный скрипт явно составляет таблицы MyISAM, которые не поддерживают транзакционную семантику. Механизм хранения InnoDB действительно поддерживает транзакции и это то, что мы будем использовать. Мы можем изменить механизм хранения следующими запросами.

ALTER TABLE City ENGINE=InnoDB;
ALTER TABLE Country ENGINE=InnoDB;
ALTER TABLE CountryLanguage ENGINE=InnoDB;

Хорошая практика программирования в Spring отделяет интерфейсы и реализации. Это означает, что мы можем создать интерфейс Java и только использовать операции в этом интерфейсе без любого внутреннего ведома того, какова фактическая реализация. Мы позволим Spring работать с внедрением, и это будет управлять транзакциями для нашего внедрения.

Сначала вы создаете простой интерфейс:

public interface Ex3Dao {
  Integer createCity(String name, String countryCode,
                     String district, Integer population);
}

Этот интерфейс содержит один метод, который создаст новую запись города в базе данных и возвратит идентификатор новой записи. Затем необходимо создать реализацию этого интерфейса.

public class Ex3DaoImpl implements Ex3Dao {
  protected DataSource dataSource;
  protected SqlUpdate updateQuery;
  protected SqlFunction idQuery;

  public Integer createCity(String name, String countryCode,
                            String district, Integer population)
  {
    updateQuery.update(new Object[]
                {name, countryCode, district, population});
    return getLastId();
  }
  protected Integer getLastId() {
    return idQuery.run();
  }
}

Вы видите, что мы только воздействуем на абстрактные объекты запроса здесь и не имеем дело непосредственно с API JDBC. Кроме того, это полноценная реализация. Со всем нашим управлением транзакциями будет иметь дело конфигурация. Чтобы начать конфигурацию, мы должны создать DAO.

<bean id="dao" class="code.Ex3DaoImpl">
  <property name="dataSource" ref="dataSource"/>
  <property name="updateQuery">...</property>
  <property name="idQuery">...</property>
</bean>

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

<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

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

<aop:config>
  <aop:pointcut id="daoMethods"
    expression="execution(* code.Ex3Dao.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/>
</aop:config>

Это в основном говорит что все методы, к которым обращается интерфейс Ex3Dao, обернуты в транзакцию. Чтобы использовать это, мы только должны получить dao от прикладного контекста и обращатся к методу в экземпляре dao.

Ex3Dao dao = (Ex3Dao) ctx.getBean("dao");
Integer id = dao.createCity(name,countryCode, district, pop);

Мы можем проверить, что нет никакого управления транзакциями, происходящего в нашем коде Java, и все формируется с Spring. Это очень сильное понятие, рассматриваемое как одна из самых выгодных особенностей Spring.

14.3. Объединение связи в Spring

Во многих ситуациях, таких как веб-приложения, будет большое количество маленьких транзакций базы данных. Когда дело обстоит так, обычно имеет смысл создавать фонд соединений с базой данных, доступных для веб-запросов по мере необходимости. Хотя MySQL не порождает дополнительный процесс, когда связь установлена, есть все еще небольшое количество издержек, чтобы создать и настроить связь. Объединение связей также облегчает такие проблемы, как работа большого числа сокетов в состоянии TIME_WAIT .

Подготовка объединения подключений с Spring так же проста, как изменение конфигурации источника данных в прикладном контексте. Есть много конфигураций, которые мы можем использовать. Первый пример основан на Jakarta Commons DBCP library. Пример ниже заменяет исходную конфигурацию, которая была на основе DriverManagerDataSource с DBCP's BasicDataSource.

<bean id="dataSource" destroy-method="close"
  class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="${db.driver}"/>
  <property name="url" value="${db.jdbcurl}"/>
  <property name="username" value="${db.username}"/>
  <property name="password" value="${db.password}"/>
  <property name="initialSize" value="3"/>
</bean>

Конфигурация этих двух решений очень похожа. Различие в том, что DBCP объединит связи с базой данных вместо того, чтобы создать новую связь каждый раз, когда она нужна. Мы также установили параметр, здесь названный initialSize. Это говорит DBCP, что мы хотим три связи в пуле, когда он создается.

Другой способ формировать объединение связи состоит в том, чтобы формировать источник данных в нашем сервере приложений J2EE. Используя JBoss как пример, можно настроить фонд подключений MySQL создав файл, названный mysql-local-ds.xml и разместив его в каталоге server/default/deploy в JBoss. Как только мы это настроили, мы можем использовать JNDI, чтобы искать его. С Spring этот поиск очень прост. Конфигурация источника данных похожа на это:

<jee:jndi-lookup id="dataSource" jndi-name="java:MySQL_DS"/>

Поиск

 

Найди своих коллег!

Вы можете направить письмо администратору этой странички, Алексею Паутову. mailto:alexey.v.pautov@mail.ru