0%

JPA使用指南-2

文章承自JPA使用指南-1

本文介绍 JPA 常用 API

Persistence -> EntityManagerFactory -> EntityManager -> EntityTransaction

1. Persistence

Persistence 类是用于获取 EntityManagerFactory 实例。该类包含一个名为 createEntityManagerFactory 的 静态方法 。

createEntityManagerFactory 方法有如下两个重载版本。

  • (常用)带有一个参数的方法以 JPA 配置文件 persistence.xml 中的持久化单元名为参数
  • 带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型,用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。

2. EntityManagerFactory

EntityManagerFactory 接口主要用来创建 EntityManager 实例。该接口约定了如下4个方法:

  • (常用)createEntityManager():用于创建实体管理器对象实例。
  • createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
  • isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
  • (常用)close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。

3. EntityManager

EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。

在 JPA 规范中, EntityManager 是完成持久化操作的核心对象。实体作为普通 Java 对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。

它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。

PS:实体的状态:

  • 新建状态: 新创建的对象,尚未拥有持久性主键。
  • 持久化状态:已经拥有持久性主键并和持久化建立了上下文环境
  • 游离状态:拥有持久化主键,但是没有与持久化建立上下文环境
  • 删除状态: 拥有持久化主键,已经和持久化建立上下文环境,但是从数据库中删除。
  • 基本方法测试

    • find (Class entityClass,Object primaryKey)
    • getReference(Class entityClass,Object primaryKey)
    • persist(Object entity)
    • remove(Object entity)
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.sust;

import com.sust.entity.Customer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.Random;

public class EntityManagerTest1 {
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
private EntityTransaction transaction;

@Test
public void execute() {
// 1.testFind()
// Customer customer = testFind(1);
// System.out.println("--------Before use customer--------");
// System.out.println(customer);

// 2.testGetReference()
// Customer customer = testGetReference(1);
// System.out.println("--------Before use customer--------");
// System.out.println(customer);

// 3.testPersist() 注:插入10条数据,并且每一个customer都被持久化,获得了数据库自增id字段的值
// for (int i = 1; i <= 10; i++) {
// Customer customer = new Customer();
// customer.setLastName("testPersist_" + i);
// customer.setAge(new Random().nextInt(100));
// customer.setEmail(new Random().nextInt(99999) + "@sust.edu.cn");
// testPersist(customer);
// System.out.println(customer.getId());
// }

// 4.testRemove() 注:方法作用于持久化状态的对象,故需要查询用ID出来才可以删除!
// Customer customer = testFind(1);
// testRemove(customer);

}

/**
* find (Class<T> entityClass,Object primaryKey)
* 直接返回持久化对象(缓存中没有就查数据库),不存在则返回NULL
*/
private Customer testFind(Integer primaryKey) {
return entityManager.find(Customer.class, primaryKey);
}

/**
* getReference(Class<T> entityClass,Object primaryKey)
* 懒加载,返回entityClass的代理,
* 当用到返回代理类时执行真正的查询
* 如果此entity在数据库中不存在,getReference()会抛出EntityNotFoundException
*/
private Customer testGetReference(Integer primaryKey) {
return entityManager.getReference(Customer.class, primaryKey);
}

/**
*
* 方法传入的将要被持久化的对象不能拥有ID(即临时对象),否则持久化失败
*/
private void testPersist(Customer customer) {
entityManager.persist(customer);
}

/**
* remove(Object entity)
* 方法作用于持久化状态的对象,故需要查询用ID出来才可以删除
*/
private void testRemove(Customer customer) {
entityManager.remove(customer);
}

/**
* 直接update了
*/
public void testUpdate(){
Customer customer = entityManager.find(Customer.class, 13);
customer.setAge(19);
}
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory("myPersistenceUnit-1");
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
}

@After
public void after() {
transaction.commit();
entityManager.close();
entityManagerFactory.close();
}

}

  • merge (T entity) 方法

    merge() 用于处理 Entity 的同步。即数据库的插入和更新操作

    简单来说:

    1. 传入的entity若无主键,则会执行insert,并返回一个新的持久化对象
    2. 传入entity若有主键,但数据库不存在此主键,则会执行insert,并返回一个新的持久化对象
    3. 传入entity若有主键,且数据库已存在此主键,则进行update操作,并返回一个新的持久化对象

    注意,第三种情况这里有个大坑

    如果你传入的游离对象,没有填入所有属性,那么这些属性会在update时被填入Null,

    即使加入@DynamicUpdate也没用

    这是因为merge()第三种情况update时存在一个属性拷贝,具体可以参照流程图,不详细介绍

    流程图:

    用merage()模拟update操作:(不知道有啥用)

    且要先将所更新的对象查出,也就是我们要将更改的数据填入查询出对象中,对其重新持久化(这里面也存在一个复制操作),而不是直接传入一个新建对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Test
    public void testMerge() {
    Customer foundedCustomer = entityManager.find(Customer.class, 13);
    System.out.println("-------After find()");
    System.out.println("foundedCustomer:" + foundedCustomer);
    if (foundedCustomer == null) {
    System.out.println("米有此条目");
    }
    //更新字段
    foundedCustomer.setLastName("13");

    Customer mergedCustomer = entityManager.merge(foundedCustomer);
    System.out.println("-------After merge()");
    System.out.println("mergedCustomer:" + mergedCustomer);

    //提交
    transaction.commit();
    System.out.println("-------After commit()");
    System.out.println("mergedCustomer:" + mergedCustomer);
    }

    console打印的主要信息

    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
    Hibernate: 
    select
    customer0_.id as id1_0_0_,
    customer0_.age as age2_0_0_,
    customer0_.birthday as birthday3_0_0_,
    customer0_.create_time as create_t4_0_0_,
    customer0_.email as email5_0_0_,
    customer0_.last_name as last_nam6_0_0_,
    customer0_.update_time as update_t7_0_0_
    from
    jpa_customers customer0_
    where
    customer0_.id=?
    -------After find()
    foundedCustomer:Customer(id=13, email=0, lastName=11, age=0, createTime=2019-08-23 18:32:25.908, updateTime=2019-08-23 19:00:18.515, birthday=2019-08-23)
    -------After merge()
    mergedCustomer:Customer(id=13, email=0, lastName=13, age=0, createTime=2019-08-23 18:32:25.908, updateTime=2019-08-23 19:00:18.515, birthday=2019-08-23)
    Hibernate:
    update
    jpa_customers
    set
    age=?,
    last_name=?,
    update_time=?
    where
    id=?
    -------After commit()//注意提交后的updateTime字段
    mergedCustomer:Customer(id=13, email=0, lastName=13, age=0, createTime=2019-08-23 18:32:25.908, updateTime=Fri Aug 23 19:01:54 CST 2019, birthday=2019-08-23)
  • 由以上实验得出结论:

    我们在做更新操作时,merge()之后,其返回的持久化对象实际并没有更新,因为没有提交事务,还未与数据库通信。

    我们要想安全的使用更新后的对象(保证所有字段与数据库相同,如例子中的updateTime字段)就必须在transaction.commit();之后使用merge()所返回的对象

  • 其余一些常用方法:

    • flush ():同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。
    • refresh (Object entity):用数据库实体记录的值更新实体对象的状态,即更新实例的属性值。
    • clear ():清除持久上下文环境,断开所有关联的实体。如果这时还有未提交的更新则会被撤消。
    • contains (Object entity):判断一个实例是否属于当前持久上下文环境管理的实体。
    • isOpen ():判断当前的实体管理器是否是打开状态。
    • getTransaction ():返回资源层的事务对象。EntityTransaction实例可以用于开始和提交多个事务。
    • close ():关闭实体管理器。之后若调用实体管理器实例的方法或其派生的查询对象的方法都将抛出 IllegalstateException 异常,除了getTransaction 和 isOpen方法(返回 false)。不过,当与实体管理器关联的事务处于活动状态时,调用 close 方法后持久上下文将仍处于被管理状态,直到事务完成。
  • JPQL方法:

    • createQuery (String qlString):创建一个查询对象。
    • createNamedQuery (String name):根据命名的查询语句块创建查询对象。参数为命名的查询语句。
    • createNativeQuery (String sqlString):使用标准 SQL语句创建查询对象。参数为标准SQL语句字符串。
    • createNativeQuery (String sqls, String resultSetMapping):使用标准SQL语句创建查询对象,并指定返回结果集 Map的 名称。

4. EntityTransaction

EntityTransaction 接口用来管理资源层实体管理器的事务操作。通过调用实体管理器的getTransaction方法 获得其实例。

  • begin () 用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛出 IllegalStateException 异常。

  • commit () 用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。

  • rollback () 撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作,从而不对数据库产生影响。

  • setRollbackOnly () 使当前事务只能被撤消。

  • getRollbackOnly () 查看当前事务是否设置了只能撤消标志。

  • isActive () 查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛出 IllegalStateException 异常;如果返回 false 则不能调用 commit、rollback、setRollbackOnly 及 getRollbackOnly 方法,否则将抛出 IllegalStateException 异常。