Introduction to Java Persistence API
Рубрика: Development, Java | 30 January 2008, 11:10 | Vadim Voituk
Ранее мне не приходилось в полноценной промышленной разработке использовать различные ORM-фреймворки а-ля iBATIS, Hibernate, Toplink etc.
Все мои познания в них ограничивались “поиграться и бросить” Hibernate & iBATIS.
Сейчас же, в предверии перехода на EJB3, жизнь заставила разобраться с JPA – оттуда и “ростут ноги” этой заметки.
Постараюсь продемонстирировать какой простой и понятной становится задача отображения этого класса на таблицы реляционной БД с ипользованием JPA.
При этом, дабы не отвлекаться на конфигурирование клиент-серверного окружения, проведем демонстрацию на обычном J2SE приложении.
Для хранения данных приложения воспользуемся Java-базой данных Apache Derby и JPA-провайдером
Oracle TopLink Essentials
Предположим у нас есть простой привычный POJO-класс представляющий сущность заметки в блог:)
public class BlogPost {
private long id;
private String title;
private String body;
private Date date;
private boolean published;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
// ...тут будут ещё другие методы доступа к полям класса...
}
Добавим к нему немного “магии” JPA-аннотаций:
@Entity // Указываем что класс является сущностью
@Table(name = "post_list") // + и хранится в таблице post_list
public class BlogPost { ... }
Стоит сразу отметить что все аннотации, которые относятся к JPA находятся в пакете javax.persistence.*
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
Далее следует указать, что уникальным ID записи у нас будет свойство “id”, а также, что будем использовать автоматический механизм генерации его значения:
public class BlogPost {
@Id
@GeneratedValue (strategy = GenerationType.AUTO)
private long id;
...
Также спецификация JPA для свойств типа java.util.Date и java.util.Calendar требует указания типа временного поля в БД.
Делается это с помощью аннотации @Temporal:
@Temporal(TemporalType.TIMESTAMP)
private Date date;
Завершаем манипуляции с многострадальным классом и создаем файл META-INF/persistence.xml такого содержания:
<?xml version="1.0" encoding="UTF-8" ?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="TestStore" transaction-type="RESOURCE_LOCAL">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<class>com.voituk.jpaexample.BlogPost</class>
<properties>
<property name="toplink.jdbc.url" value="jdbc:derby:myteststore;create=true" />
<property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="toplink.jdbc.user" value="app" />
<property name="toplink.jdbc.password" value="app" />
<property name="toplink.ddl-generation" value="create-tables" />
<property name="toplink.application-location" value="./db-schema"/>
<property name="toplink.logging.level" value="FINE" />
<property name="toplink.target-database" value="Derby" />
</properties>
</persistence-unit>
</persistence>
Основной конфигурационной единицей JPA является Persistence Unit – в нем описывается тип провайдера, который предоставляет “услуги” JPA а также список классов, за которые он “отвечает” – JPA-сущностей.
В блоке <properties> описываются параметры доступа к базе данных, её тип, настройки логирования etc.
На этом заканчивается вся магия и метапрограммирование, и переходим к написанию кода.
В первую очередь, для удобства инициализации обьектов добавим в класс BlogPost такой конструктор:
public BlogPost(String title, String body, Date date, boolean published) {
this.title = title;
this.body = body;
this.date = date;
this.published = published;
}
А так как JPA требует наличия у каждого класса-сущности конструктора по умолчанию, то добавляем и его:
public BlogPost() { /* Do nothing */ }
Далее программно создадим несколько записей, сохраним их в БД и выведем их на экран.
Управление сущностями (записями) в JPA производится с помощью экземпляра EntityManager-а, предоставляемого фабрикой, которая в свою очередь предоставляется провайдером JPA.
Для получения экземпляра EntityManager воспользуемя кодом:
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
//...
EntityManagerFactory factory = null;
EntityManager manager = null;
try {
factory = Persistence.createEntityManagerFactory("TestStore");
manager = factory.createEntityManager();
//TODO: Add several blogPosts entities to database using EntityManager
//TODO: Read all entities from database using EntityManager
} finally {
if (manager!=null) manager.close();
if (factory!=null) factory.close();
}
где “TestStore” – имя нашего PersistenceUnit-а, описанного в файле META-INF/persistence.xml
Код по добавлению записей в БД с использованием JPA выглядит просто и элегантно:
//Add several blogPosts entities to database using EntityManager
manager.getTransaction().begin();
manager.persist( new BlogPost("BlogPost 1", "This is first blog post", new Date(), true) );
manager.persist( new BlogPost("Just another blog post", "This is second blog post", new Date(), true) );
manager.getTransaction().commit();
Для получения из БД всех записей можно воспользоваться таким кодом:
Query query = manager.createQuery("SELECT obj FROM BlogPost AS obj ORDER BY obj.date DESC");
List list = (List) query.getResultList();
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
BlogPost blogPost = (BlogPost) iterator.next();
System.out.println(blogPost.getId() + " - " + blogPost.getTitle());
}
В результате получим что-то вроде:
302 - Just another blog post
301 - BlogPost 1
При этом хочу сразу отметить, что строка запроса в вызове createQuery – это никак не SQL, а Java Persistence Query Language – более обьектно-ориентированная альтернатива SQL.
В то же время JPA предоставляет возможность задавать запросы и с помощью “старого доброго” SQL.
Если вас смущает обилие технической информации, которую TopLink “выбрасывает” на экран – можно отключить логирование установкой параметра
toplink.logging.level=OFF в файле META-INF/persistence.xml
Надеюсь такой небольшой простой примерчик показал основы JPA.
За более подробной информацией стоит глянуть приведнные ниже ссылки.
Полный исходный код приведенного примера (4,1 Мб)
Ссылки:
Standardizing Java Persistence with the EJB3 Java Persistence API
Understanding JPA
Java Persistence Query Language в Java EE5 Tutorial
Oracle TopLink JPA
How-To use TopLink JPA in Java SE – нюансы по использованию ToplLink в J2SE-среде

> предверии перехода на EJB3
Который год оно уже длится? С лета 2005-го? 2.5 года? А еще ни один коммерческий сервер не ушел в продакшн. Круто!
Если быть точным то финал спеков вышел в мае 2006-го, веблоджик 10 вышел в марте 2007-го и включал EJB 3.0., ну и для вебсферы ejb 3.0 feature pack уже есть какое то время.
Вообще-то я имелл ввиду “переход на EJB3 в своем проекте” :)
Но комментарии выдались полезными :)
Что можете сказать о JBoss Seam, годится он для написания серьезных приложений?
To Boris:
С использованием JBoss Seam пишут серъезные приложения.
Aha, автомат поел угловые скобки. Попробую исправить:
В этом фрагменте кода:
List<blogpost> list = (List<blogpost>) query.getResultList();
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
BlogPost blogPost = (BlogPost) iterator.next();
System.out.println(blogPost.getId() + ” – ” + blogPost.getTitle());
}
</blogpost></blogpost>
очевидно, ошибка: параметр настраиваемого класса спутан с тэгом разметки документа. На мой взгляд, здесь надо либо убрать лишние фрагменты кода:
List list = query.getResultList();
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
BlogPost blogPost = (BlogPost) iterator.next();
System.out.println(blogPost.getId() + ” – ” + blogPost.getTitle());
}
либо переработать его с использованием всех возможностей Java 2 v 5:
List<BlogPost> list = (List<BlogPost>) query.getResultList();
for (BlogPost blogPost: list) {
System.out.println(blogPost.getId() + ” – ” + blogPost.getTitle());
}
Спасибо, было занятно посмотреть. Пошел просматривать свою подборку книг по EJB3
Сергей, самое приятное что JPA можно использовать и без EJB.
Но лично я начинал с ним разбираться именно пользуясь книгой “EJB3 in Action”