SPRING-SOURCE.RU
  1. JPA сохраняемость

JPA сохраняемость

Хранение данных в объектно-реляционных системах называется persistence (сохранение). Когда объект данных сохраняется в базу данных, значит объект считается сохраненным (persisted).

В JPA используется революционный подход для сохранения объекта, выбрасывая тяжелый Java EE контейнер за уравнение. Также JPA может хорошо работать с остальной частью Java EE 5 движка.

До прихода JPA, Spring фреймворк поддерживал различные persistence механизмы. Он поддерживал прямое использование JDBC, реализацию доступа к данным объекта (DAO), и различные ORM технологии.

Эта глава вводит вас во множество средств доступа к данным и поддержку persistence. Вы увидите как Spring использует DAO, шаблоны и другое. Самое главное, что вы узнаете о JPA и как Spring позволяет сделать persistence для любого POJO компонента с почти не специфичного для базы данных API программирования.

Практические упражнения в этой главе позволят сделать доступным persistence для доменной модели объектов PIX. Вы получите знания о том как хранить, получать, и изменять PIX POJO в и из реляционных баз данных.

В этой главе вы узнаете:

JDBC архитектура

Spring JDBC архитектура

JDBC API - это низкоуровневый API, который взаимодействует с реляционными базами данных. Каждый поставщик РБД предоставляет драйвер совместимый с JDBC. Драйвер напрямую взаимодействует с базой данных, как показано на диаграмме.

Диаграмма иллюстрирует, как разработчики могут писать свой доступ к базе с использованием JDBC API. Эта API реализация построена на Java SE платформе и не требует установки другого вида. Доступ к базе от различных поставщиков, требует наличие драйвера. JDBC драйвер уже загружен в большинство RDBMS. Все новые РБД движки предоставляют интерфейсы к своей функциональности через SQL и JDBC операции основаны в первую очередь на отправке SQL запросов к РДБ и затем извлечения результата. В сущности, когда пишем JDBC код, вы должны создавать SQL запросы, выполнять их на сервере, и затем работать с табличным результатом, который вернул сервер.

Второй рисунок показывает, как JDBC код может сохранять данные объекта в РБД. Вы должны пройти дерево сохраняемых объектов и для каждого такого объекта вы должны сохранять каждое значение поля соответственно полям в таблице БД. Код должен создавать и отправлять SQL запрос в РБД для осуществления операции. Сопоставление между объектами и РБД делается вручную и все взаимосвязи должны жестко кодироваться в JDBC логике доступа.

Spring JDBC сохранение данных

Традиционный JDBC подход

JDBC API дает программисту прямой контроль над доступом к БД и управлению кэшем. Но есть некоторые характеристики, которые делают его довольно утомительным в использовании:

  1. 1.Громоздкие связи и обработка ресурсов. Разработчик должен иметь дело с операциями такими как, открытие и закрытие соединения и try-catch-finally блоками для commit и rollback в транзакциях.
  2. 1.Процедурная сущность не поддерживает Java объектов. JDBC API не поддерживает сохранение Java объектов напрямую. Этот процедурный API не поддается простому объектно-ориентированному сохранению. В действительности, вы должны обладать большими знаниями в реляционной модели когда программируете JDBC. Вам необходимо сопоставить ваше дерево объектов в приложении сперва в таблицы, а затем в строки и столбцы.
  3. 1.Нет иерархии исключений. JDBC использует малоинформативный SQLException для всех исключений, что достаточно сложно интерпретировать. Следующий пример показывает стандартную ошибку:
  4.                         
    SQLException java.sql.BatchUpdateException: DB2-008: unique constraint
    (DB2_CS003) violated
    		

    Такое исключение следует интерпретировать на основе кода ошибки конкретного производителя БД.

Следующий код демонстрирует пример JDBC для сохранения одного класса домена PIX. Этот код повторяющийся и объемный. Это также иллюстрирует недостатки изложенные ранее.

                        
package com.wrox.beginspring.pix.dao.examples;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.wrox.beginspring.pix.model.Album;

/*
 * <pre>
 * The album database named pix is created.
 * The album managment tables along with sequence tables
 * ALBUM_SEQUENCE and PICTURE_SEQUENCE is created 
 * <pre>
 */
public class AlbumDao {

    public void insertAlbum(Album album) {
        Connection con = null;
        PreparedStatement stmt = null;
        try {
            con = getConnection();
            con.setAutoCommit(false);

            System.out.println("Persisting User = "
                    + album.getUser().getUserName());

            // Create User
            stmt = con
                    .prepareStatement("INSERT INTO PIXUSER (USERNAME,FIRSTNAME,LASTNAME,"
                            + "EMAIL,PASSWORD)" + "values (?, ?,?,?,?)");
            stmt.setString(1, album.getUser().getUserName());
            stmt.setString(2, album.getUser().getFirstName());
            stmt.setString(3, album.getUser().getLastName());
            stmt.setString(4, album.getUser().getEmail());
            stmt.setString(5, album.getUser().getPassword());
            stmt.execute();

            System.out.println("Persisting User");
            System.out.println("User = " + album.getName());

            // Get Album sequence.
            stmt = con
                    .prepareStatement("insert into album_sequence values (null)");
            stmt.execute();
            stmt = con
                    .prepareStatement("select max(identity()) from album_sequence");
            ResultSet rs = stmt.executeQuery();
            if (rs.next()) {
                // Retrieve the auto generated key i.e movie_id.
                album.setId(rs.getInt(1));
            }

            stmt = con
                    .prepareStatement("INSERT INTO ALBUM (ID,DTYPE,NAME, DESCRIPTION,USER_USERNAME)"
                            + "values (?,?,?, ?,?)");
            stmt.setInt(1, album.getId());
            stmt.setString(2, "H");
            stmt.setString(3, album.getName());
            stmt.setString(4, album.getDescription());
            stmt.setString(5, album.getUser().getUserName());
            stmt.execute();

            System.out.println("Persisting Album");
            System.out.println("Album id generated = " + album.getId());

            int count = album.getPictures().size();

            // Insert Picture information and get the picture id auto generated
            // key.

            for (int i = 0; i < count; i++) {

                // Get Picture sequence.
                stmt = con
                        .prepareStatement("INSERT INTO PICTURE_SEQUENCE VALUES(null)");
                stmt.execute();

                stmt = con
                        .prepareStatement("select max(identity()) from picture_sequence");
                rs = stmt.executeQuery();
                if (rs.next()) {
                    // Retrieve the auto generated key i.e movie_id.
                    album.getPictures().get(i).setId(rs.getInt(1));
                }

                stmt = con
                        .prepareStatement("INSERT INTO PICTURE (ID,NAME,SIZE)"
                                + "values (?,?, ?)");

                // Set picture name and size.
                stmt.setInt(1, album.getPictures().get(i).getId());
                stmt.setString(2, album.getPictures().get(i).getName());
                stmt.setFloat(3, album.getPictures().get(i).getSize());
                stmt.execute();

                System.out.println("Picture id generated = "
                        + album.getPictures().get(i).getId());

            }

            // Associate album and picture.

            System.out.println("Linking album and picture");

            for (int i = 0; i < count; i++) {

                stmt = con
                        .prepareStatement("INSERT INTO ALBUM_PICTURE (ALBUM_ID,PICTURES_ID)"
                                + "values (?, ?)");

                // Set album id
                stmt.setInt(1, album.getId());
                stmt.setInt(2, album.getPictures().get(i).getId());
                stmt.execute();

            }

            // Commit transaction
            con.commit();

            System.out.println("Insert successful");

        } catch (SQLException e) {
            e.printStackTrace();
            try {
                con.rollback();
            } catch (SQLException e1) {

                e1.printStackTrace();
            }
            // throw Exception
        } finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (con != null) {
                    con.close();
                }
            } catch (SQLException e1) {
                // Log error.
            }
        }
    }

    private Connection getConnection() throws SQLException {
        // Take from connection pool or from DriverManager
        DriverManager.registerDriver(new org.hsqldb.jdbcDriver());
        //If not using default setup; change the URL to point to pix database.
        return DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/pix", "sa", "");
    }
}