SPRING-SOURCE.RU

Доступ к данным в Spring

Существует много статей о том, как делать инъекцию зависимости в Spring, но очень мало толковой информации о подключениях к базам данных. В Spring это можно делать либо напрямую при помощи JDBC либо использовать ORM технологии, например, Hibernate.

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

Начнем наш урок с создания класса:

                        
public class Student {
  protected String name;

  //Getters and setters
		

Это обычный JAVA класс, так называемый POJO. Здесь одно поле и два метода (напишите их сами) setter и getter, которые устанавливают и возвращают что либо.

Теперь перейдем к базе данных. Мы будем использовать базу данных Hypersonic (hsqldb.jar). Прежде всего скачайте из интернета этот файл. Находясь в той же директории, где и hsqldb.jar напишите в командной строке такую команду:

                        
java -cp hsqldb.jar org.hsqldb.Server -database.0 file:data/test -dbname.0 asiavant
		

После выполнения команды у вас должен стартовать сервер. Что в этой команде мы указали? Аргумент file:data/test это путь, где будут созданы файлы, то есть в папке data и имя файлов test. А –dbname.0 asiavant указывает псевдоним базы данных. Псевдоним, который используется для доступа к JDBC при помощи HSQLDB. Цифры нуль указывают индекс базы данных. Индексы могут быть от 0 – 9.

Теперь можно запускать менеджер, который поможет нам создать таблицу в нашей базе:

                        
java -cp hsqldb.jar org.hsqldb.util.DatabaseManager
		

После выполнения этой команды вы увидите окно как на картинке. Добавьте в строку URL слово asiavant – наш алиас. Проверьте остальные параметры и нажимайте кнопку ОК.

Spring hsqldb DatabaseManager

                        
CREATE TABLE STUDENT ( NAME VARCHAR )
		

Когда вы успешно выполните программу, то снова запускайте этот менеджер и наберите команду SELECT * FROM имя таблицы и вы сможете увидеть результаты вашей работы.

Все, таблица создана и менеджер нам пока больше не нужен. Выполняем команду SHUTDOWN COMPACT.

Давайте теперь рассмотрим классический путь сохранения данных.

                        
Student student = new Student();
student.setName("Amitabh");

try{
   Class.forName("org.hsqldb.jdbcDriver");
   }catch(ClassNotFoundException cfe){
       System.out.println("Driver not found");
       System.exit(0);
  }

Connection conn = null;
PreparedStatement stmt = null;
try{
   conn = DriverManager.getConnection ("jdbc:hsqldb:hsql://localhost","sa","");
   stmt = conn.prepareStatement ("insert into STUDENT (name) values (?) ");
   stmt.setString(1, student.getName());
   stmt.execute();
   }catch(SQLException se){
   	 System.out.println("Problem with data insert");
   } finally{
   	try{
           if(stmt != null) {stmt.close();}
	   if(conn != null) {conn.close();}
	   } catch(SQLException se) {}
		

Что здесь происходит:

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

                        
public interface StudentDao {

   public void saveStudent(Student student); }
		

Если мы используем JDBC, то реализация класса будет выглядеть следующим образом:

                        
public class StudentJdbcDao implements StudentDao{

   public void saveStudent(Student student) {
   
   }
}
		

Зачем нужны шаблоны? Роль шаблона – убрать весь повторяющийся код, чтобы разработчик мог сосредоточиться на бизнес логике. Spring обрабатывает доступ к данным, используя шаблоны и callback. Код, такой как открытие и закрытие соединения, выполняется шаблоном, а переменная часть кода, обработка результата, например, обрабатывается callback.

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

На данный момент мы ищем способ взаимодействия с базой через JDBC. Давайте сначала попробуем JdbcTemplate. Этот шаблон автоматически обрабатывает управление ресурсами, обработку исключений и управление транзакциями. Теперь наша реализация класса для JdbcDAO выглядит так:

                        
public class StudentJdbcDao implements StudentDao{

   private JdbcTemplate jdbcTemplate;

   public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate = jdbcTemplate;
   }

   public void saveStudent(Student student) {
        jdbcTemplate.update
		("insert into STUDENT (name) values (?)",new Object[] {student.getName()} );
   }
}
		

Вопрос теперь в том, как получить jdbcTemplate. Сделаем это при помощи Spring.

                        
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<property name="dataSource" ref="dataSource" />
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    destroy-method="close">
  <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
  <property name="url" value="jdbc:hsqldb:hsql://localhost" />
  <property name="username" value="sa" />
  <property name="password" value="" />
</bean>
		

DataSource – это бин, который предоставляет все свойства для соединения к базе данных. Затем мы делаем инъекцию dataSource в jdbcTemplate и затем делаем инъекцию jdbcTemplate в StudentJdbcDao.

                        
<bean id="studentDao" class="StudentJdbcDao">
   <property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
		

Теперь мы можем получить доступ к dao вызвав следующий код:

                        
Student student = new Student();
student.setName("AmitabhDao");
StudentDao studentDao = (StudentDao)appContext.getBean("studentDao");
studentDao.saveStudent(student);
		

Для всех приложений JDBC поддерживает DAO классы и мы должны быть уверены что добавили JdbcTemplate свойство и установили setter метод. Также мы должны быть уверены, что связали JdbcTemplate бин с JdbcTemplate свойством нашего DAO. Это не столь большая трудность для одного DAO, но что делать если у нас их много? Код начнет повторяться, а этого нам не нужно.

Решением может быть создание класса для всех ваших DAO объектов, где JdbcTemplate свойство уже установлено. Затем все наши DAO классы расширяют этот класс и используют JdbcTemplate свойство родительского класса для доступа к данным.

При помощи dao есть возможность сократить код там, где мы делаем инъекцию jdbcTemplate. Если мы воспользуемся этими возможностями, то получим следующий код:

                        
public class StudentJdbcDao extends JdbcDaoSupport implements StudentDao {

   public void saveStudent(Student student) {
	this.getJdbcTemplate().update
		("insert into STUDENT (name) values (?)",
               	  new Object[] {student.getName()} );
  }
}
		

JdbcDaoSupport предоставляет удобный доступ к JdbcTemplate через getJdbcTemplate метод. Когда конфигурируем наш DAO класс в Spring, вы можете напрямую связывать JdbcTemplate бин в jdbcTemplate свойство. После расширения JdbcDaoSupport класса нам больше нет необходимости связывать JdbcTemplate бин с нашим DAO классом. Теперь мы пишем так:

                        
<bean id="studentDao" class="com.dataccesstest.StudentJdbcDao">
	<property name="dataSource" ref="dataSource" />
</bean>
		

Когда StudentJdbcDao имеет свой сконфигурированное dataSource свойство, тогда он внутри себя создаст JdbcTemplate. Это устраняет неоьходимость явно объявлять JdbcTemplate бин в Spring.

Конфигурация Data Sources

Spring предоставляет несколько способов конфигурирования data source:

Ранее мы видели, как работать с соединениями используя свойства соединения, когда мы работали с JDBC напрямую. Spring предоставляет два основных драйвера:

JDBC шаблон

Spring предоставляет три типа класса шаблонов для работы с JDBC:

Каждый класс шаблона поддерживает DaoSupport класс для простого связывания шаблонов. Мы уже видели использование JdbcTemplate. Давайте взглянем как работает NamedParameterJdbcTemplate. В этом случае DAO класс будет выглядеть так:

                        
public class StudentJdbcDao implements StudentDao {

    protected NamedParameterJdbcTemplate namedJdbcTemplate;

    //Method to inject NamedParameterJdbcTemplate   
    public void setNamedJdbcTemplate (NamedParameterJdbcTemplate namedJdbcTemplate) {
	this.namedJdbcTemplate = namedJdbcTemplate;
    }

    public void saveStudent(Student student) {
	Map parameters = new HashMap();
	parameters.put("name",student.getName());
	namedJdbcTemplate.update
		("insert into STUDENT (name) values (:name)",
		  parameters);
	}
...
}
		

Xml будет выглядеть так:

                        
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
	<constructor-arg ref="dataSource" />
</bean>
		

Обратите внимание, что источник данных вводиться через конструктор и SimpleJdbcTemplate будет выглядеть следующим образом:

                        
protected SimpleJdbcTemplate simpleJdbcTemplate;

    //Setter method for SimpleJdbcTemplate
    ...

    public void saveStudent(Student student) {
          //Note how the arguments are passed. It uses varargs feature.
	simpleJdbcTemplate.update
		("insert into STUDENT (name,percentage) values (?,?)",
		 student.getName(),student.getPercentage());
		

XML конфигурация:

                        
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
    <constructor-arg ref="dataSource" />
</bean>
		

Извлечение коллекции

Давайте посмотрим как нам извлекать коллекции из базы данных. Мы будем использовать SimpleJdbcTemplate:

                        
public List<Student> getAllStudents() {
   return simpleJdbcTemplate.query
	("Select name as Name from Student",
         	  new ParameterizedRowMapper<Student>(){
	          public Student mapRow(ResultSet rs,int rowNum)
	          	throws SQLException {
	                Student student = new Student();
		        student.setName(rs.getString("Name"));
		        return student;
		  }
        }
   );
}