Что такое JPA? Введение в Java Persistence API

В качестве спецификации Java Persistence API касается персистентности , что в широком смысле означает любой механизм, с помощью которого объекты Java переживают процесс приложения, который их создал. Не все объекты Java необходимо сохранять, но большинство приложений сохраняют ключевые бизнес-объекты. Спецификация JPA позволяет вам определять, какие объекты должны сохраняться и как эти объекты должны сохраняться в ваших приложениях Java.

Сама по себе JPA не является инструментом или фреймворком; скорее, он определяет набор концепций, которые могут быть реализованы любым инструментом или фреймворком. Хотя модель объектно-реляционного сопоставления (ORM) JPA изначально была основана на Hibernate, с тех пор она эволюционировала. Точно так же, хотя JPA изначально предназначалась для использования с реляционными базами данных / SQL, некоторые реализации JPA были расширены для использования с хранилищами данных NoSQL. Популярной платформой, поддерживающей JPA с NoSQL, является EclipseLink, эталонная реализация для JPA 2.2.

JPA 2.2 в Джакарте EE

API Java Persistence впервые был выпущен как подмножество спецификации EJB 3.0 (JSR 220) в Java EE 5. С тех пор он превратился в свою собственную спецификацию, начиная с выпуска JPA 2.0 в Java EE 6 (JSR 317). На момент написания этой статьи JPA 2.2 была принята для продолжения как часть Jakarta EE.

JPA и спящий режим

Из-за их взаимосвязанной истории Hibernate и JPA часто объединяют. Однако, как и спецификация Java Servlet, JPA породила множество совместимых инструментов и фреймворков; Hibernate - лишь одна из них.

Hibernate - это ORM-библиотека для Java, разработанная Гэвином Кингом и выпущенная в начале 2002 года. Кинг разработал Hibernate как альтернативу entity-компонентам для обеспечения устойчивости. Фреймворк был настолько популярен и настолько востребован в то время, что многие из его идей были приняты и кодифицированы в первой спецификации JPA.

Сегодня Hibernate ORM - одна из наиболее зрелых реализаций JPA и по-прежнему популярный вариант ORM в Java. Hibernate ORM 5.3.8 (текущая версия на момент написания) реализует JPA 2.2. Кроме того, семейство инструментов Hibernate расширилось и теперь включает популярные инструменты, такие как Hibernate Search, Hibernate Validator и Hibernate OGM, которые поддерживают постоянство модели предметной области для NoSQL.

JPA и EJB

Как отмечалось ранее, JPA была представлена ​​как подмножество EJB 3.0, но с тех пор эволюционировала как собственная спецификация. EJB - это спецификация, сфокусированная не на JPA, и она реализована в контейнере EJB. Каждый контейнер EJB включает уровень сохраняемости, который определяется спецификацией JPA.

Что такое Java ORM?

Хотя они отличаются по исполнению, каждая реализация JPA предоставляет какой-то уровень ORM. Чтобы понимать JPA и JPA-совместимые инструменты, вам нужно хорошо разбираться в ORM.

Объектно-реляционное сопоставление - это задача, которую разработчики не должны выполнять вручную. Фреймворк, такой как Hibernate ORM или EclipseLink, кодифицирует эту задачу в библиотеке или фреймворке, уровне ORM . Как часть архитектуры приложения, уровень ORM отвечает за управление преобразованием программных объектов для взаимодействия с таблицами и столбцами в реляционной базе данных. В Java уровень ORM преобразует классы и объекты Java, чтобы их можно было хранить и управлять ими в реляционной базе данных.

По умолчанию имя сохраняемого объекта становится именем таблицы, а поля становятся столбцами. После настройки таблицы каждая строка таблицы соответствует объекту в приложении. Сопоставление объектов можно настроить, но значения по умолчанию, как правило, работают хорошо.

JPA с NoSQL

До недавнего времени нереляционные базы данных были редкостью. Движение NoSQL изменило все это, и теперь Java-разработчикам доступны различные базы данных NoSQL. Некоторые реализации JPA эволюционировали, чтобы охватить NoSQL, включая Hibernate OGM и EclipseLink.

На рисунке 1 показана роль JPA и уровня ORM в разработке приложений.

JavaWorld /

Настройка уровня Java ORM

Когда вы настраиваете новый проект для использования JPA, вам нужно будет настроить хранилище данных и поставщика JPA. Вы настроите коннектор хранилища данных для подключения к выбранной базе данных (SQL или NoSQL). Вы также включите и настроите поставщика JPA , который представляет собой структуру, такую ​​как Hibernate или EclipseLink. Хотя вы можете настроить JPA вручную, многие разработчики предпочитают использовать готовую поддержку Spring. См. Раздел « Установка и настройка JPA » ниже, чтобы продемонстрировать как ручную, так и основанную на Spring установку и настройку JPA.

Объекты данных Java

Объекты данных Java - это стандартизованная структура сохраняемости, которая отличается от JPA в первую очередь поддержкой логики сохраняемости в объекте и давней поддержкой работы с нереляционными хранилищами данных. JPA и JDO достаточно похожи, поэтому провайдеры JDO часто также поддерживают JPA. См. Проект Apache JDO, чтобы узнать больше о JDO в отношении других стандартов сохранения, таких как JPA и JDBC.

Сохранение данных в Java

С точки зрения программирования уровень ORM является уровнем адаптера : он адаптирует язык графов объектов к языку SQL и реляционных таблиц. Уровень ORM позволяет объектно-ориентированным разработчикам создавать программное обеспечение, которое сохраняет данные, не выходя за рамки объектно-ориентированной парадигмы.

Когда вы используете JPA, вы создаете карту из хранилища данных с объектами модели данных вашего приложения. Вместо определения того, как объекты сохраняются и извлекаются, вы определяете сопоставление между объектами и своей базой данных, а затем вызываете JPA для их сохранения. Если вы используете реляционную базу данных, большая часть фактического соединения между кодом вашего приложения и базой данных будет обрабатываться JDBC, Java Database Connectivity API.

В качестве спецификации JPA предоставляет аннотации метаданных , которые вы используете для определения сопоставления между объектами и базой данных. Каждая реализация JPA предоставляет свой собственный механизм для аннотаций JPA. Спецификация JPA также предоставляет PersistanceManagerили EntityManager, которые являются ключевыми точками контакта с системой JPA (в которых ваш код бизнес-логики сообщает системе, что делать с отображенными объектами).

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

Листинг 1. Простой класс данных в Java.

 public class Musician { private Long id; private String name; private Instrument mainInstrument; private ArrayList performances = new ArrayList(); public Musician( Long id, String name){ /* constructor setters... */ } public void setName(String name){ this.name = name; } public String getName(){ return this.name; } public void setMainInstrument(Instrument instr){ this.instrument = instr; } public Instrument getMainInstrument(){ return this.instrument; } // ...Other getters and setters... } 

MusicianКласс в листинге 1 используется для удержания данных. Он может содержать примитивные данные, такие как поле имени . Он также может поддерживать отношения с другими классами, такими как mainInstrumentи performances.

Musician«S причина бытия должна содержать данные. Этот тип класса иногда называют DTO или объектом передачи данных . DTO - обычная черта разработки программного обеспечения. Хотя они содержат много видов данных, они не содержат никакой бизнес-логики. Сохранение объектов данных - повсеместная проблема при разработке программного обеспечения.

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

Один из способов сохранить экземпляр Musicianкласса в реляционной базе данных - использовать библиотеку JDBC. JDBC - это уровень абстракции, который позволяет приложению выполнять команды SQL, не задумываясь о базовой реализации базы данных.

В листинге 2 показано, как можно сохранить Musicianкласс с помощью JDBC.

Листинг 2. JDBC вставляет запись.

 Musician georgeHarrison = new Musician(0, "George Harrison"); String myDriver = "org.gjt.mm.mysql.Driver"; String myUrl = "jdbc:mysql://localhost/test"; Class.forName(myDriver); Connection conn = DriverManager.getConnection(myUrl, "root", ""); String query = " insert into users (id, name) values (?, ?)"; PreparedStatement preparedStmt = conn.prepareStatement(query); preparedStmt.setInt (1, 0); preparedStmt.setString (2, "George Harrison"); preparedStmt.setString (2, "Rubble"); preparedStmt.execute(); conn.close(); // Error handling removed for brevity 

Код в листинге 2 довольно самодокументируется. georgeHarrisonОбъект может прийти из любого места (передний конец отправленное, внешней службы и т.д.), и установить его ID и имя поля. Затем поля объекта используются для предоставления значений оператора SQL insert. ( PreparedStatementКласс является частью JDBC, предлагая способ безопасного применения значений к SQL-запросу.)

Хотя JDBC позволяет управлять настройкой вручную, он громоздок по сравнению с JPA. Чтобы изменить базу данных, вам сначала необходимо создать запрос SQL, который отображает ваш объект Java в таблицы в реляционной базе данных. Затем вы должны изменять SQL всякий раз, когда изменяется сигнатура объекта. С JDBC поддержка SQL становится задачей сама по себе.

Сохранение данных с помощью JPA

Теперь рассмотрим листинг 3, в котором мы сохраняем Musicianкласс с помощью JPA.

Listing 3. Persisting George Harrison with JPA

 Musician georgeHarrison = new Musician(0, "George Harrison"); musicianManager.save(georgeHarrison); 

Listing 3 replaces the manual SQL from Listing 2 with a single line, session.save(), which instructs JPA to persist the object. From then on, the SQL conversion is handled by the framework, so you never have to leave the object-oriented paradigm.

Metadata annotations in JPA

The magic in Listing 3 is the result of a configuration, which is created using JPA's annotations. Developers use annotations to inform JPA which objects should be persisted, and how they should be persisted.

Listing 4 shows the Musician class with a single JPA annotation.

Listing 4. JPA's @Entity annotation

 @Entity public class Musician { // ..class body } 

Persistent objects are sometimes called entities. Attaching @Entity to a class like Musician informs JPA that this class and its objects should be persisted.

XML vs. annotation-based configuration

JPA also supports using external XML files, instead of annotations, to define class metadata. But why would you do that to yourself?

Configuring JPA

Like most modern frameworks, JPA embraces coding by convention (also known as convention over configuration), in which the framework provides a default configuration based on industry best practices. As one example, a class named Musician would be mapped by default to a database table called Musician.

The conventional configuration is a timesaver, and in many cases it works well enough. It is also possible to customize your JPA configuration. As an example, you could use JPA's @Table annotation to specify the table where the Musician class should be stored.

Listing 5. JPA's @Table annotation

 @Entity @Table(name="musician") public class Musician { // ..class body } 

Listing 5 tells JPA to persist the entity (Musician class) to the musician table.

Primary key

In JPA, the primary key is the field used to uniquely identify each object in the database. The primary key is useful for referencing and relating objects to other entities. Whenever you store an object in a table, you will also specify the field to use as its primary key.

In Listing 6, we tell JPA what field to use as Musician's primary key.

Listing 6. Specifying the primary key

 @Entity public class Musician { @Id private Long id; 

In this case, we've used JPA's @Id annotation to specify the id field as Musician's primary key. By default, this configuration assumes the primary key will be set by the database--for instance, when the field is set to auto-increment on the table.

JPA supports other strategies for generating an object's primary key. It also has annotations for changing individual field names. In general, JPA is flexible enough to adapt to any persistence mapping you might need.

CRUD operations

Once you've mapped a class to a database table and established its primary key, you have everything you need to create, retrieve, delete, and update that class in the database. Calling session.save() will create or update the specified class, depending on whether the primary-key field is null or applies to en existing entity. Calling entityManager.remove() will delete the specified class.

Entity relationships in JPA

Simply persisting an object with a primitive field is only half the equation. JPA also has the capability to manage entities in relation to one another. Four kinds of entity relationships are possible in both tables and objects:

    1. One-to-many
    2. Many-to-one
    3. Many-to-many
    4. One-to-one

Each type of relationship describes how an entity relates to other entities. For example, the Musician entity could have a one-to-many relationship with Performance, an entity represented by a collection such as List or Set.

If the Musician included a Band field, the relationship between these entities could be many-to-one, implying collection of Musicians on the single Band class. (Assuming each musician only performs in a single band.)

If Musician included a BandMates field, that could represent a many-to-many relationship with other Musician entities.

Наконец, Musicianможет иметь отношение один к одному с Quoteобъектом, который используется для представления известной цитаты: Quote famousQuote = new Quote().

Определение типов отношений

JPA имеет аннотации для каждого из своих типов сопоставления отношений. В листинге 7 показано, как можно аннотировать отношение «один ко многим» между Musicianи Performance.

Листинг 7. Аннотирование отношения "один ко многим"