JPA 및 Hibernate를 사용한 Java 지속성, Part 1 : 엔티티 및 관계

JPA (Java Persistence API)는 관계형 데이터베이스와 객체 지향 프로그래밍 간의 격차를 해소하는 Java 사양입니다. 2 부로 구성된이 튜토리얼에서는 JPA를 소개하고 Java 객체가 JPA 엔티티로 모델링되는 방법, 엔티티 관계가 정의되는 방법 및 EntityManagerJava 애플리케이션에서 저장소 패턴과 함께 JPA를 사용하는 방법을 설명 합니다.

이 튜토리얼은 Hibernate를 JPA 제공자로 사용합니다. 대부분의 개념은 다른 Java 지속성 프레임 워크로 확장 될 수 있습니다.

JPA 란 무엇입니까?

EJB 3.0을 포함한 JPA 및 관련 프레임 워크의 발전에 대해 알아 보려면 "JPA 란 무엇입니까? Java Persistence API 소개"를 참조하십시오. 및 JDBC.

JPA의 객체 관계

관계형 데이터베이스는 1970 년대부터 프로그램 데이터를 저장하는 수단으로 존재했습니다. 오늘날 개발자는 관계형 데이터베이스에 대한 많은 대안을 가지고 있지만 이러한 유형의 데이터베이스는 확장 가능하고 잘 이해되며 여전히 소규모 및 대규모 소프트웨어 개발에 널리 사용됩니다.

관계형 데이터베이스 컨텍스트의 Java 객체는 엔티티 로 정의됩니다 . 엔티티는 열과 행을 차지하는 테이블에 배치됩니다. 프로그래머는 외래 키조인 테이블 을 사용하여 엔터티 간의 관계, 즉 일대일, 일대 다 및 다 대다 관계를 정의합니다. SQL (Structured Query Language)을 사용하여 외래 키 제약 조건을 사용하여 개별 테이블과 여러 테이블에서 데이터를 검색하고 상호 작용할 수도 있습니다. 관계형 모델은 단순하지만 개발자는 쿼리를 작성하여 데이터를 검색하고 해당 데이터에서 개체를 구성 할 수 있습니다.

물체 관계 임피던스 불일치

관계형 데이터베이스에 데이터 개체를 매핑하는 문제를 나타내는 개체 관계 임피던스 불일치 라는 용어에 익숙 할 수 있습니다 . 이러한 불일치는 객체 지향 설계가 일대일, 일대 다 및 다 대다 관계로 제한되지 않기 때문에 발생합니다. 대신 객체 지향 디자인에서 우리는 객체, 그 속성 및 동작, 객체의 관계를 생각합니다. 두 가지 예는 캡슐화와 상속입니다.

  • 객체가 다른 객체를 포함하는 경우, 우리는 통해 정의 캡슐화 --a 가-A 관계를.
  • 물체가 다른 물체의 특성화의 경우, 우리는를 통해 정의 상속 --an 이다-A 관계.

연관, 집계, 구성, 추상화, 일반화, 실현 및 종속성은 모두 관계형 모델에 매핑하기 어려울 수있는 객체 지향 프로그래밍 개념입니다.

ORM : 객체 관계형 매핑

객체 지향 설계와 관계형 데이터베이스 모델링 간의 불일치로 인해 ORM (객체 관계형 매핑)을 위해 특별히 개발 된 도구 클래스가 개발되었습니다. Hibernate, EclipseLink 및 iBatis와 같은 ORM 도구는 엔티티 및 해당 관계를 포함한 관계형 데이터베이스 모델을 객체 지향 모델로 변환합니다. 이러한 도구 중 상당수는 JPA 사양 이전에 존재했지만 표준 없이는 기능이 공급 업체에 따라 다릅니다.

2006 년 EJB 3.0의 일부로 처음 출시 된 JPA (Java Persistence API)는 객체에 주석을 달 수있는 표준 방식을 제공하여 관계형 데이터베이스에 매핑 및 저장할 수 있습니다. 사양은 또한 데이터베이스와 상호 작용하기위한 공통 구조를 정의합니다. Java 용 ORM 표준을 사용하면 공급 업체 구현에 일관성을 제공하는 동시에 유연성과 추가 기능도 허용합니다. 예를 들어 원래 JPA 사양은 관계형 데이터베이스에 적용 할 수 있지만 일부 공급 업체 구현은 NoSQL 데이터베이스와 함께 사용할 수 있도록 JPA를 확장했습니다.

JPA의 진화

JPA의 첫 번째 릴리스 인 버전 1.0은 2006 년에 JCP (Java Community Process)를 통해 JSR (Java Specification Request) 220으로 게시되었습니다. 버전 2.0 (JSR 317)은 2009 년에, 버전 2.1 (JSR 338)은 2013 년에 게시되었습니다. 버전 2.2 (JSR 338의 유지 관리 릴리스)는 2017 년에 발표되었습니다. JPA 2.2는 Jakarta EE에 포함 및 지속적인 개발을 위해 선택되었습니다.

JPA 시작하기

Java Persistence API는 구현이 아니라 사양입니다. ORM 제품과 상호 작용하기 위해 코드에서 사용할 수있는 공통 추상화를 정의합니다. 이 섹션에서는 JPA 사양의 몇 가지 중요한 부분을 검토합니다.

다음 방법을 배우게됩니다.

  • 데이터베이스에서 엔터티, 필드 및 기본 키를 정의합니다.
  • 데이터베이스의 엔터티 간의 관계를 만듭니다.
  • EntityManager및 그 방법으로 작업하십시오 .

엔티티 정의

엔티티를 정의하려면 @Entity어노테이션 이있는 클래스를 작성해야합니다 . @Entity주석은입니다 마커 주석 지속적 엔티티를 발견하는 데 사용됩니다. 예를 들어, 책 엔티티를 생성하려면 다음과 같이 주석을 추가합니다.

 @Entity public class Book { ... } 

기본적으로이 엔티티는 Book주어진 클래스 이름에 의해 결정된대로 테이블에 매핑됩니다 . 이 엔티티를 다른 테이블 (및 선택적으로 특정 스키마)에 매핑하려는 경우 @Table주석을 사용하여 수행 할 수 있습니다 . Book클래스를 BOOKS 테이블에 매핑하는 방법은 다음과 같습니다 .

 @Entity @Table(name="BOOKS") public class Book { ... } 

BOOKS 테이블이 PUBLISHING 스키마에있는 경우 @Table주석에 스키마를 추가 할 수 있습니다 .

 @Table(name="BOOKS", schema="PUBLISHING") 

필드를 열에 매핑

테이블에 매핑 된 엔터티를 사용하여 다음 작업은 해당 필드를 정의하는 것입니다. 필드 는 각 필드의 이름이 테이블의 열 이름에 매핑되는 클래스의 멤버 변수로 정의됩니다. 다음 @Column과 같이 주석 을 사용하여이 기본 매핑을 재정의 할 수 있습니다 .

 @Entity @Table(name="BOOKS") public class Book { private String name; @Column(name="ISBN_NUMBER") private String isbn; ... } 

이 예에서는 name특성에 대한 기본 매핑을 수락 했지만 특성에 대한 사용자 지정 매핑을 지정했습니다 isbn. name속성은 매핑됩니다 이름 열하지만, isbn속성은 ISBN_NUMBER 컬럼에 매핑됩니다.

@Column주석은 그것이 등 (그것이 진수 값의 경우)이 삽입 및 업데이트 할 수 있는지, 그 정밀도와 스케일 고유하고해야하는지, 널 (NULL)인지, 길이를 포함하여 필드 / 컬럼의 추가 속성을 정의 할 수있게 해준다 .

기본 키 지정

관계형 데이터베이스 테이블에 대한 요구 사항 중 하나는 기본 키 또는 데이터베이스의 특정 행을 고유하게 식별하는 키를 포함해야한다는 것 입니다. JPA에서는 @Id주석을 사용하여 필드를 테이블의 기본 키로 지정합니다. 기본 키는 Java 기본 유형, Integer또는 Long, a String, a Date, a BigInteger또는 a 와 같은 기본 랩퍼 여야 합니다 BigDecimal.

In this example, we map the id attribute, which is an Integer, to the ID column in the BOOKS table:

 @Entity @Table(name="BOOKS") public class Book { @Id private Integer id; private String name; @Column(name="ISBN_NUMBER") private String isbn; ... } 

It is also possible to combine the @Id annotation with the @Column annotation to overwrite the primary key's column-name mapping.

Relationships between entities

Now that you know how to define an entity, let's look at how to create relationships between entities. JPA defines four annotations for defining entities:

  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany

One-to-one relationships

The @OneToOne annotation is used to define a one-to-one relationship between two entities. For example, you may have a User entity that contains a user's name, email, and password, but you may want to maintain additional information about a user (such as age, gender, and favorite color) in a separate UserProfile entity. The @OneToOne annotation facilitates breaking down your data and entities this way.

The User class below has a single UserProfile instance. The UserProfile maps to a single User instance.

 @Entity public class User { @Id private Integer id; private String email; private String name; private String password; @OneToOne(mappedBy="user") private UserProfile profile; ... } 
 @Entity public class UserProfile { @Id private Integer id; private int age; private String gender; private String favoriteColor; @OneToOne private User user; ... } 

The JPA provider uses UserProfile's user field to map UserProfile to User. The mapping is specified in the mappedBy attribute in the @OneToOne annotation.

One-to-many and many-to-one relationships

The @OneToMany and @ManyToOne annotations facilitate both sides of the same relationship. Consider an example where a Book can have only one Author, but an Author may have many books. The Book entity would define a @ManyToOne relationship with Author and the Author entity would define a @OneToMany relationship with Book.

 @Entity public class Book { @Id private Integer id; private String name; @ManyToOne @JoinColumn(name="AUTHOR_ID") private Author author; ... } 
 @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @OneToMany(mappedBy = "author") private List books = new ArrayList(); ... } 

In this case, the Author class maintains a list of all of the books written by that author and the Book class maintains a reference to its single author. Additionally, the @JoinColumn specifies the name of the column in the Book table to store the ID of the Author.

Many-to-many relationships

Finally, the @ManyToMany annotation facilitates a many-to-many relationship between entities. Here's a case where a Book entity has multiple Authors:

 @Entity public class Book { @Id private Integer id; private String name; @ManyToMany @JoinTable(name="BOOK_AUTHORS", [email protected](name="BOOK_ID"), [email protected](name="AUTHOR_ID")) private Set authors = new HashSet(); ... } 
 @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @ManyToMany(mappedBy = "author") private Set books = new HashSet(); ... } 

In this example, we create a new table, BOOK_AUTHORS, with two columns: BOOK_ID and AUTHOR_ID. Using the joinColumns and inverseJoinColumns attributes tells your JPA framework how to map these classes in a many-to-many relationship. The @ManyToMany annotation in the Author class references the field in the Book class that manages the relationship; namely the authors property.

That's a quick demo for a fairly complex topic. We'll dive further into the @JoinTable and @JoinColumn annotations in the next article.

Working with the EntityManager

EntityManager is the class that performs database interactions in JPA. It is initialized through a configuration file named persistence.xml. This file is found in the META-INF folder in your CLASSPATH, which is typically packaged in your JAR or WAR file. The persistence.xml file contains:

  • The named "persistence unit," which specifies the persistence framework you're using, such as Hibernate or EclipseLink.
  • A collection of properties specifying how to connect to your database, as well as any customizations in the persistence framework.
  • A list of entity classes in your project.

Let's look at an example.

Configuring the EntityManager

First, we create an EntityManager using the EntityManagerFactory retrieved from the Persistence class:

 EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Books"); EntityManager entityManager = entityManagerFactory.createEntityManager(); 

In this case we've created an EntityManager that is connected to the "Books" persistence unit, which we've configured in the persistence.xml file.

The EntityManager class defines how our software will interact with the database through JPA entities. Here are some of the methods used by EntityManager:

  • find retrieves an entity by its primary key.
  • createQuery creates a Query instance that can be used to retrieve entities from the database.
  • createNamedQuery loads a Query that has been defined in a @NamedQuery annotation inside one of the persistence entities. Named queries provide a clean mechanism for centralizing JPA queries in the definition of the persistence class on which the query will execute.
  • getTransaction defines an EntityTransaction to use in your database interactions. Just like database transactions, you will typically begin the transaction, perform your operations, and then either commit or rollback your transaction. The getTransaction() method lets you access this behavior at the level of the EntityManager, rather than the database.
  • merge() adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When using merge(), objects are not managed.
  • persist adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When using persist(), objects are managed.
  • refresh refreshes the state of the current entity from the database.
  • flush synchronizes the state of the persistence context with the database.

이러한 모든 방법을 한 번에 통합하는 것에 대해 걱정하지 마십시오. 당신은 직접 작업하여 그들을 알게 될 것이다 EntityManager우리는 다음 섹션에서 더 많은 일을 할 것이다하는.