一、简介

JPA(Java Persistence API)即Java持久化API,为Java开发人员提供了一个对象/关系映射工具,用于管理Java 应用程序中的关系数据。持久性包括四个方面:Java持久化API、查询语言、Java持久化Criteria API和对象/关系映射元数据。

一般来说,Java开发人员使用大量的代码或者专有框架与数据库交互,而使用JPA会显著减少与数据库交互的负担;它在对象模型(Java程序)和关系模型(数据库程序)之间架起了一座桥梁,有以下优点:

  • 不需要创建复杂的数据访问对象DAO(Data Access Objects)

  • JPA可以帮助开发者管理事务

  • 可以优先使用类名和属性的查询语言,避免使用SQL

二、实体及关系

1、实体

Java持久化API要求标识需要存储在数据库中的类,API使用术语”实体”(Entity)来定义要映射到关系数据库的类。 例如,下面的例子中将Player类定义为了实体:

@Entity
public class Player {

}

@Entity注释标识在类定义之前,Java持久化API将会在关系数据库中创建一个Player实体表。(默认情况下,表名和类名相同。)

也可以通过name属性指定实体的名称(在查询中使用实体名称):

@Entity (name="BaseballPlayer")
public class Player {
...
}

还可以通过Table注解的name属性来指定与实体名称不同的表名:

@Entity
@Table(name="BASEBALL_PLAYER")
public class Player {
...
}

实体可以是抽象的或具体的类。但是抽象实体还必须由另一个实体类子类化以便进行数据库存储。

2、字段和属性

实体的状态由其字段或属性的值来定义,可以使用变量字段或属性的getter/setter方法来检索或保存实体。

如果对实例变量本身进行注释,持久性提供程序将直接访问实例变量。如果对JavaBean风格的getter/setter方法进行注释,则持久性提供程序将使用这些访问器来加载和存储持久性状态。

可以使用大多数基础类型,包括基础类型及其对应的包装器、 String以及其他类型等字段作持久化。

所有实体都必须有一个主键;主键可以是单个唯一的字段,也可以是字段的组合;通过Id注解标识单字段主键。

主键字段必须具有以下类型之一:原始类型(例如:intlong等)、原始类型的包装类(例如:IntegerLong等)、java.lang.Stringjava.util.Datejava.sql.Date

  • 定义列

使用@Column注解定义数据库列并指定列名:

@Column(name="SURNAME")
public String getLastName() {
...
}
  • 主键自动生成

标识id属性为主键并自动递增:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
  • 不持久化

下面的例子表示此属性不需要持久化,使用了注解标注在方法上的方式:

@Transient
public String getLastSpokenWords() {
	return lastSpokenWords;
}

3、实体关系

持久化对象通常不会单独使用:实体类通常与使用或提供服务的其他类交互。类与类之间可以存在一对一、一对多、多对一以及多对多的关系,分别对应四种注解:OneToOneOneToManyManyToOneManyToMany

例如,球员(Player)与球队(Team)存在多对一的关系,即一个球队对应着多个球员:

@Entity
public class Player implements Serializable {
...
    private Team team;

    @ManyToOne
    public Team getTeam() {
        return team;
    }
...
}

由于Team和Player类具有双向关系,反过来看,Team与Player是一对多(OneToMany)的关系;mappedBy属性与OneToMany属性一起使用,以便让持久化引擎知道如何匹配Player和Team。

在下面的例子中,mappedBy属性表示一个Player对象的Team属性将映射到Team实例。由Player对象的Team属性映射意味着 Team对象的标识符将作为外键列存在于Player表中。

@Entity
public class Team implements Serializable {

    private Long id;
    private String teamName;
    private String division;
    private Collection<Player> players;
    
    /** Creates a new instance of Team */
    public Team() {
        players = new HashSet();
    }

    /**
     * Gets the id of this Team.
     * @return the id
     */
    @Id
    @GeneratedValue
    public Long getId() {
        return this.id;
    }

    /**
     * Sets the id of this Team to the specified value.
     * @param id the new id
     */
    public void setId(Long id) {
        this.id = id;
    }

    @OneToMany(mappedBy = "team")
    public Collection<Player> getPlayers() {
        return players;
    }
    
    public void setPlayers(Collection<Player> players) {
        this.players = players;
    }
    ...    
}

4、持久性单位

应用程序中的实体集称为持久化单元(persistence unit),必须在persistence.xml文件中定义应用程序的持久化单元。这个文件应该存在于META-INF目录中,META-INF目录应该放在项目的源目录中。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/index.html"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/index.html
    http://bit.ly/cDXfCA">
  <persistence-unit name="league" transaction-type="RESOURCE_LOCAL">

    <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
    <class>com.sun.demo.jpa.Player</class>
    <class>com.sun.demo.jpa.Team</class>
    <properties>

      <property name="toplink.jdbc.user" value="league"/>
      <property name="toplink.jdbc.password" value="league"/>
      <property name="toplink.jdbc.url" value="jdbc:derby://localhost:1527/league"/>

      <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
      <property name="toplink.ddl-generation" value="create-tables"/>
    </properties>

  </persistence-unit>
</persistence>

这个文件的大部分是模板,主要有以下几部分:persistence-unitproviderclassproperty

  • provider

Provider元素声明了提供用于创建EntityManager实例的初始工厂的类。

  • class

使用class元素列出应用程序中的实体类名称。持久性提供者通过读取persistence.xml文件中的实体名称,知道哪些应用程序类要映射到关系数据库。

  • property

如果不能进行JNDI查找,应该将数据库连接属性放到persistence.xml文件中。数据库连接属性包括数据库连接的用户名和密码、数据库连接字符串和驱动程序类名。

还可以配置持久性提供程序属性,比如创建或删除新表的选项等。持久性属性名称空间javax.persistence保留给规范定义的属性,特定供应商的选项和属性必须使用自己的名称空间,以避免与规范发生冲突。例如,上面的persistence.xml文件使用GlassFish参考实现;它特定于供应商的属性位于toplink名称空间中,而不是规范本身的一部分。

下面的属性使用了javax.persistence规范定义的属性:

<persistence-unit name="MyUnit" transaction-type="RESOURCE_LOCAL">
	<properties>
		<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/foo_bar"/>
		<property name="javax.persistence.jdbc.user" value=""/>
		<property name="javax.persistence.jdbc.password" value=""/>
		<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
	</properties>
</persistence-unit>

三、实体管理

1、API概述

Java持久化API是Java EE 5规范的一部分,内容位于javax.persistence包下,对应的Maven依赖如下:

<!-- https://mvnrepository.com/artifact/javax.persistence/javax.persistence-api -->
<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
</dependency>

API中的EntityManager(实体管理器)提供了开始和结束事务、持久化上下文(persistence context)中的实体、查找实体以及合并甚至删除这些实体的方法。此外,EntityManager实例还可以创建和执行查询。创建实体管理器的方法如下:

// Create the EntityManager
EntityManagerFactory factory = Persistence.createEntityManagerFactory("league");
EntityManager em = factory.createEntityManager();

因为EntityManager实例表示持久性单元,所以必须提供持久性单元名称。在上面的示例中,league是持久性单元的名称,对应于在persistence.xml中声明的persistence-unit的name属性。

persistence context持久化上下文是由持久性提供程序在运行时管理的唯一实体实例的分组,类似的术语是持久性单元(persistence unit),它是应用程序可能使用的所有实体类的集合,持久性单元定义一组映射到单个数据库的实体。

实体在API规范中具有生命周期状态:

  • New

新实体是应用程序中的新对象,应用程序中可能存在一个新实体,但持久性上下文尚不知道该实体。

  • Managed

托管实体是已经持久化或已经存在于数据库中的实体,这些实体在持久化上下文中有一个标识。

  • Detached

分离的实体具有持久性标识,但是它们当前不在持久性上下文中主动管理。

  • Removed

已删除的实体存在于持久化上下文中,但计划从该上下文中删除。

2、管理实体

  • 持久化

获取到EntityManager对象后,就可以用它来创建查询(queries)和事务(transactions)。在存储新的Player和Team实体之前,需要开启(begin)一个事务;在事务期间,使用EntityManager的persist方法来管理新实体。当持久化新实体时,它们将移动到托管状态。对于数据库中发生的更改,必须提交事务(commit)。通常在所有事务结束之后,调用实体管理器及其工厂的close方法。

public class CreatePlayersAndTeams {
    
    /** Creates a new instance of CreatePlayersAndTeams */
    public CreatePlayersAndTeams() {
    }
    
	private static Player[] players = new Player[] {
        // name, number, last quoted statement
        new Player("Lowe", "Derek", 23, "The sinker's been good to me."),
        new Player("Kent", "Jeff", 12, "I wish I could run faster."),
        new Player("Garciaparra", "Nomar", 5,
                "No, I'm not superstitious at all.")
                
    };
    
    private static Team[] teams = new Team[] {
        new Team("Los Angeles Dodgers", "National"),
        new Team("San Francisco Giants", "National"),
        new Team("Anaheim Angels", "American"),
        new Team("Boston Red Sox", "American")
    };

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Create the EntityManager
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("league");
        EntityManager em = emf.createEntityManager();
        
        em.getTransaction().begin();
        
        for(Team team: teams) {
            em.persist(team);
        }
       
        for(Player player: players) {
            player.setTeam(teams[0]);
            teams[0].addPlayer(player);
            em.persist(player);
        }
        
        em.getTransaction().commit();
        
        em.close();
        emf.close();
    }
    
}
  • 查询

可以使用EntityManager对象的find方法通过主键来检索实体:

// The EntityManager, em, has already been created at this point.
...            
// Retrieve Player entities using their keys.
for(long primaryKey = 1; primaryKey < 10; primaryKey++) {
    Player player = em.find(Player.class, primaryKey);
    if (player != null) {
        System.out.println(player.toString());
    }     
}
  • 分离与合并

分离的实体(Detached Entities)在数据库中具有持久标识,但不在当前持久性上下文中。如果是从以前的某个序列化的文件创建了实体,或者清除或关闭了实体管理器,则会出现这种情况。此时可以更新实体,并使用merge方法将其合并回持久性上下文中。

// The EntityManager, em, already exists for this example.
...
// We just happen to know that '5' is one of the
// player identifiers. You shouldn't normally hard-code this
// into any application.
Player p = em.find(Player.class, 5L);
em.clear();
// p is now detached for the convenience of this example

Team t = new Team("Ventura Surfers", "National");
p.setTeam(t);

em.getTransaction().begin();
Player managedPlayer = em.merge(p);
em.getTransaction().commit();
...

上面的例子中调用了EntityManager的clear方法,将Player实例与持久性上下文分离。除此之外,当关闭EntityManager时,实体也会变得分离;此时实体仍然在数据库中,但实体管理器不再主动管理它。通过EntityManager的merge方法将分离的对象放回托管状态并返回实体的托管副本。

  • 更新

通过EntityManager的find方法可以检索到一个托管的实体,更新实体只需要在事务中直接修改实体属性即可:

em.getTransaction().begin();
for(Player p : playerList) {
    // add 1 to each players jersey number
    int jersey = p.getJerseyNumber();
    p.setJerseyNumber(jersey + 1);
}
em.getTransaction().commit();
  • 删除

可以使用EntityManager的Remove方法删除一个实体,此方法需要在事务中进行:

// 'em' is an EntityManager instance
em.getTransaction().begin();
// use a hard-coded player id for convenience in this sample only
Player player = em.find(Player.class, 5L);
if (player != null) {
    System.out.println(player.toString());
    em.remove(player);
}
em.getTransaction().commit();

3、JPQL

JPQL(Java Persistence query language)即Java持久性查询语言是EJB查询语言(EJB QL)的扩展,它允许对应用程序中的实体执行动态和静态查询。它在许多方面与SQL相似,但与SQL相比有更多好处,因为它是在实体及其关系上运行的,而不是在实际的关系数据库模式上运行的。这使得查询不管基础数据库是什么都可以移植。

查询有三种不同的方式: 查询(select)、更新(update)和删除(delete)。select可以从数据库查询并返回一组实体;update可以更改现有实体或实体集的一个或多个属性;delete可以从数据库中删除一个或多个实体。

  • 创建Query

可以通过EntityManager的createQuery方法创建Query对象:

Query query = em.createQuery(queryStatement);
  • 查询

查询指定条件的数据:

Query q = em.createQuery("select c from Player c where c.team.teamName = 'Los Angeles Dodgers'");
List<Player> playerList = q.getResultList();

也可以用编程方式设置where子句的参数:

Query q = em.createQuery("select c from Player c where c.team.teamName = :name");
q.setParameter("name", aTeamName);
List<Player> playerList = q.getResultList();
for(Player p : playerList) {
    System.out.println(p.toString());
}
  • 更新
Query q = em.createQuery("update Player p " +
                         "set p.jerseyNumber = (p.jerseyNumber + 1) " +
                         "where p.team.teamName = :name");
q.setParameter("name", aTeamName);       
em.getTransaction().begin();
q.executeUpdate();
em.getTransaction().commit();
  • 删除
String aTeamName = "Anaheim Angels";
...
Query q = em.createQuery("delete from Team t " +
    "where t.teamName = :name");
q.setParameter("name", aTeamName);
em.getTransaction().begin();
q.executeUpdate();
em.getTransaction().commit();

四、样例

工程中使用了Oracle TopLink,需增加依赖或手工引入相关jar包:

<!-- https://mvnrepository.com/artifact/javax.persistence/toplink-essentials -->
<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>toplink-essentials</artifactId>
    <version>1.0</version>
</dependency>

还需要引入数据库驱动jar,此处使用MySQL:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.36</version>
</dependency>

1、实体&表定义

  • Employee
package com.strikeback.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="t_employee")
public class Employee {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	@Column(name="ename", length=100)
	private String name;
	private double salary;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
}
  • persistence.xml

在src目录下新建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="Study_JPA" transaction-type="RESOURCE_LOCAL">

		<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>

		<class>com.strikeback.jpa.Employee</class>

		<properties>
			<property name="toplink.jdbc.user" value="root" />
			<property name="toplink.jdbc.password" value="password" />
			<property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/jpadb" />
			<property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver" />
			<property name="toplink.ddl-generation" value="create-tables" />
		</properties>

	</persistence-unit>
</persistence>
  • 数据库表

对应生成的表结构为:

CREATE TABLE `t_employee` (
  `ID` int(11) NOT NULL,
  `ename` varchar(100) DEFAULT NULL,
  `SALARY` double DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2、插入

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
Employee john = new Employee();
john.setName("John");
john.setSalary(5000);
entityManager.persist(john);
Employee tom = new Employee();
tom.setName("Tom");
tom.setSalary(3600);
entityManager.persist(tom);
entityManager.getTransaction().commit();

entityManager.close();
factory.close();

3、查询

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();
Employee employee = entityManager.find(Employee.class, 1);
System.out.printf("Name: %s, Salary: %s", employee.getName(), employee.getSalary());

输出:

Name: John, Salary: 5000.0

4、更新

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
Employee employee = entityManager.find(Employee.class, 1);
System.out.printf("Name: %s, Salary: %s\n", employee.getName(), employee.getSalary());
employee.setSalary(7000);
entityManager.getTransaction().commit();
System.out.printf("Name: %s, Salary: %s\n", employee.getName(), employee.getSalary());

entityManager.close();
factory.close();

输出:

Name: John, Salary: 5000.0
Name: John, Salary: 7000.0

5、删除

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
Employee employee = entityManager.find(Employee.class, 1);
entityManager.remove(employee);
entityManager.getTransaction().commit();

entityManager.close();
factory.close();

四、JPQL样例

JPQL查询语句:

SELECT ... FROM ...
[WHERE ...]
[GROUP BY ... [HAVING ...]]
[ORDER BY ...]

更新和删除语句:

UPDATE ... SET ... [WHERE ...]

DELETE FROM ... [WHERE ...]

测试数据如下:

1、查询

  • 普通查询与聚合函数
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();
String jpql = "select e.name from Employee e";
Query query = entityManager.createQuery(jpql);
List<String> names = query.getResultList();
for(String name : names){
	System.out.printf("Name: %s\n", name);
}

//使用聚合函数
jpql = "select MAX(e.salary) from Employee e";
query = entityManager.createQuery(jpql);
Double result = (Double) query.getSingleResult();
System.out.println("Max Employee Salary :" + result);

entityManager.close();
factory.close();

输出:

Name: John
Name: Tom
Name: Jack
Name: Anna
Name: Miya

Max Employee Salary :8000.0
  • Between And和Like
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();
//Between And
String jpql = "select e from Employee e where e.salary between 6000 and 10000";
Query query = entityManager.createQuery(jpql);
List<Employee> result = query.getResultList();
for(Employee employee : result){
	System.out.printf("ID: %s, Name: %s, Salary: %s\n", employee.getId(), employee.getName(), employee.getSalary());
}

System.out.println();

//Like
jpql = "select e from Employee e where e.name like 'J%'";
query = entityManager.createQuery(jpql);
result = query.getResultList();
for(Employee employee : result){
	System.out.printf("ID: %s, Name: %s, Salary: %s\n", employee.getId(), employee.getName(), employee.getSalary());
}

entityManager.close();
factory.close();

输出:

ID: 4, Name: Anna, Salary: 8000.0
ID: 5, Name: Miya, Salary: 6000.0

ID: 1, Name: John, Salary: 5000.0
ID: 3, Name: Jack, Salary: 5000.0
  • 排序
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

String jpql = "select e from Employee e order by e.salary asc";
Query query = entityManager.createQuery(jpql);
List<Employee> result = query.getResultList();
for(Employee employee : result){
	System.out.printf("ID: %s, Name: %s, Salary: %s\n", employee.getId(), employee.getName(), employee.getSalary());
}

entityManager.close();
factory.close();

输出:

ID: 2, Name: Tom, Salary: 3600.0
ID: 1, Name: John, Salary: 5000.0
ID: 3, Name: Jack, Salary: 5000.0
ID: 5, Name: Miya, Salary: 6000.0
ID: 4, Name: Anna, Salary: 8000.0
  • 命名查询

使用@NamedQuery注解可以预定义不可更改的查询字符串:

@Entity
@Table(name="t_employee")
@NamedQuery(name="find employee by id", query="select e from Employee e where e.id = :id")
public class Employee {
	...
}

查询:

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

Query query = entityManager.createNamedQuery("find employee by id");
query.setParameter("id", 5);
Employee employee = (Employee) query.getSingleResult();
System.out.printf("Name: %s, Salary: %s\n", employee.getName(), employee.getSalary());

entityManager.close();
factory.close();

输出:

Name: Miya, Salary: 6000.0

2、更新

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

String jpql = "update Employee e set e.salary = :salary where e.id = :id";
Query query = entityManager.createQuery(jpql);
query.setParameter("salary", 7000);
query.setParameter("id", 1);

entityManager.getTransaction().begin();
query.executeUpdate();
entityManager.getTransaction().commit();

entityManager.close();
factory.close();

3、删除

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

String jpql = "delete from Employee e where e.name = :name";
Query query = entityManager.createQuery(jpql);
query.setParameter("name", "Tom");

entityManager.getTransaction().begin();
query.executeUpdate();
entityManager.getTransaction().commit();

entityManager.close();
factory.close();

五、Criteria API

Criteria API是用于定义实体查询的预定义(predefined)API,是定义JPQL查询的另一种方式;这些查询是类型安全的、可移植的,并且可以通过更改语法来轻松修改。

标准(Criteria)API的主要优点是可以在编译时更早地检测到错误,基于字符串的JPQL查询和基于JPA标准(criteria based)的查询在性能和效率上是相同的。

1、标准查询结构

标准查询结构(Criteria Query Structure)查询的基本步骤如下:

  • 使用EntityManager对象创建CriteriaBuilder对象。

  • 使用CriteriaBuilder对象创建CriteriaQuery查询对象,此查询对象的属性将根据查询的详细信息进行修改。

  • 使用CriteriaQuery.from()方法设置查询的根。

  • 使用CriteriaQuery.select()方法设置结果列表(result list)类型。

  • 创建指定查询结果类型的TypedQuery<T>

  • 使用TypedQuery的getResultList()方法获取查询集合。

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Entity class> cq = cb.createQuery(Entity.class);
Root<Entity> from = cq.from(Entity.class);
cq.select(Entity);

TypedQuery<Entity> q = em.createQuery(cq);
List<Entity> allitems = q.getResultList();

2、查询样例

测试数据如下:

由于toplink不支持Criteria API,因此此处使用eclipselink,对应的Maven依赖为:

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>eclipselink</artifactId>
    <version>2.7.4</version>
</dependency>
  • persistence.xml
<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.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_2_0.xsd">

	<persistence-unit name="Study_JPA" transaction-type="RESOURCE_LOCAL">

		<class>com.strikeback.jpa.Employee</class>

		<properties>
			<property name="eclipselink.jdbc.url" value="jdbc:mysql://localhost:3306/jpadb" />
			<property name="eclipselink.jdbc.user" value="root" />
			<property name="eclipselink.jdbc.password" value="password" />
			<property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver" />
			<property name="eclipselink.logging.level" value="FINE" />
			<property name="eclipselink.ddl-generation" value="create-tables" />
		</properties>

	</persistence-unit>
</persistence>
  • 查询过滤排序
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = builder.createQuery(Employee.class);

Root<Employee> root = criteriaQuery.from(Employee.class);
CriteriaQuery<Employee> select = criteriaQuery.select(root);
select.where(builder.like(root.get("name"), "%a%"));
select.orderBy(builder.asc(root.get("salary")));
TypedQuery<Employee> typedQuery = entityManager.createQuery(select);

List<Employee> list = typedQuery.getResultList();
for(Employee employee : list){
	System.out.printf("ID: %s, Name: %s, Salary: %s\n", employee.getId(), employee.getName(), employee.getSalary());
}

entityManager.close();
factory.close();

输出:

ID: 3, Name: Jack, Salary: 5000.0
ID: 5, Name: Miya, Salary: 6000.0
ID: 4, Name: Anna, Salary: 8000.0

六、实体关系

1、多对一(ManyToOne)

实体之间的多对一关系在关系数据库中,可以通过在表之间使用外键/主键来实现;例如:员工(Employee)和部门(Department)之间是多对一的关系,每个员工记录中都包含一个部门ID;Employee表中的部门ID字段是Department表中的主键,Department ID是Employee表的外键。

  • Department
package com.strikeback.jpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Department {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
  • Employee
package com.strikeback.jpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class Employee {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String name;
	private double salary;
	@ManyToOne
	private Department department;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	public Department getDepartment() {
		return department;
	}
	public void setDepartment(Department department) {
		this.department = department;
	}
}
  • persistence.xml

persistence.xml中配置class:

...
<class>com.strikeback.jpa.Employee</class>
<class>com.strikeback.jpa.Department</class>
...
  • 测试方法
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
//Department
Department department = new Department();
department.setName("manager");
entityManager.persist(department);
//Employee - john
Employee john = new Employee();
john.setName("John");
john.setSalary(5000);
john.setDepartment(department);
entityManager.persist(john);
//Employee - tom
Employee tom = new Employee();
tom.setName("Tom");
tom.setSalary(4000);
tom.setDepartment(department);
entityManager.persist(tom);
//Employee - jack
Employee jack = new Employee();
jack.setName("Jack");
jack.setSalary(6000);
jack.setDepartment(department);
entityManager.persist(jack);
entityManager.getTransaction().commit();

entityManager.close();
factory.close();
  • 数据表

department:

employee:

2、一对多(OneToMany)

在上面的例子中,如果员工(Employee)和部门(Department)的关系是反向的,那么就是多对一的关系。

  • Employee
@Entity
public class Employee {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String name;
	private double salary;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
}
  • Department
@Entity
public class Department {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;
	
	@OneToMany(targetEntity=Employee.class)
	private List<Employee> employees;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public List<Employee> getEmployees() {
		return employees;
	}
	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}
}
  • 测试方法
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
//Employee - john
Employee john = new Employee();
john.setName("John");
john.setSalary(5000);
entityManager.persist(john);
//Employee - tom
Employee tom = new Employee();
tom.setName("Tom");
tom.setSalary(4000);
entityManager.persist(tom);
//Employee - jack
Employee jack = new Employee();
jack.setName("Jack");
jack.setSalary(6000);
entityManager.persist(jack);
//Department
Department department = new Department();
department.setName("manager");
List<Employee> employees = new ArrayList<>();
employees.add(john);
employees.add(tom);
employees.add(jack);
department.setEmployees(employees);
entityManager.persist(department);
entityManager.getTransaction().commit();

entityManager.close();
factory.close();
  • 数据表

department:

employee:

department_employee:

3、一对一(OneToOne)

在一对一关系中,一个项目(Item)只能属于另一个项目(Item)。在上面的例子中,假设员工(Employee)和部门(Department)是一对一的关系:

  • Department
@Entity
public class Department {

	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	private int id;
	private String name;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
  • Employee
@Entity
public class Employee {
	
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	private int id;
	private String name;
	private double salary;
	
	@OneToOne
	private Department department;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	public Department getDepartment() {
		return department;
	}
	public void setDepartment(Department department) {
		this.department = department;
	}
}
  • 测试方法
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
//Department - manager
Department manageDept = new Department();
manageDept.setName("manager");
entityManager.persist(manageDept);
//Employee - john
Employee john = new Employee();
john.setName("John");
john.setSalary(5000);
john.setDepartment(manageDept);
entityManager.persist(john);

//Department - development
Department developmentDept = new Department();
developmentDept.setName("development");
entityManager.persist(developmentDept);
//Employee - tom
Employee tom = new Employee();
tom.setName("Tom");
tom.setSalary(4000);
tom.setDepartment(developmentDept);
entityManager.persist(tom);
entityManager.getTransaction().commit();

entityManager.close();
factory.close();
  • 数据表

department:

employee:

4、多对多(ManyToMany)

多对多关系是指一个实体中的一行或多行与其他实体中的多行相关联;例如:班级(MyClass)和教师(Teacher)是多对多的关系,一个班级对应多个教师,一个教师也对应多个班级。

  • MyClass
@Entity
public class MyClass {

	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	private int id;
	private String name;
	
	@ManyToMany(mappedBy="classes")
	private Set<Teacher> teachers;

	public MyClass() {}
	
	public MyClass(String name) {
		super();
		this.name = name;
	}

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Set<Teacher> getTeachers() {
		return teachers;
	}
	public void setTeachers(Set<Teacher> teachers) {
		this.teachers = teachers;
	}
}
  • Teacher
@Entity
public class Teacher {

	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	private int id;
	private String name;
	private String subject;
	@ManyToMany(targetEntity=MyClass.class)
	private Set<MyClass> classes;

	public Teacher() {}
	
	public Teacher(String name, String subject, Set<MyClass> classes) {
		super();
		this.name = name;
		this.subject = subject;
		this.classes = classes;
	}

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public Set<MyClass> getClasses() {
		return classes;
	}
	public void setClasses(Set<MyClass> classes) {
		this.classes = classes;
	}
}
  • persistence.xml

persistence.xml中配置class:

...
<class>com.strikeback.jpa.MyClass</class>
<class>com.strikeback.jpa.Teacher</class>
...
  • 测试方法
EntityManagerFactory factory = Persistence.createEntityManagerFactory("Study_JPA");
EntityManager entityManager = factory.createEntityManager();

entityManager.getTransaction().begin();
//class one
MyClass classOne = new MyClass("Class One");
entityManager.persist(classOne);
//class two
MyClass classTwo = new MyClass("Class Two");
entityManager.persist(classTwo);
//class three
MyClass classThree = new MyClass("Class Three");
entityManager.persist(classThree);

Set<MyClass> classes = new HashSet<>();
classes.add(classOne);
classes.add(classTwo);
//math teacher
Teacher mathTeacher = new Teacher("Jack", "Mathematics", classes);
entityManager.persist(mathTeacher);

classes = new HashSet<>();
classes.add(classTwo);
//Chemistry teacher
Teacher chemistryTeacher = new Teacher("Tom", "Chemistry", classes);
entityManager.persist(chemistryTeacher);

classes = new HashSet<>();
classes.add(classTwo);
classes.add(classThree);
//Physics teacher
Teacher physicsTeacher = new Teacher("Anna", "Physics", classes);
entityManager.persist(physicsTeacher);
entityManager.getTransaction().commit();

entityManager.close();
factory.close();
  • 数据表

myclass:

teacher:

teacher_myclass:

参考资料: