JPA教程

看到一篇不错的介绍JPA的文章, 翻译后在此记录, 英文好的同学可以直接跳转到原文学习。

Java Persistence API(JPA)是一种Java规范,是关系数据库与面向对象的编程的桥梁。本教程介绍JPA,并说明了如何将Java对象映射为为JPA Entity(实体, 后文用Entity表示),如何定义Entity关系以及如何在Java应用程序中使用Repository模式(Repository pattern)来使用JPA的EntityManager

请注意,本教程使用Hibernate作为JPA提供程序。 大多数概念可以扩展到其他Java持久性框架。

JPA中的对象关系映射

自1970年代以来,关系数据库就已经作为一种存储程序数据的工具。 尽管当今有很多关系数据库的替代方, 但关系型数据库仍然是易于理解, 且广泛用于小型和大型软件开发中的数据库。

Java对象在关系型数据库的上线文中被定义为Entity。 Entity与表对应, 有列和行组成, 使用外键和关联关系定义实体之间的关系,即一对一,一对多和多对多关系。 我们可以使用SQL获取表中的数据。 关系模型是扁平的,但是开发人员可以编写查询来检索数据并从该数据构造对象。

(对象-关系)的不匹配

您可能熟悉术语“对象关系阻抗不匹配”,这是将数据对象映射到关系数据库所面临的挑战。 之所以会出现这种不匹配,是因为面向对象的设计不限于一对一,一对多和多对多的关系。 相反,在面向对象的设计中,我们考虑对象,它们的属性和行为以及对象之间的关系。 封装和继承是两个示例:

如果一个对象包含另一个对象,我们通过封装来定义它-has-a关系。
如果一个对象是另一个对象的特化,我们通过继承定义一个is-a关系。
关联,聚合,组成,抽象,泛化,实现和依赖项都是面向对象的编程概念,可能很难映射到关系模型。

ORM: 对象关系映射

ORM 即 Object-relational mapping, 面向对象的设计与关系数据库建模之间的不匹配导致了专门针对对象关系映射(ORM)开发的一类工具。 Hibernate,EclipseLink和iBatis等ORM工具将关系数据库模型(包括实体及其关系)转换为面向对象的模型。 这些工具中有许多是在JPA规范之前存在的,但是没有标准,它们的功能就取决于供应商。

Java Persistence API(JPA)于2006年作为EJB 3.0的一部分首次发布,它提供了一种注释对象的标准方法,以便可以将它们映射并存储在关系数据库中。 该规范还定义了与数据库交互的通用结构。 拥有适用于Java的ORM标准可为供应商实现带来一致性,同时还具有灵活性和附加功能。 例如,虽然原始JPA规范适用于关系数据库,但某些供应商实现已扩展了JPA以用于NoSQL数据库。

JPA的第一个版本1.0版于2006年通过Java社区流程(JCP)作为Java规范请求(JSR)220发布。版本2.0(JSR 317)于2009年发布,版本2.1(JSR 338)于2013年发布, 并在2017年发布了2.2版(JSR 338的维护版本)。已选择JPA 2.2包含在Jakarta EE中并正在进行开发。

JPA入门

Java Persistence API是一种规范,而不是一种实现:它定义了一个通用抽象,您可以在代码中使用它与ORM产品进行交互。 本节回顾了JPA规范的一些重要部分。

您将学习如何:

  • 在数据库中定义实体,字段和主键。
  • 在数据库中的实体之间创建关系。
  • 使用EntityManager及其方法。

定义Entities

使用@Entity注解注释进行注释的类会被当做一个Entity。 例如,如果要创建一个书本实体,则可以按以下方式对其进行注释:

1
2
3
4
@Entity
public class Book {
...
}

默认情况下,该实体将映射到Book表,具体根据类名来定。 如果要将此Entity映射到另一个表(以及制定的Schema),则可以外加@Table, 例如:

1
2
3
4
5
@Entity
@Table(name="BOOKS")
public class Book {
...
}

如果BOOKS表位于PUBLISHINGSchema中,则可以将该Schema添加到@Table注解中:

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

将类的成员变量映射到数据库column

将Entity映射到表之后, 需要定义字段。 字段在类中定义为成员变量,每个字段的名称都映射到表中的列名称。 您可以使用@Column注解覆盖此默认映射,如下所示:

1
2
3
4
5
6
7
8
@Entity
@Table(name="BOOKS")
public class Book {
private String name;
@Column(name="ISBN_NUMBER")
private String isbn;
...
}

此例中, name为默认映射, isbn属性指定了自定义映射, 映射到ISBN_NUMBER列。

@Column注解允许我们定义其他属性,包括字段长度,是否可以为空,是否唯一,小数位数(如果是十进制值),是否可插入和可更新等等。

指定主键

关系数据库表的要求之一是它必须包含主键或唯一标识数据库中特定行的键。 在JPA中,我们使用@Id注解将字段指定为表的主键。 主键必须是Java基本类型,基本包装(例如IntegerLong),StringDateBigIntegerBigDecimal

在此示例中,我们将id属性映射到BOOKS表中的ID列:

1
2
3
4
5
6
7
8
9
10
11

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

也可以组合使用@Id@Column注释以覆盖主键的列名。

Entities之间的关系

现在您知道了如何定义实体,让我们看一下如何在实体之间创建关系。 JPA定义了四个用于定义实体的注释:

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

一对一 关系

@OneToOne注解用于定义两个实体之间的一对一关系。 例如,您可能具有一个UserEntity,其中包含用户的名称,电子邮件和密码,但是您可能希望在单独的UserProfileEntity中维护有关该用户的其他信息(例如年龄,性别和喜欢的颜色)。 @OneToOne注释有助于以这种方式分解数据和实体。

下面的User类具有一个UserProfile实例。 UserProfile映射到单个User实例。

1
2
3
4
5
6
7
8
9
10
11
@Entity
public class User {
@Id
private Integer id;
private String email;
private String name;
private String password;
@OneToOne(mappedBy="user")
private UserProfile profile;
...
}
1
2
3
4
5
6
7
8
9
10
11
@Entity
public class UserProfile {
@Id
private Integer id;
private int age;
private String gender;
private String favoriteColor;
@OneToOne
private User user;
...
}

映射关系通过@OneToOne注解中的mappingBy指定。

一对多 和 多对一 关系

@OneToMany@ManyToOne注解同时用在两个类中, 用来表示同一组关系。 参照这样一个例子,一本书只能有一个作者,但是一个作者可能有很多书。 BookEntity将与Author定义一个@ManyToOne关系,而AuthorEntity与Book定义一个@OneToMany关系。

1
2
3
4
5
6
7
8
9
10
11

@Entity
public class Book {
@Id
private Integer id;
private String name;
@ManyToOne
@JoinColumn(name="AUTHOR_ID")
private Author author;
...
}
1
2
3
4
5
6
7
8
9
10
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
@OneToMany(mappedBy = "author")
private List<Book> books = new ArrayList<>();
...
}

在这种情况下,Author类维护该作者编写的所有书籍的列表,而Book类维护对其单个作者的引用。 @JoinColumn用来指定Book表中作者ID的字段名称。

多对多 关系

最后,@ManyToMany注解用于表示实体之间的多对多关系。 这正是BookEntity有多个作者的情况:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class Book {
@Id
private Integer id;
private String name;
@ManyToMany
@JoinTable(name="BOOK_AUTHORS",
joinColumns=@JoinColumn(name="BOOK_ID"),
inverseJoinColumns=@JoinColumn(name="AUTHOR_ID"))
private Set<Author> authors = new HashSet<>();
...
}
1
2
3
4
5
6
7
8
9
10
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany(mappedBy = "author")
private Set<Book> books = new HashSet<>();
...
}

此例中, 我们创建一个新表BOOK_AUTHORS, 包含两个列BOOK_IDAUTHOR_ID。 使用joinColumns和inverseJoinColumns属性可以告诉JPA框架如何以多对多关系映射这些类。 Author类中的@ManyToMany注释引用Book类中用于管理关系的字段; 即authors属性。

这是一个相当复杂的主题的快速演示。 在下一篇文章中,我们将进一步研究@JoinTable和@JoinColumn注解。

使用EntityManager

EntityManager是在JPA中执行数据库交互的类。 它通过名为persistence.xml的配置文件初始化。 该文件位于CLASSPATHMETA-INF文件夹中,该文件夹通常打包在JAR或WAR文件中。 persistence.xml文件包含:

  • 名为“持久性单元”(persistence unit),它指定您正在使用的持久性框架,例如Hibernate或EclipseLink。
  • 属性的集合,指定如何连接到数据库以及持久性框架中的所有自定义项。
  • 项目中实体类的列表。

让我们看一个例子。

配置EntityManager

先使用Persistence创建EntityManagerFactory, 再创建EntityManager

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

我们创建了一个连接到Books持久性单元(persistence unit)的EntityManager,该单元已在persistence.xml文件中进行了配置。

EntityManager类定义我们的软件如何通过JPA Entity与数据库进行交互。以下是EntityManager使用的一些方法:

  • find 通过主键检索Entity。
  • createQuery 创建一个Query实例,该实例可用于从数据库中检索Entity。
  • createNamedQuery用于加载持久化实体之内@NamedQuery注解中定义的QueryNamedQuery提供了一种干净的机制,用于在将执行查询的持久性类的定义中集中的JPA查询。
  • getTransaction定义要在数据库交互中使用的EntityTransaction。就像数据库事务一样,您通常将开始事务,执行操作,然后提交或回滚事务。使用getTransaction()方法可以在EntityManager级别(而不是数据库)上访问此行为。
  • merge 将一个实体添加到持久性上下文中,以便在提交事务时,该实体将被持久化到数据库中。使用merge时,不管理对象。
  • persist 将实体添加到持久性上下文中,以便在提交事务时将实体持久化到数据库中。使用persist时,将管理对象。
  • refresh从数据库刷新当前实体的状态。
  • flush 将持久性上下文的状态与数据库同步。

不必担心一次集成所有这些方法。您将通过直接与EntityManager一起了解它们,我们将在下一部分中做更多的工作。

merge()和persist()之间的区别在于merge()将实体添加到持久性上下文中,但是您对该对象的引用不是“托管的”。 如果在调用merge()之后对对象进行更改,则这些更改将不会发送到数据库。 persist()方法将您的对象标记为“托管”。 调用persist()之后对对象所做的任何更改都将发送到数据库。

JPA 与 Hibernate

在本节中,我们将跳过概念,并开始编写代码,该代码使用带有Hibernate的JPA在关系数据库中来回存储数据。我们将从配置示例应用程序开始,以使用Hibernate作为JPA提供程序,然后我们将快速配置EntityManager并编写两个要持久保存到数据库的类:BookAuthor。最后,我们将编写一个简单的应用程序,将所有应用程序组件组合在一起,并将两个Entity成功地持久存储到数据库中。

在本节中,您将学习如何:

  • 配置一个Java应用程序以将Hibernate用作您的JPA提供程序。
  • 在persistence.xml文件中配置JPA的EntityManager。
  • 创建一个简单的JPA域模型,该模型代表具有一对多关系的两个类。
  • 使用存储库将持久性逻辑与应用程序代码完全分开。
  • 使用JPA编写,构建和运行示例应用程序以连接关系数据库。

您还将开始使用JPA查询语言(JPQL),并使用它在示例应用程序上执行一些简单的数据库操作。

获取源代码

配置Hibernate

为简单起见,我们将在开发和运行时示例中都使用嵌入式H2数据库。 您可以将persistence.xml文件中的JDBC URL更改为指向所需的任何数据库。

首先查看该项目的Maven POM文件,如清单1所示。

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.geekcap.javaworld</groupId>
<artifactId>jpa-example</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>jpa-example</name>
<url>http://maven.apache.org</url>
<properties>
<java.version>1.8</java.version>
<hibernate.version>5.3.6.Final</hibernate.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.geekcap.javaworld.jpa.JpaExample</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

我们将使用Java 8和Hibernate版本5.3.6构建该项目。 最终版,即撰写本文时的最新版本。 构建节点中的插件将设置Java编译版本,使生成的JAR文件可执行,并确保将所有依赖项都复制到lib文件夹中,以便可执行JAR可以运行。 我们包括四个依赖项:

  • hibernate-core:Hibernate的核心功能。
  • hibernate-entitymanager:Hibernate对EntityManager的支持。
  • hibernate-jpa-2.1-api:JPA API。
  • h2:嵌入式H2数据库。 注意,它的范围设置为运行时,以便我们在运行代码时可以使用它。

配置EntityManager

回想一下,JPA的EntityManagerpersistence.xml文件驱动。 清单2显示了该文件的内容。

persistence.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<persistence 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_2_0.xsd"
version="2.0">
<persistence-unit name="Books" transaction-type="RESOURCE_LOCAL">
<!-- Persistence provider -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- Entity classes -->
<class>com.geekcap.javaworld.jpa.model.Book</class>
<class>com.geekcap.javaworld.jpa.model.Author</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:bookstore" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="show_sql" value="true"/>
<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>

persistence.xml文件从一个persistence节点开始,该节点可以包含一个或多个persistence-unitpersistence-unit具有名称,稍后在创建EntityManager时将使用该名称,它定义了该单元的属性。 在这种情况下,我们在此单元中配置属性以执行以下操作:

  • 指定HibernatePersistenceProvider,因此应用程序知道我们正在使用Hibernate作为我们的JPA提供程序。
  • 定义两个Entity:Book类和Author类。
  • 通过JDBC定义数据库配置。 在这种情况下,我们使用内存中的H2实例。
  • 配置Hibernate,包括将Hibernate方言设置为H2Dialect,以便Hibernate知道如何与H2数据库通信。

领域模型(domain model)

对于此应用程序,我们正在对Book类和Author类进行建模。 这些实体具有一对多的关系,这意味着一本书只能由一位作者撰写,但是一位作者可以撰写多本书

注意: 当我们谈论数据库表时,通常谈论的是“数据模型”,但是当谈论Java实体及其关系时,通常将其称为“领域模型”。

Book class

Book.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.geekcap.javaworld.jpa.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@Entity
@Table(name = "BOOK")
@NamedQueries({
@NamedQuery(name = "Book.findByName",
query = "SELECT b FROM Book b WHERE b.name = :name"),
@NamedQuery(name = "Book.findAll",
query = "SELECT b FROM Book b")
})
public class Book {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToOne
@JoinColumn(name="AUTHOR_ID")
private Author author;
public Book() {
}
public Book(Integer id, String name) {
this.id = id;
this.name = name;
}
public Book(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author=" + author.getName() +
'}';
}
}

Book类是一个简单的POJO(普通的Java对象),它管理三个属性:

  • id:图书的主键或标识符。
  • name:书的名称或标题。
  • author:写这本书的作者。

类本身带有三个注解:

  • @Entity:将Book标识为JPA Entity。
  • @Table:覆盖此实体将保留到的表的名称。 在这种情况下,我们将表名称定义为BOOK。
  • @NamedQueries:允许您定义JPA查询语言查询,以后可以由EntityManager检索和执行。

Bookid属性使用@Id@GeneratedValue进行注释。 @Id注解将id标识为Book的主键,它将解析为基础数据库的主键。 @GeneratedValue告诉JPA,当实体持久化到数据库时,数据库应生成主键。 因为我们没有指定@Column注解,所以该ID将映射到相同的列名id

图书的名称属性将映射到BOOK表中的“名称”列。

Author class

Author.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.geekcap.javaworld.jpa.model;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name="AUTHOR")
@NamedQueries({
@NamedQuery(name = "Author.findByName",
query = "SELECT a FROM Author a WHERE a.name = :name")
})
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private List<Book> books = new ArrayList<>();
public Author() {
}
public Author(String name) {
this.name = name;
}
public Author(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Book> getBooks() {
return books;
}
public void addBook(Book book) {
books.add(book);
book.setAuthor(this);
}
@Override
public String toString() {
return "Author{" +
"id=" + id +
", name='" + name + '\'' +
", books=" + books +
'}';
}
}

级联类型

您可能会在@OneToMany批注中注意到CascadeTypeCascadeType是枚举类型,它定义要在给定关系中应用的级联操作。 在这种情况下,CascadeType定义了对作者执行的操作,这些操作应传播到书中。 CascadeType包括以下内容:

  • DETACH:将实体与EntityManager分离时,也将在操作的另一侧分离实体。
  • MERGE:将实体合并到EntityManager中时,也将在操作的另一侧合并实体。
  • PERSIST:当实体持久化到EntityManager时,也将实体持久化到操作的另一端。
  • REFRESH:从EntityManager刷新实体时,还刷新操作另一侧的实体。
  • FLUSH:将实体刷新到EntityManager时,刷新其对应的实体。
  • ALL:包括所有上述操作类型。

对作者执行任何操作时,应更新其书籍。 这是有道理的,因为没有作者就不可能存在一本书。

Repositories in JPA

我们可以创建一个EntityManager并在示例应用程序类中执行所有操作,但是使用外部存储库类将使代码更简洁。 根据存储库模式的定义,创建BookRepositoryAuthorRepository可以隔离每个实体的持久性逻辑。 清单5显示了BookRepository的源代码。

BookRepository.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.geekcap.javaworld.jpa.repository;
import com.geekcap.javaworld.jpa.model.Book;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class BookRepository {
private EntityManager entityManager;
public BookRepository(EntityManager entityManager) {
this.entityManager = entityManager;
}
public Optional<Book> findById(Integer id) {
Book book = entityManager.find(Book.class, id);
return book != null ? Optional.of(book) : Optional.empty();
}
public List<Book> findAll() {
return entityManager.createQuery("from Book").getResultList();
}
public Optional<Book> findByName(String name) {
Book book = entityManager.createQuery("SELECT b FROM Book b WHERE b.name = :name", Book.class)
.setParameter("name", name)
.getSingleResult();
return book != null ? Optional.of(book) : Optional.empty();
}
public Optional<Book> findByNameNamedQuery(String name) {
Book book = entityManager.createNamedQuery("Book.findByName", Book.class)
.setParameter("name", name)
.getSingleResult();
return book != null ? Optional.of(book) : Optional.empty();
}
public Optional<Book> save(Book book) {
try {
entityManager.getTransaction().begin();
entityManager.persist(book);
entityManager.getTransaction().commit();
return Optional.of(book);
} catch (Exception e) {
e.printStackTrace();
}
return Optional.empty();
}
}

BookRepository使用EntityManager初始化,我们将在示例应用程序中创建它。 第一个方法findById()调用EntityManager的find()方法,该方法检索具有给定主键的给定类的实体。 例如,如果我们添加一本新书并且其主键生成为“ 1”,则entityManager.find(Book.class,1)将返回ID为1的Book。 在数据库中找不到,则find()方法返回null。 因为我们希望代码更具弹性,并且不传递null,所以它检查null值并返回包装在OptionalOptional.empty()中。

仔细查看EntityManager方法

  • findAll() 方法使用JPQL创建一个新查询以检索所有书籍。如前所述,我们可以将它写为SELECT b FROM Book bfrom Book是实现它的一种简便方法。 createQuery()方法创建一个Query实例,该实例支持许多setter方法(例如setParameter(),我们将在后面看到),以使构建查询看起来更加优雅。 它有两种方法可以执行我们感兴趣的查询:

AuthorRepository.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.geekcap.javaworld.jpa.repository;
import com.geekcap.javaworld.jpa.model.Author;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class AuthorRepository {
private EntityManager entityManager;
public AuthorRepository(EntityManager entityManager) {
this.entityManager = entityManager;
}
public Optional<Author> findById(Integer id) {
Author author = entityManager.find(Author.class, id);
return author != null ? Optional.of(author) : Optional.empty();
}
public List<Author> findAll() {
return entityManager.createQuery("from Author").getResultList();
}
public Optional<Author> findByName(String name) {
Author author = entityManager.createNamedQuery("Author.findByName", Author.class)
.setParameter("name", name)
.getSingleResult();
return author != null ? Optional.of(author) : Optional.empty();
}
public Optional<Author> save(Author author) {
try {
entityManager.getTransaction().begin();
entityManager.persist(author);
entityManager.getTransaction().commit();
return Optional.of(author);
} catch (Exception e) {
e.printStackTrace();
}
return Optional.empty();
}
}

Hibernate JPA使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.geekcap.javaworld.jpa;
import com.geekcap.javaworld.jpa.model.Author;
import com.geekcap.javaworld.jpa.model.Book;
import com.geekcap.javaworld.jpa.repository.AuthorRepository;
import com.geekcap.javaworld.jpa.repository.BookRepository;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
import java.util.Optional;
public class JpaExample {
public static void main(String[] args) {
// Create our entity manager
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Books");
EntityManager entityManager = entityManagerFactory.createEntityManager();
// Create our repositories
BookRepository bookRepository = new BookRepository(entityManager);
AuthorRepository authorRepository = new AuthorRepository(entityManager);
// Create an author and add 3 books to his list of books
Author author = new Author("Author 1");
author.addBook(new Book("Book 1"));
author.addBook(new Book("Book 2"));
author.addBook(new Book("Book 3"));
Optional<Author> savedAuthor = authorRepository.save(author);
System.out.println("Saved author: " + savedAuthor.get());
// Find all authors
List<Author> authors = authorRepository.findAll();
System.out.println("Authors:");
authors.forEach(System.out::println);
// Find author by name
Optional<Author> authorByName = authorRepository.findByName("Author 1");
System.out.println("Searching for an author by name: ");
authorByName.ifPresent(System.out::println);
// Search for a book by ID
Optional<Book> foundBook = bookRepository.findById(2);
foundBook.ifPresent(System.out::println);
// Search for a book with an invalid ID
Optional<Book> notFoundBook = bookRepository.findById(99);
notFoundBook.ifPresent(System.out::println);
// List all books
List<Book> books = bookRepository.findAll();
System.out.println("Books in database:");
books.forEach(System.out::println);
// Find a book by name
Optional<Book> queryBook1 = bookRepository.findByName("Book 2");
System.out.println("Query for book 2:");
queryBook1.ifPresent(System.out::println);
// Find a book by name using a named query
Optional<Book> queryBook2 = bookRepository.findByNameNamedQuery("Book 3");
System.out.println("Query for book 3:");
queryBook2.ifPresent(System.out::println);
// Add a book to author 1
Optional<Author> author1 = authorRepository.findById(1);
author1.ifPresent(a -> {
a.addBook(new Book("Book 4"));
System.out.println("Saved author: " + authorRepository.save(a));
});
// Close the entity manager and associated factory
entityManager.close();
entityManagerFactory.close();
}
}

(详细内容请看原文)

第二篇文章:

https://www.javaworld.com/article/3387643/java-persistence-with-jpa-and-hibernate-part-2-many-to-many-relationships.html

本文参考: https://www.javaworld.com/article/3373652/java-persistence-with-jpa-and-hibernate-part-1-entities-and-relationships.html

0%