JPA 概述
JPA 介绍
JPA 本质上就是一种 ORM 规范,不是 ORM 框架,因为 JPA 并未提供 ORM 实现,它只是制定了一套规范,提供了一些接口,具体实现由 ORM 厂商决定。
JPA 包括三方面的技术
ORM 映射元数据
JPA API
查询语言(JPQL)
使用 JPA 持久化对象的步骤
创建 EntityMangerFactory
通过 EntityManagerFactory 获得 EntityManager 实例
开始事务
执行持久化操作
提交事务
关闭 EntityManager
关闭 EntityManagerFactory
// 创建 EntityManageFactory
String persistenceUnitName = "NewPersistenceUnit";
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
// 创建 EntityManage
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 开始事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// 执行持久化操作
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("[email protected]");
customer.setLastName("tom");
entityManager.persist(customer);
// 提交事务
transaction.commit();
// 关闭 EntityManage
entityManager.close();
// 关闭 EntityManageFactory
entityManagerFactory.close();
persistence.xml
JPA 规范要求在类路径的 META-INF 目录下放置persistence.xml,文件的名称是固定的。
org.hibernate.ejb.HibernatePersistence
com.kernel.jpa.helloworld.Customer
JPA 的基本注解
@Entity
标记一个类为实体类,添加该注解后,会在数据库中创建一个和类名相同的表。
@Transient
使用该注解忽略工具方法,不需要映射为数据表的一列。
@Temporal
设置 Date 类型的精度。
DATE:精确到年月日
TIME:精确到时分秒
TIMESTAMP:精确到年月日时分秒
@Table
当实体类与其映射的数据表名称不一致时,需要使用该注解标注,该注解与 @Entity 标注并列使用,置于实体类注解之前。
name:数据库的表名
catalog:数据库目录
schema:数据库模式
@ID
该注解声明一个实体类的属性映射为数据库的主键,一般我们将其标注在 getter 方法上。
@GeneratedValue
该注解用来设置主键的生成策略,通过 strategy 属性指定
有四种策略:
IDENTITY:采用数据库 ID 自增长的方式来自增主键字段,Oracle 不支持。
AUTO:JPA 自动选择合适的策略,默认选项。
SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySQL 不支持。
TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
@Cloumn
当实体类的属性与其映射的数据表的列不同名时需要标注该注解。
name:数据表的列名。
unique:唯一约束。
nullable:是否可以为空。
length:列的长度。
columnDefinition
@Basic
表示基本注解,默认为没有标注任何注解的 getter 方法添加该注解。
JPA API
Persistence
该类包含一个名为 createEntityManagerFactory 的 静态方法 ,用来获取 EntityManagerFactory。
EntityManagerFactory
EntityManagerFactory 接口主要用来创建 EntityManager 实例,提供了四个方法:
createManagerFactory() 用来创建 EntityManager。
createManagerFactory(Map map) Map 参数用来提供 EntityManager 的属性。
isOpen() 检查 EntityManagerFactory 是否处于打开状态。
close() 关闭 EntityManagerFactory。
EntityManager
在 JPA 中,EntityManager 是完成持久化操作最核心的对象,实体作为一个普通的 Java 对象,只有在调用EntityManager 将其持久化后才会变成一个持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新、根据主键查找、还可以根据 JPQL 查询实体。
实体的状态:
新建状态:新创建的对象,尚未拥有持久化主键。
持久化状态:已经拥有持久化主键并和持久化建立了上下文环境。
游离状态:已经拥有了持久化主键但是没有和持久化建立上下文环境。
删除状态:已经拥有了持久化主键并和持久化建立了上下文环境,但是从数据库中被删除。
find(Class
getReference (Class
persist (Object entity):将创建的实体对象变成持久化状态。
remove (Object entity):删除实例。
merge (T entity):数据库的插入和更新操作:
如果传入的对象是一个临时对象,即没有 id 的对象,JPA 创建一个新的对象,并复制临时对象的属性到新对象中,将新对象执行持久化操作。
如果传入的对象是一个游离对象,即存在 id 的对象,首先查询缓存中是否存在 id 对象的持久化对象,如果存在,将游离对象的属性拷贝到新创建的对象中,并执行更新操作;如果不存在,就查询数据库中是否存在 id 对应的记录,如果存在,将游离对象的属性拷贝到新创建的对象中,并执行更新操作;如果不存在,JPA 创建一个新的对象,并复制临时对象的属性到新对象中,将新对象执行持久化操作。
flush():同步持久化上下文环境,将持久化上下文环境中未保存实体的状态信息保存到数据库中。
setFulshMode(FlushModeType flushMode):设置持久化上下文的 Flush 模式:
FlushModeType.AUTO 为自动更新数据库实体。
FlushModeType.COMMIT 为直到提交事务时才更新数据库记录。
getFlushMode() 获取持久化上文的 Flush 模式。
refresh() 用数据库实体记录的值更新实体对象的状态。
clear() 清除持久化上下文环境,断开所有关联的实体。
contains() 判断一个实例是否属于当前持久化上下文环境管理的实体。
isOpen() 查询 EntityManager 是否处于打开状态
getTransaction() 获得一个事务。
close() 关闭 EntityManager 实例。
EntityTransaction
begin() 开启事务。
commit() 提交事务。
rollback() 回滚事务。
setRollbackOnly() 使当前事务只能被撤消。
getRollbackOnly() 查看当前事务是否设置了只能撤消标志。
isActive() 查看当前事务是否是活动的,如果是,则不能调用 begin 方法,否则将抛出 IllegalStateException 异常;如果不是则不能调用commit、rollback、setRollbackOnly 及 getRollbackOnly 方法,否则将抛出 IllegalStateException 异常。
映射关联关系
映射双向一对多及多对一的关联关系
双向一对多关系中,必须存在一个关系维护端,在 JPA 规范中,要求 many 的一方作为关系的维护端(owner side),one 的一方作为被维护端(inverse side)。
可以在 one 方指定 @OneToMany 注释并设置 mappedBy 属性,以指定它是这一关联中的被维护端,many 为维护端。
在 many 方指定 @ManyToOne 注释,并使用 @JoinColumn 指定外键名称。
使用 mappedBy 设置维护段的字段。
映射双向一对一的关联关系
基于外键的 1-1 关联关系:在双向的一对一关联中,需要在关系被维护端(inverse side)中的 @OneToOne 注释中指定 mappedBy,以指定是这一关联中的被维护端。同时需要在关系维护端(owner side)建立外键列指向关系被维护端的主键列。
在维护端设置 unique 设置为 true。
如果延迟加载要起作用, 就必须设置一个代理对象。
Manager 其实可以不关联一个 Department。
如果有 Department 关联就设置为代理对象而延迟加载,如果不存在关联的 Department 就设置 null,因为外键字段是定义在 Department 表中的,Hibernate 在不读取 Department 表的情况是无法判断是否有关联有 Deparmtment,因此无法判断设置 null 还是代理对象,而统一设置为代理对象,也无法满足不关联的情况,所以无法使用延迟加载,只有显式读取 Department。
映射双向多对多的关联关系
在双向多对多关系中,我们必须指定一个关系维护端(owner side),可以通过 @ManyToMany 注释中指定 mappedBy 属性来标识其为关系维护端。
@ManyToManybr/>@JoinTable(name="中间表名称",
joinColumns=@joinColumn(name="本类的外键",inversejoinColumns=@JoinColumn(name="对方类的外键",
br/>referencedColumnName="本类与外键对应的主键"),
inversejoinColumns=@JoinColumn(name="对方类的外键",
referencedColunName="对方类与外键对应的主键")
)
使用二级缓存
\
ALL:所有的实体类都被缓存。
NONE:所有的实体类都不被缓存。
ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存。
DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类。
UNSPECIFIED:默认值,JPA 产品默认值将被使用。
org.hibernate.ejb.HibernatePersistence
com.kernel.jpa.helloworld.Customer
com.kernel.jpa.helloworld.Order
com.kernel.jpa.helloworld.Manager
com.kernel.jpa.helloworld.Department
ENABLE_SELECTIVE
JPQL
JPQL语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异。
JPQL语言的语句可以是 select 语句、update 语句或delete语句,它们都通过 Query 接口封装执行。
javax.persistence.Query
Query接口封装了执行数据库查询的相关方法。调用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以获得查询对象,进而可调用 Query 接口的相关方法来执行查询操作。
Query接口的主要方法:
int executeUpdate() 用于执行update或delete语句。
List getResultList() 用于执行select语句并返回结果集实体列表。
Object getSingleResult() 用于执行只返回单个结果实体的select语句。
Query setFirstResult(int startPosition) 用于设置从哪个实体记录开始返回查询结果。
Query setMaxResults(int maxResult) 用于设置返回结果实体的最大数。与setFirstResult结合使用可实现分页查询。
Query setFlushMode(FlushModeType flushMode) 设置查询对象的Flush模式。参数可以取2个枚举值:FlushModeType.AUTO 为自动更新数据库记录,FlushMode Type.COMMIT 为直到提交事务时才更新数据库记录。
使用 Hibernate 的查询缓存
String jpql = "FROM Customer c WHERE c.age > ?";
// 设置查询缓存
Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);
// 占位符的索引从1开始
query.setParameter(1, 2);
List resultList = query.getResultList();
resultList = query.getResultList();
关联查询
JPQL 也支持和 SQL 中类似的关联语法。如:
left out join / left join
inner join
left join / inner join fetch
其中,left join和left out join等义,都是允许符合条件的右边表达式中的实体为空。
子查询和 JPQL 函数
JPQL也支持子查询,在 where 或 having 子句中可以包含另一个查询。当子查询返回多于 1 个结果集时,它常出现在 any、all、exist s表达式中用于集合匹配查询。它们的用法与SQL语句基本相同。
JPQL提供了以下一些内建函数,包括字符串处理函数、算术函数和日期函数。
字符串处理函数主要有:
concat(String s1, String s2):字符串合并/连接函数。
substring(String s, int start, int length):取字串函数。
trim([leading|trailing|both,] [char c,] String s):从字符串中去掉首/尾指定的字符或空格。
lower(String s):将字符串转换成小写形式。
upper(String s):将字符串转换成大写形式。
length(String s):求字符串的长度。
locate(String s1, String s2[, int start]):从第一个字符串中查找第二个字符串(子串)出现的位置。若未找到则返回0。
整合 Spring
三种整合方式:
LocalEntityManagerFactoryBean:适用于那些仅使用 JPA 进行数据访问的项目,该 FactoryBean 将根据JPA PersistenceProvider 自动检测配置文件进行工作,一般从“META-INF/persistence.xml”读取配置信息,这种方式最简单,但不能设置 Spring 中定义的DataSource,且不支持 Spring 管理的全局事务
从JNDI中获取:用于从 Java EE 服务器获取指定的EntityManagerFactory,这种方式在进行 Spring 事务管理时一般要使用 JTA 事务管理
LocalContainerEntityManagerFactoryBean:适用于所有环境的 FactoryBean,能全面控制 EntityManagerFactory 配置,如指定 Spring 定义的 DataSource 等等。
true
true
update