天天看点

第三章、实体操作(JPA规范)

本章介绍了如何使用EntityManager API 管理实体实例生命周期以及如何使用查询API来检索和查询实体及其持久状态。

文章目录

    • 3.1、EntityManager (实体管理器)
      • 3.1.1 EntityManager 接口
      • 3.1.2、实体管理API使用的例子
    • 3.2、实体实例的生命周期
      • 3.2.1、实体实例创建
      • 3.2.2、 持久化一个实体实例
      • 3.2.3、移除
      • 3.2.4、与数据库同步
      • 3.2.5、刷新实体实例
      • 3.2.6、从持久性上下文中去掉实体实例
      • 3.2.7、分离的实体
        • 3.2.7.1、合并Detached(分离)实体状态
        • 3.2.7.2、Detached(分离)实体和延迟加载
      • 3.2.8、托管(managed)实例
      • 3.2.9、加载状态
    • 3.3、持久性上下文生命周期和同步类型
      • 3.3.1、使用当前事务进行同步
      • 3.3.2、事务提交
      • 3.3.3、事务回滚
    • 3.4、锁和并发
      • 3.4.1、乐观锁
      • 3.4.2、Version属性
      • 3.4.3、悲观锁
      • 3.4.4、锁定模式
        • 3.4.4.1、OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT
        • 3.4.4.2、PESSIMISTIC_READ, PESSIMISTIC_WRITE, PESSIMISTIC_FORCE_INCREMENT
        • 3.4.4.3、锁模式属性和使用
      • 3.4.5、OptimisticLockException (乐观锁定异常)
    • 3.5、实体监听器和回调方法
      • 3.5.1、实体监听器
      • 3.5.2、生命周期回调方法
      • 3.5.3、实体生命周期回调方法的语法
      • 3.5.4、例子
      • 3.5.5、实体生命周期事件的多种生命周期回调方法。
      • 3.5.6、例子
      • 3.5.7、异常
      • 3.5.8、XML描述符中的回调监听器类和生命周期的方法规范
        • 3.5.8.1、回调监听器的规范
        • 3.5.8.2、实体监听器与实体的绑定规范
    • 3.6、 Bean验证
      • 3.6.1、生命周期事件的自动验证
        • 3.6.1.1、开启自动验证
        • 3.6.1.2、生命周期事件自动验证的要求
      • 3.6.2 提供ValidatorFactory
    • 3.7、实体图
      • 3.7.1、EntityGraph 接口
      • 3.7.2、AttributeNode 接口
      • 3.7.3、Subgraph 接口
      • 3.7.4、在查询操作中使用实体图
        • 3.7.4.1、Fetch Graph Smantics(获取图语法)
        • 3.7.4.1、加载图语法
    • 3.8、基本属性的类型转换
    • 3.9、缓存
      • 3.9.1、shared-cache-mode 元素
      • 3.9.2、缓存检索模式和缓存存储模式属性
    • 3.10 查询 APIs
      • 3.10.2、TypedQuery 接口
      • 3.10.3、 Tuple接口(元组接口)
      • 3.10.4、TupleElement 接口
      • 3.10.5、Parameter Interface ( 参数接口)
      • 3.10.6 StoredProcedureQuery Interface
      • 3.10.7、Query Execution ( 查询执行)
        • 3.10.7.1、例子
      • 3.10.8、查询和Flush模式
      • 3.10.查询和锁定模式
      • 3.10.10、查询提示
      • 3.10.11、参数对象
      • 3.10.12、命名参数
      • 3.10.13、位置参数
      • 3.10.14 命名查询
      • 3.10.15、多态查询
      • 3.10.16、SQL查询
        • 3.10.16.1、从原生查询返回托管实体
        • 3.10.16.2、返回非托管实体
          • 3.10.16.2.1、标量结果
          • 3.10.16.2.2、构造结果
        • 3.10.16.3、合并结果类型
        • 3.10.16.4、限制
      • 3.10.17 存储过程
        • 3.10.17.1、命名式存储过程查询
        • 3.10.17.2、动态指定存储过程
      • 3.10.17.3、存储过程的执行
    • 3.11、异常总结

3.1、EntityManager (实体管理器)

  • EntityManager 实例关联一个持久上下文信息, 持久性上下文信息包含实体实例的集合,其中对于任何持久性实体标识,都有一个唯一的实体实例。在这个持久性上下文中,实体实例和他们生命周期都可以被管理, EntityManger接口定义用于与持久性上下文交互的方法,EntityManager的API可以用于创建和移除持久实体实例,通过主键查询持久性实体和遍历所有持久实体。
  • 实体集合可以被定义在一个持久单元的EntityManager实例进行管理。持久性单元定义了与应用程序相关或分组的所有类的集合,并且这些类必须在它们到单个数据库的映射中共置。
  • 3.1节定义EntityManager接口
  • 3.2节描述实体生命周期
  • 3.3节 描述EntityManager 与持久性上下文的关系,进一步的详细参看第7章
  • 3.4节描述并发控制的机制和锁
  • 3.5节描述实体监听器和生命周期回调方法
  • 3.6节Bean Validation(Bean验证)自动验证的支持
  • 3.7节描述使用实体图去控制路径和查询操作的边界
  • 3.8节描述实体与数据库之间基本数据类型属性的约定
  • 3.9节描述可移植二级缓存的配置
  • 3.10节描述Query, TypedQuery,StoredProcedureQuery和关联接口
  • 3.11节描述提供的异常概览
  • java持久性查询语言定义在第4章,API的Criteria的构造查询在第6章介绍,定义持久化单元在第8章描述。

3.1.1 EntityManager 接口

  • package javax.persistence;
    import java.util.Map;
    import java.util.List;
    import javax.persistence.metamodel.Metamodel;
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.CriteriaQuery;
    import javax.persistence.criteria.CriteriaUpdate;
    import javax.persistence.criteria.CriteriaDelete;
    
    /**
    *
    * 该接口用于与持久上下文交互且创建执行查询对象
    *
    *
    */
    public interface EntityManager{
      
      /**
      * 管理实例和持久化
      * @param entity 
      * @throws EntityExistsException  如果实体已经存在(如果实体已经存在,那么
      * 在做持久化调用时候将可能抛出EntityExistsException异常或是在flush或提交时候也会
      * 抛出EntityExistsException或其他PersistenceException异常)
      * @throws IllegalArgumentException 如果这个实例不是实体
      * @throws TransactionRequiredException 这里没有事务标记,但是在调用容器管理
      *实体管理器它标记为类型为PersistenceContextType.TRANSACTION 
      *(也就需要事务标记才能调用)
      *
      *
      */
      public void persist(Object entity);
      
      /**
      * 合并给定实体的状态到当前持久化上下文中。
      * @param entity
      * @return 返回合并状态的实体对象
      * @throws IllegalArgumentException 如果实例不是实体或它一个已经移除的实体
      * @throws TransactionRequiredException 这里没有事务标记,但是在调用容器管理
      *实体管理器它标记为类型为PersistenceContextType.TRANSACTION 
      *(也就需要事务标记才能调用)
      *
      *
      */
      public <T> T merge(T entity);
      
      /**
      *
      * 移除该实体
      * @param entity
      * @throws IllegalArgumentException 如果这个实例不是实体或它是一个分离的实体
      * @throws TransactionRequiredException 这里没有事务标记,但是在调用容器管理
      * 实体管理器它标记为类型为PersistenceContextType.TRANSACTION 
      * (也就需要事务标记才能调用)
      */
      public void remove(Object entity);
      
      /**
      * 通过主键查询实体
      * 如果实体实例存在于上下文持久性,如果存在就返回
      * @param entityClass
      * @param primaryKey
      * @return 如果实体不存在在返回null,否则返回主键对应的实体
      * @throws IllegalArgumentException 如果第一个参数不是实体类型或第二参数类型
      * 不是有效类型或为null
      *
      */
      public <T> T find(Class<T> entityClass, Object primaryKey);
      
      
      /**
      * 通过主键和特定属性查询
      * 如果实体实例存在于上下文持久性,如果存在就返回
      * 如果特定供应商属性不识别,将默认忽略
      * @param entityClass
      * @param primaryKey
      * @param properties 标准和特定供应商的属性和提示
      * @return 返回查询实体或如果实体不存在则返回
      * @throws IllegalArgumentException 如果第一个参数不是实体类型或第二参数类型
      * 不是有效类型或为null
      */
      public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties);
      
      
      
      /**
      * 通过主键和锁查询
      * 搜索指定类和主键的实体,并根据指定的锁定类型对其进行锁定
      * 如果实体实例包含在持久性上下文中, 则从那里返回它,并且此方法的效果与在实体上
      * 调用lock方法的效果相同。
      * 如果在持久性上下文中找到该实体,并且锁定模式类型是悲观的,并且该实体具有版本属性,
      * 则持久提供程序在获取数据库锁定时必须执行乐观版本检查。如果这些检查失败,则将引发
      * OptimisticLockException
      * 如果锁定模式类型为悲观模式,并且找到了实体实例,但无法将其锁定:
      * - 如果数据库锁定失败导致事务级回滚,则将引发PessimisticLockException
      * - 如果数据库锁定失败仅导致语句级回滚,则将抛出LockTimeoutException
      * @param entityClass
      * @param primaryKey
      * @param lockMode
      * @return 返回实体,如果不存返回null
      * @throws IllegalArgumentException 如果第一个参数不是实体类型或第二参数类型
      * 不是有效类型或为null
      * @throws TransactionRequiredException 如果没有事务并且制定了NONE以外的锁定
      * 模式, 或者在尚未加入当前事务的实体管理器上调用并且指定了NONE以外的锁定模式
      * @throws OptimisticLockException 如果乐观版本检查失败
      * @throws PessimisticLockException 如果悲观锁定失败和事务回滚
      * @throws LockTimeoutException 如果悲观失败和只有语句回滚
      * @throws PersistenceException 不支持锁定调用
      */
      public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode);
      
      
      /**
      * 使用指定属性按主键查询并锁定,搜索指定类和主键的实体, 并根据指定的锁定类型对其
      *进行锁定
      * 如果持久上下文包含该实体,则返回,如果在持久上下文中找到该实体,并且锁定模式类型是
      * 悲观的,并且该实体具有版本属性,则持久性提供程序在获取数据库锁定时必须执行乐观版本
      * 检查。如果这些检查失败,则将引发OptimisticLockException.
      * 如果锁定模式类型为悲观模式,并且找到了实体实例,但无法锁定该实体实例:
      *		-如果数据库锁定失败导致事务级回滚,则将引发PessimisticLockException
      *   -如果数据库锁定失败导致语句级回滚,则将引发LockTimeoutException
      * 如果特定供应商属性不识别,将默认忽略
      * 可移植应用程序不应依赖于标准超时提示, 根据使用的数据库和提供者使用的锁定机制,
      * 提供可能会或可能不会被观察到
      *
      * @param entityClass
      * @param primaryKey
      * @param lockMode
      * @param properties 标准和特定供应商属性和提示
      * @return 返回实体或如果实体不存在在返回null
      * @throws IllegalArgumentException 如果第一个参数不是实体类型或第二参数
      * 实体的主键是无效类型或为null
      * @throws TransactionRequiredException 如果没有事务并且制定了NONE以外的锁定
      * 模式,或者在尚未加入当前事务的实体管理器上调用并且指定了NONE以外的锁定模式。
      * @throws OptimisticLockException 如果乐观锁版本检查失败
      * @throws PessimisticLockException 如果悲观锁锁定失败和事务回滚
      * @throws LockTimeoutException 如果悲观锁定失败和只有语句回滚
      * @throws PersistenceException 不支持锁定调用
      *
      */
      public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> propertis);
      
      
      
      /**
      * 获取实体,实体状态可能延迟加载, 如果数据库中请求的实体不存在,那么在首次访问
      * 实体状态时抛出EntityNotFoundException抛出。(持久提供商允许调用getReference
      * 时抛出 EntityNotFoundException
      * 应用程序不应期望实体状态在分离后将可用,除非在打开实体管理器时由应用程序访问了实体状态
      * @param entityClass
      * @param primaryKey
      * @return 返回一个实体实例
      * @throws IllegalArgumentException 如果第一个参数不是实体类型或第二参数
      * 实体的主键是无效类型或为null
      * @throws EntityNotFoundException 如果无法访问实体状态
      *
      */
      public <T> getReference(Class<T> entityClass, Object primaryKey);
      
      
      
      /**
      * 将持久性上下文同步到基础数据库
      * @throws TransactionRequiredException 没有事务 或 实体管理器并没加入当前事务
      * @throws PersistenceException 如果flush失败
      *
      */
      public void flush();
      
      
      
      /**
    	* 设置flush模式,并将其应用所有持久上下文中的对象
    	*
    	* @param flushMode
    	*
    	*/
      public void setFlushMode(FlushModeType flushMode);
      
      /**
      *  获取flush模式, 并将其应用所有持久上下文中的对象
      * @return flushMode
      */
      public FlushModeType getFlushMode();
      
      
      /**
    * 在持久上下文中用特定锁定模式类型锁定实体实例对象。
      * 如果是一个悲观锁模式类型,持久层提供商必须在获取数据库锁时要乐观锁版本号检测操作
      * 如果检测失败,那么OptimisticLockException 异常将会抛出
      * 如果锁模式类型是悲观锁且这个实体实例可以找到但是不能锁定时:
      * - 如果数据库锁定失败导致事务级别的回滚,那么PessimisticLockException异常
      * - 如果数据库锁定失败导致语句级别的回滚,那么LockTimeoutException异常
      * @param entity
      * @param lockMode
      * @throws IllegalArgumentException 如果实例不是实体或实体被分离时
      * @throws TransactionRequiredException 如果这个没有事务或调用实体管理器时候没
      * 有加入当前的事务
      * @throws EntityNotFoundException 当悲观锁操作时实体并不存在数据库中
      * @throws OptimisticLockException  如果乐观版本检查失败
      * @throws PessimisticLockException 如果悲观锁锁定失败且事务回滚
      * @throws LockTimeoutException  如果悲观锁锁定失败且语句基本事务回滚
      * @throws PersistenceException  不支持锁定调用时
      */
      public void lock(Object entity, LockModeType lockMode);
      
      
      
      
      
       /**
      * 在持久上下文中用特定锁定模式类型锁定实体实例对象,并且这些对象包含特定属性
      * 如果是一个悲观锁模式类型,持久层提供商必须在获取数据库锁时要乐观锁版本号检测操作
      * 如果检测失败,那么OptimisticLockException 异常将会抛出
      * 如果锁模式类型是悲观锁且这个实体实例可以找到但是不能锁定时:
      * - 如果数据库锁定失败导致事务级别的回滚,那么PessimisticLockException异常
      * - 如果数据库锁定失败导致语句级别的回滚,那么LockTimeoutException异常
      * 如果特定供应商属性或提示并不能识别,那么就会静默忽略。
      * 可移植应用不应该依赖于标准超时提示,根据使用的数据库和提供者使用的锁定机制,提示可
      * 会或可能不会被观察到
      * @param entity
      * @param lockMode
      * @param properties 标准和特定厂商属性和提示
      * @throws IllegalArgumentException 如果实例不是实体或实体被分离时
      * @throws TransactionRequiredException 如果这个没有事务或调用实体管理器时候没
      * 有加入当前的事务
      * @throws EntityNotFoundException 当悲观锁操作时实体并不存在数据库中
      * @throws OptimisticLockException  如果乐观版本检查失败
      * @throws PessimisticLockException 如果悲观锁锁定失败且事务回滚
      * @throws LockTimeoutException  如果悲观锁锁定失败且语句基本事务回滚
      * @throws PersistenceException  不支持锁定调用时
      */
      public void lock(Object entity, LockModeType lockMode, Map<String,Object> properties);
      
      
      
      /**
      * 从数据库刷新实体的最新状态,如果有任何变化都将进行实体重写
      * @param entity
      * @throws IllegalArgumentException 如果实例不是实体或实体没有被管理
      * @throws TransactionRequiredException  调用时候没有事务,但是容器管理实体标记
      * 为 PersistenceContextType.TRANSACTION
      * @throws EntityNotFoundException 实体不存在于数据库中
      */
      public void refresh(Object entity);
      
      
      
       /**
      * 使用特定属性从数据库刷新实体的最新状态,如果有任何变化都将进行实体重写
      * 如果特定供应商属性或提示并不能识别,那么就会静默忽略。
      * @param entity
      * @param properties 标准和特定厂商属性和提示
      * @throws IllegalArgumentException 如果实例不是实体或实体没有被管理
      * @throws TransactionRequiredException  调用时候没有事务,但是容器管理实体标记
      * 为 PersistenceContextType.TRANSACTION
      * @throws EntityNotFoundException 实体不存在于数据库中
      */
      public void refresh(Object entity, Map<String, Object> properties);
      
       /**
      * d从数据库刷新实体的最新状态,如果有任何变化都将进行实体重写,且使用给定锁定模式类型
      * 进行锁定
      * 如果锁模式类型是悲观锁且这个实体实例可以找到但是不能锁定时:
      * - 如果数据库锁定失败导致事务级别的回滚,那么PessimisticLockException异常
      * - 如果数据库锁定失败导致语句级别的回滚,那么LockTimeoutException异常
      * @param entity
      * @param lockMode 
      * @throws IllegalArgumentException 如果实例不是实体或实体没有被管理
      * @throws TransactionRequiredException  调用时候没有事务,但是容器管理实体标记
      * 为 PersistenceContextType.TRANSACTION, 如果调用一个扩展实体管理且它们没有事
      * 务,但是锁定模式不是指定为NONE或调用一个扩展实体管理且它没有加入当前事务但是锁定模
      * 式不是指定为NONE
      * @throws EntityNotFoundException 实体不存在于数据库中
      * @throws PessimisticLockException 如果悲观锁锁定失败且事务回滚
      * @throws LockTimeoutException  如果悲观锁锁定失败且语句基本事务回滚
      * @throws PersistenceException  不支持锁定调用时
      */
      public void refresh(Object entity, LockModeType lockMode);
      
      
       /**
      * d从数据库刷新实体的最新状态,如果有任何变化都将进行实体重写,且使用给定锁定模式类型
      * 和特定属性进行锁定
      * 如果锁模式类型是悲观锁且这个实体实例可以找到但是不能锁定时:
      * - 如果数据库锁定失败导致事务级别的回滚,那么PessimisticLockException异常
      * - 如果数据库锁定失败导致语句级别的回滚,那么LockTimeoutException异常
      * 如果特定供应商属性或提示并不能识别,那么就会静默忽略。
      * 可移植应用不应该依赖于标准超时提示,根据使用的数据库和提供者使用的锁定机制,提示可
      * 会或可能不会被观察到
      * @param entity
      * @param lockMode
      * @param properties 标准和特定厂商属性和提示
      * @throws IllegalArgumentException 如果实例不是实体或实体没有被管理
      * @throws TransactionRequiredException  调用时候没有事务,但是容器管理实体标记
      * 为 PersistenceContextType.TRANSACTION, 如果调用一个扩展实体管理且它们没有事
      * 务,但是锁定模式不是指定为NONE或调用一个扩展实体管理且它没有加入当前事务但是锁定模
      * 式不是指定为NONE
      * @throws EntityNotFoundException 实体不存在于数据库中
      * @throws PessimisticLockException 如果悲观锁锁定失败且事务回滚
      * @throws LockTimeoutException  如果悲观锁锁定失败且语句基本事务回滚
      * @throws PersistenceException  不支持锁定调用时
      */
      public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties);
      
      
      /**
      * 清除持久上下文信息, 导致所有管理的实体将会被分离出来
      * 实体的变更也不会同步到数据进行持久化
      *
      *
      */
      public void clear();
      
      
      /**
      * 从持久上下文信息移除给定实体,导致一个管理的实体成独立出来,如果有任何(包含移除实
      * 体)不会刷新变更到实体中,它将不会同步给数据库,实体前引用将会继续引用独立出来的实
      * 体
      * @param entity
      * @throws IllegalArgumentException 如果这个实例不是实体
      *
      */
      public void detach(Object entity);
      
    
      /**
      * 判断传入实体是不是当前持久上下文中管理的实体对象
      * @param entity
      * @return boolean 表明是否存在实体
      * @throws IllegalArgumentException 如果不是实体
      */
      public boolean contains(Object entity);
      
      
      /**
      * 为实体获取当前锁模式
      * @param entity
      * @return lock mode
      * @throws TransactionRequiredException  如果当前没有事务或实体管理器并没加入当
      * 前事务
      * @throws IllegalArgumentException 如果实体不是一个被管理实体且事务被激活了
      */
      public LockModeType getLockMode(Object entity);
      
      
      /**
      * 设置实体管理器属性或提示,如果特定厂商的属性或提示识别不了, 这个将被忽略
      * @param propertyName 属性名称或提示
      * @param value 值
      * @throws IllegalArgumentException 如果第二参数不是有效值
      *
      */
      public void setProperty(String propertyName, Object value);
      
      
      /**
      *  获取实体管理有影响所有属性和提示关联到值,改变这个Map的内容不会影响已经配置生效的
      * @return 生效的属性和提示的map
      *
      *
      */
      public Map<String,Object> getProperties();
      
      
      /**
      * 创建一个用于执行java持久查询语言语句的查询实体
      * @param qlString 一个Java持久查询字符串
      * @ return 新查询实例
      * @throws IllegalArgumentException 如果查询字符串是无效的
      *
      */
      public Query createQuery(String qlString);
      
      
      /**
      * 创建一个TypedQuery实例用于查询标准(criteria)查询
      * @param criteriaQuery 一个标准查询对象
      * @return 返回新查询实体
      * @throws IllegalArgumentException 如果标准查询是无效的
      */
      public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery);
      
      
      /**
      * 创建一个Query实例用于查询标准(criteria)更新查询
      * @param updateQuery 一个标准更新查询对象
      * @return 返回新查询实体
      * @throws IllegalArgumentException 如果标准更新查询是无效的
      */
      public Query createQuery(CriteriaUpdate updateQuery);
      
        /**
      * 创建一个Query实例用于查询标准(criteria)删除查询
      * @param deleteQuery 一个标准删除查询对象
      * @return 返回新查询实体
      * @throws IllegalArgumentException 如果标准删除查询是无效的
      */
      public Query createQuery(CriteriaDelete deleteQuery);
      
      
      /**
      * 创建TypedQuery实例执行java持久查询语言语句. 查询列表必须要至少一条数据,且
      * 数据类型必须关联到resultClass 参数
      * @param qlString 一个java持久查询语言
      * @param resultClass 查询结果的类型
      * @return 新的查询对象
      * @throws IllegalArgumentException 如果qlString不是有效语句或查询结果不能关联
      * 到 resultClass类型
      */
      public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass);
      
      
      /**
      * 创建一个Query查询实例执行一个名称查询(在java持久查询语言或原生SQL语句)
      * @param name 定义在元数据中查询的名称
      * @return 新查询实例
      * @throws IllegalArgumentException  如果查询被没有定义的名称或查询语句无效
      *
      */
      public Query createNameQuery(String name);
      
      
      /**
      * 创建一个TypedQuery实例执行java 持久查询语言名称查询,查询列表必须要至少一条数
      * 据,且数据类型必须关联到resultClass 参数
      * @param name 查询语句的名称定义在元数据中
      * @param resultClass 查询结果的类型
      * @return 新查询对象
      * @throws IllegalArgumentException 如果qlString不是有效语句或查询结果不能关联
      * 到 resultClass类型
      */
      public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass);
      
      /**
      * 创建一个Query实例执行原生SQL语言,例如update或delete。
      * 如果查询不是一个update 或 delete查询,查询执行将会产生每一行结果都会返回,与之对
      * 应是Object[](如果选择列表中只有一列,则返回类型为Object的结果)。按值在选择列表中
      * 的出现顺序返回列值,并应用默认的JDBC类型映射。
      * @param sqlString 一个原生的查询SQL
      * @return 新的查询实体
      */
      public Query createNativeQuery(String sqlString);
      
      /**
      * 创建一个Query执行原生SQL查询
    	* @param sqlString 一个原生查询sql语句
    	* @param resultClass 结果对象类型
    	* @return 新查询实例
    	*/
      public Query createNativeQuery(String sqlString, Class resultClass);
      
      
      /**
      *
      * 创建一个StoredProcedureQuery执行存储过程
      * @param name 在元数据中分配给存储过程查询的名称
      * @return 存储过程查询实例
      * @throws IllegalArgumentException 查询并没定义该名称的语句
      *
      */
      public StoredProcedureQuery createNamedStoredProcedureQuery(String name);
      
      /**
      * 创建一个StoredProcedureQuery执行存储过程
      * 参数必须在执行存储过程之前进行注册
      * 如果存储过程返回一个或多个结果集,任何结果集都会返回Object[]类型
      * @param procedureName 数据库中存储过程的名称
      * @return 返回新查询存储过程实例
      * @throws IllegalArgumentException 如果给定名称存储过程名称并不存在(或查询执行
      * 失败
      */
      public StoredProcedureQuery createStoredProcedureQuery(String procedureName);
      
      
      /**
      * 创建一个StoredProcedureQuery执行存储过程
      * 参数必须在执行存储过程之前进行注册
      * 必须按存储过程调用返回结果集的顺序指定resultCLass参数
      * @param procedureName 数据库中存储过程的名称
      * @param resultClasses  存储过程产生的结果集要映射到的类
      * @return 返回新查询存储过程实例
      * @throws IllegalArgumentException 如果给定名称存储过程名称并不存在(或查询执行
      * 失败
      */
      public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class ... resultClass);
      
      
      
       /**
      * 创建一个StoredProcedureQuery执行存储过程
      * 参数必须在执行存储过程之前进行注册
      * 必须按存储过程调用返回结果集的顺序指定resultSetMappings参数
      * @param procedureName 数据库中存储过程的名称
      * @param resultSetMappings  存储过程产生的结果集要映射
      * @return 返回新查询存储过程实例
      * @throws IllegalArgumentException 如果给定名称存储过程名称并不存在(或查询执行
      * 失败
      */
      public StoredProcedureQuery createStoreProcedureQuery(String procedureName, String ... resultSetMappings);
      
      
      /**
      * 表明这个实体管理器,一个JTA事务被激活,这个方法应该在JTA应用管理实体上被调用, 它
      * 被创建外部激活事务的作用域 或在类型实体管理器 
      * SynchronizationType.UNSYNCHRONIZED 去关联当前JTA事务
      * @throws TransactionRequiredException, 如果没有事务
      *
      */
      public void joinTransaction();
      
      
      /**
      * 决定实体管理器是否已经加入当前事务,如果实体管理没有加入当前事务或没有事务激活 返回 
      * false
      * @return boolean
      *
      */
      public boolean is JoinedToTransaction();
      
      
      /**
      * 返回特定类型的对象去获取供应商特定API接口, 如果供应商的实体管理实现不支持特定类
      * 型,那么PersistenceException异常将会抛出
    	* @param cls 对象的类型将会被返回, 一般情况要么是实体管理器实现类或它实现的接口
    	* @return 特定类型的实例
    	* @throws PersistenceException, 如果供应商不支持这个调用
    	*/
      public <T> unwrap(Class<T> cls);
      
      
      /**
      *  返回实体管理器基础供应商对象,如果可以获取,这个方法结果有具体实现,那么对于新的应
      * 用程序unwrap方法是首选方法
      * @return 返回实体管理器基础供应商对象
      *
      */
      public Object getDelegate();
      
      
      /**
      * 关闭一个应用实体管理器,在关闭调用之后,实体管理的所有的方法的获取任何Query 
      *TypedQuery 和 StoredProcedureQuery对象时候将会抛出IllegalStateException异
      * 常(getProperties, getTransaction和isOpen[返回false]方法除外)
      * 如果在这个方法调用时,实体管理器已经加入一个激活状态的事务,那么这个管理器将会等待
      * 事务完成才会关闭
      * @throws IllegalStateException 如果这个实体管理是容器管理的
      *
      */
      public void close();
      
      /**
      * 确定这个实体管理器是否打开状态
    	* @return true 一直是true,直到实体管理器关闭
    	*/
      public boolean isOpen();
      
      
      /**
      * 返回一个资源级别 EntityTransaction对象。
      * 这个EntityTransaction实例必须串行使用 begin 和commit 执行多次事务
      * @return EntityTransaction 实例
      * @throws IllegalStateException 如果调用在JTA实体管理上
      */
      public EntityTransaction getTransaction();
      
      
      /**
      * 返回实体管理的实体管理器工厂类
      * @return EntityManagerFactory 实例
      * @throws IllegalStateException 如果实体管理器已经关闭了
      */
      public EntityManagerFactory getEntityManagerFactory();
      
      
      /**
      * 返回创建CriteriaQuery对象构造器的CriteriaBuilder实例
      * @return CriteriaBuilder 实例
      * @throws IllegalStateException 如果实体已经关闭了
      */
      public CriteriaBuilder getCriteriaBuilder();
      
       /**
      * 返回Metamodel接口实例,用于获取持久单元的metamodel
      * @return Metamodel 实例
      * @throws IllegalStateException 如果实体已经关闭了
      */
      public Metamodel getMetamodel();
      
      
      /**
      * 返回一个可变EntityGraph,它用于动态创建一个EntityGraph
      * @param rootType 实体图形类
      * @return 实体图形实例
      */
      public<T> EntityGraph<T> createEntityGraph(Class<T> rootType);
      
      /**
      * 返回一个可变名字为EntityGraph一个副本,如果特定名称没有与之对应实体图形,null将
      * 会返回
      * @param graphName 实体图形的名称
      * @return 实体图形
      */
      public EntityGraph<?> createEntityGraph(String graphName);
      
      /**
      * 返回一个名称对应的EntityGraph对象,这个返回EntityGraph应该考虑它不可变
      * @param graphName 存在实体图形的名称
      * @return 实体图形
    	* @throws IllegalArgumentException  如果给定名字没有对应EntityGraph
    	*/
      public EntityGraph<?> getEntityGraph(String graphName);
      
      
      /**
      * 对于给定类型返回所有EntityGraph对象列表
    	* @param entityClass 实体类型
    	* @return 定义在entity中的实体图形对象
    	* @throws IllegalArgumentException, 如果这个类不是实体
    	*/
      public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass);
    }
     
               
  • 当一个实体管理器在事务作用域持久上下文被使用,那么在事务上下文中必须调用persist, merge, remove和refresh方法,如果没有事务上下文,那么抛出javax.persistence.TransactionRequiredException
  • 指定非LockModeType.NONE的锁定模式的方法必须在事务中调用。如果没有事务或实体管理器没有加入事务,那么抛出javax.persistence.TransactionRequiredException
  • find方法(调用时候没有锁或调用使用使用LockModeType.NONE类型)和getReference方法不要在事务中调用,如果一个使用事务作用域上下文的事务管理器,那个结果实体将会被分离,如果使用扩展持久上下文的实体管理器,那么这些实体将会被管理,查看3.3 实体管理器使用一个外部事务。
  • 如果实体管理器是open状态,那么从实体管理器获取Query,TypedQuery, StoredProcedureQuery, CriteriaBuilder,Metamodel和EntityTransaction对象都是有效的。
  • 如果传入createQuery方法参数是一个无效java 持久查询语句或一个有效CriteriaQuery对象,那么IllegalArgumentException异常将会抛出或者查询失败的话PersistenceException抛出, 如果java持久查询语言查询结果不兼容查询结果,那么IllegalArgumentException异常抛出, 如果原生查询不是有效的查询或查询结果不兼容返回类型或查询执行失败那么PersistenceException异常将会抛出。如果有可能PersistenceException 应该包装基础数据库异常
  • 如果将持久性上下文加入到该事务中,则由EntityManager接口的方法(而不是LockTimeoutException)引发的运行时异常将导致当前事务被标记为回滚。
  • close、isOpen, joinTransaction和getTransaction方法用于管理实体和生命周期,见7.2.2,“获取一个应用管理实体管理器”
  • EntityManager接口和其他接口定义方法包含属性或提示作为参数,以下是properties和hints的区分:
    • 除非另有明确说明,否则提供者必须遵守本规范定义的属性。
    • 提示(hint)指定应用程序的首选项,尽可能的话,供应商应遵守此规范定义的提示,但是不一定是一直遵循,可移植的应用程序一定不能依赖提示

3.1.2、实体管理API使用的例子

  • @Stateless
    public class OrderEntryBean implements OrderEntry{
      @PersistenceContext
      EntityManager em;
      
      public void enterOrder(int custId, Order newOrder){
        Customer cust = em.find(Customer.class, custId);
        cust.getOrders().add(newOrder);
        newOrder.setCustomer(cust);
        em.persist(newOrder);
      }
    }
               

3.2、实体实例的生命周期

  • 这节描述EntityManager操作管理实体实例的生命周期,实体实例可以有新建(new),托管(managed),分离(detached)或移除(removed)的特征。
    • new(新建)的实体没有持久性标识,并且尚未与持久性上下文关联
    • managed(托管)实体是具有持久身份的实例,该实例当前与持久性上下文相关联。
    • detached(分离) 实体是具有持久性标识的实例,该持久性标识不(或不再)与持久性上下文关联。
    • removed(移除) : 删除的实体实例是具有持久性标识并与持久性上下文相关联的实例,在提交事务后将从数据库中将其删除
  • 以下小节描述了生命周期操作对实体的影响。cascade(级联)注解元素的使用可以用于将操作的效果传播到关联的实体。级联功能最常用与父子关系中。

3.2.1、实体实例创建

  • 实体实例创建过程就是一个new操作。实体实例由new首次创建时尚未持久。实例通过EntityManager API变成持久标记。

3.2.2、 持久化一个实体实例

  • 通过在新实体实例上调用persist方或级联persist操作,新实体实例有持久性和可以被实体管理器管理。
  • 持久操作的语法如下,例如假设实体X:
    • 如果X是一个新实体,则它将称为托管实体对象。实体X将在或在事务提交之前或flush操作的结果,刷新到数据库
    • 如果X是预先存在的托管实体,那么这个持久化操作将会忽略,但是,如果从X到这些其他实体的关系是用cascade=PERSIST或cascade=ALL 标注元素值标注的,或者用等效的XML描述符元素指定的,则持久化操作将级联到X引用的实体。
    • 如果X是一个移除的实体,那么这个操作将使得X变成托管状态
    • 如果X是分离的对象,那么当执行调用persist方法将抛出EntityExistsException异常或在flush或提交时候操作时则可能抛出PersistenceException
    • 对于由X的关系引用的所有实体Y,如果已使用级联元素值Cascade=PERSIST或Cascade=ALL 标注了与Y的关系,则将持久性操作应用于Y。

3.2.3、移除

  • 托管实体实例通过调用remove方法和级联移除操作变成移除状态
  • 持久操作的语法如下,例如假设实体X:
    • 如果X是new实体,那么执行remove操作将会忽略,然而,如果从X到这些其他实体的关系用cascade=REMOVE或cascade=ALL注释元素值标注,则移除操作将叠加到X引用的实体。
    • 如果X是managed实体,那么remove操作将导致实体变成removed, 如果从X到这些其他实体的关系用cascade=REMOVE或cascade=ALL注释元素值标注,则移除操作将叠加到X引用的实体。
    • 如果X是detached实体,移除操作或事务提交失败将会抛出IllegalArgumentException异常。
    • 如果X是removed实体,那么remove操作将会被忽略
    • 删除的实体X将在事务提交时或之前从数据库中删除,或者作为flush操作的结果
  • 删除实体后,其状态(生成状态除外)将是实体在调用删除操作时的状态。

3.2.4、与数据库同步

  • 通常,如下所述,持久性上下文将同步到数据库。但是,仅在应用程序已将其与当前事务连接后,才会将同步类型为SynchronizationType.UNSYNCHRONIZED的持久性上下文或在当前事务范围之外创建的应用程序管理的持久性上下文同步到数据使用EntityManager joinTransaction方法。
  • 持久实体的状态在事务提交时同步到数据库。 这种同步包括将对持久性实体以及其关系的任何更新写入数据库,如上所述。
  • 对实体状态的更新包括将新值分配给实体的持久属性或字段,以及修改持久属性或字段的可变值。
  • 与数据库的同步不涉及任何托管实体的刷新,除非由于指定了cascade=REFRESH或cascade=ALL 注解元素值而在这些实体上显式调用了refresh操作或将刷新操作级联到这些实体上。
  • 托管实体之间的双向关系将根据该关系拥有方所拥有的引用来保留,当内存引用在更改时,拥有者拥有的引用和相反的应用彼此保持一致,这是开发人员的职责,对于单向一对一和一对多关系,开发人员有职责确保遵守这些关系的语义。
    • 特别重要的是要确保对关系的相反方面的更改会导致在拥有方面进行适当的更新,以确保在将更改同步到数据库时不会丢失这些更改
  • 当事务处于活动状态并且持久性上下文已加入到事务中时,还允许持久厂商在运行时在其他时间执行与数据库的同步操作。flush方法可以被应用到强制同步。它可以应用实体关联到持久上下文信息。EntityManager的setFlushMode方法,Query,TypedQuery和StoredProcedureQuery接口都可以用于控制同步语法。FlushModeType.AUTO的生效可以见3.10.8节,如果FlushModeType.COMMIT被指定,那么刷新操作将会在事务提交时候发生。持久厂商可以允许,但是不是要求在其他时间执行flush动作。如果没有事务激活或如果持久上下文信息还没有加入当前的事务,那么持久厂商禁止flush(刷新)到数据库
  • flush操作的语法,假设应用实体X如下:
    • 如果X是一个托管实体,它将会同步到数据库
      • 对于所有实体Y通过关系引用X,如果这个关系对于Y已经被标记为cascade级联元素,cascade=PERSIST或cascade=ALL, 那么持久操作应用于Y。
      • 对于任意实体Y通过关系引用X,如果这个关系对于Y没有被标记为cascade级联元素,cascade=PERSIST或cascade=ALL:
        • 如果Y是new或removed状态,那么flush操作(和事务标记为回滚)或事物提交失败将会抛出IllegalStateException异常
        • 如果Y是detached(分离),这个语法取决关系的拥有者,如果是X用于这个关系,任何改变都会同步数据库,否则,如果Y关系拥有者,那么这个行为将未定义。
    • 如果X是已删除的实体,则将从数据库中删除它。没有级联选项相关。

3.2.5、刷新实体实例

  • 通过在数据库上调用refresh方法或通过级联刷新操作,可以从数据库刷新托管实体实例的状态。
  • 刷新操作的语法,假设应用实体X如下规则:
    • 如果X是managed托管对象,如果有任何改变都会从数据库刷新X的最新状态。如果刷新操作级联到其他实体,也就是引用X的关系的实体被标记为cascade=FEFRESH或cascade=ALL元素。
    • 如果X是new,detached(分离)或removed(移除)实体,IllegalArgumentException异常将会抛出

3.2.6、从持久性上下文中去掉实体实例

  • 可以调用detach方法或级联的detach操作来将一个实体实例从持久性上下文移除。对实体进行的更改(如果有的话)(包括删除实体)在移出后将不会同步到数据库。
  • 如果对实体进行了更改(包括删除实体),则应用程序必须在分离方法之前使用flush方法,以确保可移植的语义,因此持久性厂商可能会在显示调用flush方法之外的其他时间写入数据库,所有如果在分离之前未调用flush方法,则可移植的应用程序不得假定未将更改写入数据库。
  • 这个detach操作语法如下,假设应用实体X:
    • 如果X是managed(托管)实体,那么detach操作导致实体变成detached(分离状态), 这个detach操作可以被级联到关联X的其他实体上,也就实体上标记为cascade=DETACH或cascade=ALL 元素。先前引用关系将会在detach之后继续保持。
    • 如果X是一个new或detached实体, 那么detach操作将会忽略
    • 如果X是removed(移除)实体,detach操作导致它变成detached(分离状态),这个detach操作可以被级联到关联X的其他实体上,也就实体上标记为cascade=DETACH或cascade=ALL 元素。先前引用关系将会在detach之后继续保持。可移植应用程序不应将从持久上下文分离的已删除实体传递给进一步的EntityManager操作。

3.2.7、分离的实体

  • 如果使用事务范围的持久性上下文(请参见第3.3节),则由事务提交导致分离的实体。从事务回滚(请参阅第3.3.3节);避免将实体与持久性上下文分离。从清除持久性上下文,从关闭实体管理器开始,或序列化实体或通过值传递实体(如果,通过远程接口等将其传递到单独的应用程序层)。
  • 分离的实体实例继续存在于持久化上下文之外,而持久化上下文在其中存在或检索它们。它们的状态不在保证与数据库状态同步。
  • 持久性上下文结束后,应用程序可以访问可用的分离实体实例的可用状态。可用状态包括:
    • 任何持久字段或属性没有标记为fetch=LAZY
    • 应用程序已访问或通过实体图获取的任何持久字段或属性
  • 如果持久字段或属性是关联,则只有在关联实体可用时才可以安全第访问关联实例的可用状态。可用状态包括:
    • 使用find()检索的任何实体实例。
    • 使用查询检索或在提取连接(join)中显示请求的任何实体实例。
    • 应用程序访问了具有非主键持久性状态的实例变量的任何实体实例。
    • 通过导航标记为fetch=EAGER的关联,可以从另一个可用实例访问的任何实体实例。

3.2.7.1、合并Detached(分离)实体状态

  • 合并操作允许状态从分离的实体传播到由实体管理器管理的持久实体上。
  • 合并操作语法应用于实体X如下:
    • 如果X是detached实体,则将X的状态复制到具有相同表示的预先存在的管理实体实例X’上,或者创建X的新的管理副本X’
    • 如果X是new(创建)实体实例,则创建新的管理实体实例X’, 并将X的状态复制到新的管理实体实例X’中。
    • 如果X是removed(移除)实体,如果执行合并操作(或者事务提交失败)将会抛出IllegalArgumentException异常。
    • 如果X是managed(托管)实体,那么合并操作将会被忽略,然而,如果已用级联元素值cascade=MERGE或cascade=ALL标记这些关系,则合并操作将级联到X的关系所引用的实体。
    • 对于被来自具有级联元素值cascade=MERGE或 cascade=ALL的X的关系引用的所有实体Y,将Y递归合并为Y’. 对于X引用的所有此类Y,将X’设置为引用Y’.(请注意,如果X是托管实体,则X与X’是同一对象。)
    • 如果X是合并到X’的实体,并引用了另一个实体Y,其中未指定cascade=MERGE或cascade=ALL,则从X’导航相同的关联会产生对托管对象Y’引用,并带有与Y具有相同的持久身份。
  • 持久厂商程序不得合并尚未提取的标记为LAZY的字段:合并时,它必须忽略此类字段。
  • 持久性运行时实现必须在合并操作期间和/或刷新或提交时检查实体使用的任何Version列。在没有version列的情况下,持久性厂商运行时在合并操作期间不会执行其他版本检查

3.2.7.2、Detached(分离)实体和延迟加载

  • 当使用延迟加载属性或字段和/或关系时,序列化实体并将这些实体合并回持久性上下文可能无法在厂商之间相互操作
  • 要求厂商支持将分离的实体实例(其中可能包含延迟属性或字段和.或尚未获取的关系)的序列化以及后续的反序列化和合并,返回到该厂商运行时的单独JVM实例中,其中两个运行时实例有权访问实体类和任何必须的供应商持久性实现类。
  • 当需要厂商之间互操作性时,应用程序不得使用延迟加载。

3.2.8、托管(managed)实例

  • 应用程序负责确保仅在单个持久性上下文中管理实例。如果在多个持久性上下文中管理同一java实体,则该行为未定义。
  • contains()方法可用于确定是否在当前持久性上下文中管理实体实例
  • contains方法返回true的情况
    • 如果实体已从数据库中检索或由getReference返回,并且尚未删除或分离。
    • 如果实体实例是新的,并且已在实体上调用了persist方法,或者持久化操作已经级联到该实体。
  • contains方法返回false的情况
    • 如果实体是detached(分离)
    • 实体的remove方法被调用,或移除操作已经级联到它
    • 如果实体new()状态,那么persist方法没有被实体调用或持久操作还没级联到它
  • 请注意,持久化,合并,删除或分离的级联效果对于contains方法而言是立即可见的,而实体的数据库表示的实际插入,修改或删除可能会推迟到实体的末尾事务提交

3.2.9、加载状态

  • 如果一级从数据库中加载了FetchType.EAGER的所有属性(无论是显式指定还是默认设置)(包括关系和其他具有集合值的属性),或者由应用程序分配,则认为该实体已加载。具有FetchType.LAZY的属性可能已加载,也可能未加载。实体实例和关联实例的可用状态如第3.2.7节所述。
  • 如果可嵌入属性是从数据库加载的或由应用程序分配的,则认为可嵌入属性是已加载的,并且,如果该属性引用了可嵌入实例(即不为null),则可嵌入实例状态是已知的加载(即,已从数据库加载FetchType.EAGER的可嵌入对象的所有属性,或由应用程序分配。
  • 如果集合值属性是从数据库中加载的,或者该属性的值是由应用程序分配的,并且该属性引用了一个集合实例(即,不为null),则认为该集合值的属性已被加载,集合(例如实例或可嵌入对象)被视为已加载。
  • 如果关系属性从数据库加载的或由应用程序分配的,则认为单值关系属性已加载,并且,如果该属性引用实体实例(即不为null),则知道该实体实例的状态为被加载。
  • 如果基本属性的状态已从数据库中加载或由应用程序分配,则认为该基本属性已加载。
  • 无论与实体关联的持久性单元如何,都可以使用PersistenceUtil.isLoaded方法确定实体及其属性的加载状态。如果满足上述条件,则PersistenUtil.isLoaded方法将返回true,否则返回false,如果持久性单元是已知的,则可以改用PersistenceUtil.isLoaded方法,请参阅第7.11节。
  • 用于确定实体或实体属性的加载状态的持久性厂商合约在9.8.1节中进行了描述。

3.3、持久性上下文生命周期和同步类型

  • 容器管理的持久性上下文的生命周期作用域可以为事务(事务范围的持久性上下文),也可以具有超出单个事务的生命周期作用域(扩展的持久性上下文),枚举PersistenceContextType用于为容器管理的实体管理器定义持久性上下文生命周期的作用域,创建EntityManager实例时(无论是显式的还是与注入或JNDI查找结合使用),都将定义持久性上下文生命周期作用域。参见第7.6节。
  • package javax.persistence;
    
    public enum PersistenceContextType{
      TRANSACTION,
      EXTENDED
    }
               
  • 默认情况下,容器管理的实体管理器的持久性上下文的生命周期与事务的范围相对应(即其类型为PersistenceContextType.TRANSACTION).
  • 使用扩展的持久性上下文时,扩展的持久性上下文从EntityManager实例创建到关闭一直存在。此持久性上下文可能跨越EntityManager的多个事务和非事务调用。
  • 具有扩展的持久性上下文的EntityManager在事务提交后维护对实体对象的引用。这些对象仍由EntityManager管理,并且可以在事务之间作为托管对象进行更新。无论事务是否处于活动状态,在扩展的持久性上下文中从托管对象进行导航都会导致一个或多个其他托管对象。
  • 当使用具有扩展持久性上下文的EntityManager时,无论事务是否处于活动状态,都可以调用持久,删除,合并和刷新操作。当在事务中加入扩展的持久性上下文并提交事务时,这些操作的效果将被提交给数据库。
  • 扩展了应用程序管理的实体管理的持久性上下文的范围。应用程序负责管理持久性上下文的生命周期。
  • 容器管理的持久性上下文在第7.6节中进一步描述。由应用程序管理的持久性上下文将在7.7节中进一步描述。

3.3.1、使用当前事务进行同步

  • 默认情况下,容器管理的持久性上下文为SynchronizationType.SYNCHRONIZED, 并自动加入到当前事务中。除非调用EntityManager的joinTransaction方法,否则不会在当前事务中加入SynchronizationType.UNSYNCHRONIZED的持久性上下文。
  • 默认情况下,在活动事务范围内创建的,与JTA实体管理器关联的,由应用程序管理的持久性上下文将自动加入该事务。在事务范围之外创建的应用程序管理的JTA持久性上下文或类型为SynchronizationType.UNSYNCHRONIZED的应用程序管理的持久性上下文将不会加入该事务,除非调用EntityManager joinTransaction方法。
  • 与资源本地实体管理器关联的应用程序管理的持久性上下文始终自动加入该实体管理器启动的任何资源本地事务。
  • 持久性上下文同步类型将在7.6.1节中进一步描述。

3.3.2、事务提交

  • 当事务提交时,事务范围的持久性上下文的托管实体将分离。扩展持久性上下文的托管实体仍处于托管状态。

3.3.3、事务回滚

  • 对于事务范围的持久性上下文和与当前事务连接的扩展持久性上下文,事务回滚都会导致所有先前存在的托管实例和已删除实例分离。实例的状态将是事物回滚时的实例状态。事务回滚通常会导致持久性上下文在回滚时处于不一致状态。特别是,版本属性的状态和生成的状态(例如生成的主键)可能不一样。因此,以前由持久性上下文管理的实例(包括在该事务中变为持久性的新实例)可能无法以与其他分离对象相同的方式重用-例如,它们在传递给合并操作时可能会失败。
    • 注意:由于事务范围的持久性上下文的生命周期仅限于事务,而不管它是否加入该事务,因此容器在事务回滚时会关闭持久性的上下文。但是,未加入事务的扩展持久性上下文不受事务回滚的影响。

3.4、锁和并发

  • 本规范假定使用乐观并发控制。它假定实现将使用可读性隔离级别(或不持有长期读锁的供应商等效产品)来访问持久性单元所映射的数据库,并且通常会发生对该数据库的写操作,仅当调用了flush方法时-无论是由应用程序明确指定,还是由持久性厂商运行时根据刷新模式设置进行显式指定。
    • 如果事务处于活动状态,并且持久性上下文已加入到该事务中,则合规允许该规范的实现立即写入数据库(即,无论何时更新,创建和/或删除托管实体),配置需要这种非延迟数据库写操作的实现方式不再本规范的范围之内。
  • 此外,通过指定锁模式,对选定实体也支持悲观锁和乐观锁,乐观锁在3.4.1和3.4.2节描述;3.4.3节中的悲观锁,第3.4.4节介绍了乐观和悲观锁模式的设置。在第3.4.4.1节中描述了乐观锁模式设置的配置,在第3.4.4.2节中描述了悲观锁模式设置的配置。

3.4.1、乐观锁

  • 乐观锁是一种技术,用于确保仅在自从读取实体状态以来没有中间事务更新该数据时,才对于该实体状态相对应的数据库数据进行更新。这样可确保对该数据的更新或删除与数据库的当前状态一致,并且不会丢失中间的更新。可能导致违反此约束的事务将导致抛出OptimisticLockException异常,并将事务标记为回滚。
  • 希望对实体启用乐观锁的可移植应用程序必须为这些实体指定Version属性,即,具有Version注解的或在XML描述符中指定为版本属性的持久属性或字段。强烈建议应用程序为所有可能同时访问或从断开状态合并的实体启用乐观锁。不使用乐观锁可能导致实体状态不一致,更新丢失和其他状态异常。如果没有将乐观锁定义为实体状态的一部分,则应用程序必须承担维护数据一致性的责任。

3.4.2、Version属性

  • 持久性提供程序使用Version字段或属性执行乐观锁。持久性厂商在对实体实例执行生命周期操作的过程中会对其进行访问或设置。如果实体具有通过版本(version)映射的属性或字段,则该实体将字段启用乐观锁。
  • 实体可以访问其版本字段或属性的状态,也可以导出应用程序用于访问version的方法,但不得修改版本值。除了第4.10节中提到的例外,仅允许持久性厂商设置或更新对象中的version属性的值。
  • 将对象写入数据库时,持久性厂商运行时会更新version属性。所有非关系字段和属性以及实体拥有的所有关系都包含在版本检查中。
  • 持久性厂商对合并操作的实现必须在合并实体时检查版本属性,如果发现要合并的对象是该实体的旧副本,则抛出OptimisticLockException。如果实体分离, 该实体已更新,根据所使用的策略,有可能在调用flush或提交时间(以先发生着为准)之前不会抛出该异常。
  • 在执行优化锁检查时,要求持久性厂商运行时仅仅使用version属性。 持久性厂商实现可以在版本属性之外提供其他机制,以启用乐观锁检查,但是,本规范的实现不需要支持此类机制。
  • 如果仅某些实体包含版本属性,则需要持久性厂商运行时来检查已为其指定了版本属性的那些实体。无法保证对象图的一致性,但是某些实体上缺少版本属性不会阻止操作的完成。

3.4.3、悲观锁

  • 尽管乐观锁通常适合处理并发事务之间的中度竞争,但是在某些应用程序中,由于乐观事务通常会较晚失败,因此立即为选定实体获取长期数据库锁可能很有用,这样立即获得的长期数据库锁在这里称为悲观锁。
  • 悲观锁可确保一旦事务在实体实例上获得悲观锁:
    • 任何其他事务(无论是使用Java Persistence API 的应用程序事务, 还是使用基础资源的任何其他事务)都无法成功修改或删除该实例,直到持有锁的事务结束为止。
    • 如果悲观锁是互斥锁,则同一事务可以修改或删除该实体实例。
  • 当使用悲观锁锁定实体实例时,持久性提供程序必须锁定与该实体的非集合值持久性状态相对应的数据库行。如果使用联合继承策略,或者如果实体已映射到辅助表,则这需要将实体实例的行锁定在附加表中。锁定的实体包含外键的实体关系也将被锁定,但被引用实体的状态将不被锁定(除非显式锁定了那些实体)。默认情况下,实体不包含外键的元素集合和关系(例如映射到连接表的关系或目标实体包含外键的单向一对多关系)不会被锁定。
  • 如果将javax.persistence.lock.scope属性指定为PessimisticLockScope.EXTENDED的值,则包含在连接表中实体所拥有的元素集合和关系将被锁定。此类关系所引用的实体的状态将不会被锁定(除非这些实体被显示锁定)。可以将此属性作为参数传递给EntityManager, Query和TypedQuery接口的方法,这些方法运行允许指定锁定模式或与NamedQuery注解一起使用。
  • 锁定这样的关系或元素集合通常只锁定该关系或集合的连接表或集合表中的行。这意味着幻影是可能的。
  • javax.persistence.lock.scope属性的值由PessimisticLockScope枚举定义。
  • package javax.persistence;
    
    public enum PessimisticLockScope{
      NORMAL,
      EXTENDED
    }
               
  • 该规范未定义持久性厂商用于获取数据锁的机制,并且可移植应用程序不应该依赖于如何在数据库上实现悲观锁。特别是,持久性厂商或基础数据库管理系统可能会锁定比应用程序选择的行更多的行。
  • 每当在数据库上更新包含版本属性的悲观锁的实体时,持久性厂商还必须更新(递增)实体的版本列,以启用与使用乐观锁的应用程序的正确交互。请参阅第3.4.2和3.4.4节。
  • 悲观锁可以应用于不包含版本属性的实体。但是在情况下,无法确保与使用乐观锁定的应用程序进行正确的交互。

3.4.4、锁定模式

  • 锁模式旨在提供一种功能,使得读取的项目具有”可重复读取“语义,无论是"乐观"(如3.4.4.1节所述)还是"悲观"(如3.4.4.2节所述)。
  • 可以通过EntityManager锁方法,允许指定锁模式的EntityManager,Query和TypedQuery接口方法以及NamedQuery注解来指定锁模式
  • 锁模式值由LockModeType枚举定义。定义了六个不同的锁模式。锁模式类型值READ和WRITE分别是OPTIMISTIC和OPTIMISTIC_FORCE_INCREMENT的同义词。后者对于新应用将是首选。
  • package javax.persistence;
    public enum LockModeType{
      READ,
      WRITE,
      OPTIMISTIC,
      OPTIMISTIC_FORCE_INCREMENT,
      PESSIMISTIC_READ,
      PESSIMISTIC_WRITE,
      PESSIMISTIC_FORCE_INCREMENT,
      NONE
    }
               

3.4.4.1、OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT

  • 锁模式OPTIMISTIC和OPTIMISTIC_FORCE_INCREMENT用于乐观锁。锁模式类型值READ和WRITE分别于OPTIMISTIC和OPTIMISTIC_FORCE_INCREMENT同义。
  • 如果事务T1调用版本对象上的lock(entity, LockModeType.OPTIMISTIC), 则实体管理器必须确保不会发生以下两种现象:
    • P1(脏读): 事务T1修改一行,然后,另一个事务T2在提交或回滚之前读取该行并获取修改后的值。事务T2最终成功提交; T1提交还是回滚以及在T2提交之前还是之后都这样做并不重要。
    • P2(不可重复读):事务T1读取一行,然后,另一个事务T2在T1提交之前修改或删除该行,两项交易最终均成功提交。
  • 通常,这将通过实体管理器获取对获取对基础数据库行的锁来实现。 尽管采用乐观并发,通常不会立即获取长期数据库读取锁,但允许兼容的实现获得立即锁定(只要保留该锁知道提交完成即可)。如果将锁推迟到提交时间,则必须保留该锁,直到提交完成。允许以防止上述现象的方式支持可重复读取的任何实现都是允许的。
  • 不需要持久性实现来支持在非版本对象上调用lock(entity, LockModeType.OPTIMISTIC),当它不能支持这样的锁调用时,它必须抛出PersistenceException. 受支持时,无论对于版本控制的对象还是非版本控制的对象,LockModeType.OPTIMISTIC都必须始终防止现象P1和P2,在非版本对象上调用lock(entity, LockModeType.OPTIMISTIC)的应用程序将不可移植。
  • 如果事务T1调用版本对象上的lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT), 则实体管理器必须避免现象P1和P2(与LockModeType.OPTIMISTIC一样),并且还必须强制对实体的版本列进行更新(递增)。强制版本更新可以立即执行,也可以推迟到刷新或提交之前执行。如果在应用延迟版本更新之前删除了实体,则将省略强制版本更新。
  • 不需要持久性实现来支持在非版本对象上调用lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT).当它不能支持这样的锁调用时,它必须抛出PersistenceException. 如果支持的话,则无论是版本对象还是非版本对象,LockModeType.OPTIMISTIC_FORCE_INCREMENT都必须始终防止现象P1和P2。对于非版本对象,LockModeType.OPTIMISTIC_FORCE_INCREMENT是否具有任何其他行为是特定供应商的,在非版本对象上调用lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT)的应用程序将不可移植。
  • 对于版本化的对象,允许实现使用LockModeType.OPTIMISTIC_FORCE_INCREMENT,其中请求了LockModeType.OPTIMISTIC,反之亦然。
  • 如果以其他方式更新或删除了版本控制的对象,则即使未对EntityManager.lock进行任何显式调用,实现也必须确保满足LockModeType.OPTIMISTIC_FORCE_INCREMENT的要求。
  • 为了实现可移植性,应用程序不应依赖于特定于供应商的提示和配置,以确保通过使用版本属性和EntityManager lock方法以外任何机制来重复读未更新的删除的对象。但是,应注意的是,如果某个实现已在某些数据库行上获得了前期的悲观锁,则可以随意忽略代表那些行的实体对象上的lock(entity, LockModeType.OPTIMISTIC)调用。

3.4.4.2、PESSIMISTIC_READ, PESSIMISTIC_WRITE, PESSIMISTIC_FORCE_INCREMENT

  • PESSIMISTIC_READ, PESSIMISTIC_WRITE, PESSIMISTIC_FORCE_INCREMENT 用于立即获得长期数据库锁。
  • 请求类型为LockModeType.PESSIMISTIC_READ, LockModeType.PESSIMISTIC_WRITE和 LockModeType.PESSIMISTIC_FORCE_INCREMENT的锁的语义如下。
  • 如果事务T1调用对象上的lock(entity, LockModeType.PESSIMISTIC_READ)或lock(entity, LockModeType.PESSIMISTIC_WRITE), 则该实体管理器必须确保不会发生以下两种现象:
    • P1(脏读): 事务T1修改一行,然后,另一个事务T2在提交或回滚之前读取该行并获取修改后的值。事务T2最终成功提交; T1提交还是回滚以及在T2提交之前还是之后都这样做并不重要。
    • P2(不可重复读):事务T1读取一行,然后,另一个事务T2在T1提交之前修改或删除该行,两项交易最终均成功提交。
  • 任何此类锁都必须立即获得并保留,直到事务T1完成(提交或回滚)
  • 通常通过实体管理器获取对基础数据库行的长期锁来避免现象P1和P2。如上所述,任何支撑悲观的可重复都的实现都是允许的。
    • 可以在实体实例上获得具有LockModeType.PESSIMISTIC_WRITE的锁,以强制尝试更新实体数据的事务之间进行序列化。具有LockModeType.PESSIMISTIC_READ的锁可用于使用可重复读的语义查询数据,而无需在事务结束是重新读取数据以获得锁,也无需阻止其他事务读取数据。查询数据时可以使用具有LockModeType.PESSIMISTIC_WRITE的锁,并且在并发更新事务之间极有可能发生死锁或更新失败。
  • 持久性实现必须支持在非版本实体和版本控制实体上调用lock(entity,LockModeType.PESSIMISTIC_READ) 和lock(entity, LockModeType.PESSIMISTIC_WRITE).
  • 允许实现在请求LockModeType.PESSIMISTIC_READ的情况下使用LockModeType.PESSIMISTIC_WRITE, 但反之则不行。
  • 当无法获得锁,并且数据库锁失败导致事务级回滚时,提供程序必须抛出PessimisticLockException并确保已将JTA事务或EntityTransaction标记为回滚。
  • 当无法获得锁,并且数据库锁定失败仅导致语句级别回滚时,提供程序必须抛出LockTimeoutException(并且不得将事务标记为回滚)
  • 当应用程序使用LockModeType.PESSIMISTIC_READ锁定实体并稍后更新时该实体,则在将该实体刷新到数据库时,必须将该锁转换为独占锁,如果锁定转换失败,并且数据锁定失败导致事务级回滚,则提供程序必须抛出PessimisticLockException并确保已将JTA事务或EntityTransaction标记为回滚。当所转换失败,并且数据锁定失败仅导致语句级别回滚时,提供程序必须抛出LockTimeoutException(并且不得将事务标记为回滚)
  • 在已存在于持久性上下文中的版本化实体调用lock(entity, LockModeType.PESSIMISTIC_READ), lock(entity, LockModeType.PESSIMISTIC_WRITE) 或 lock(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT)时,提供者还必须获取锁时执行乐观的版本检查。如果版本检查失败,则必须引发OptimisticLockException.根据提供者使用的实现策略,有可能在调用flush或提交时间(以先发生者为准)之前不会抛出此异常。
  • 如果对版本控制对象调用事务T1调用lock(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT),在实体管理器必须避免现象P1和P2(与LockModeType.PESSIMISTIC_READ和LockModeType.PESSIMISTIC_WRITE相同),并且还必须强制对实体的版本列进行更新(递增)。
  • 不需要持久性实现来支持在非版本对象上调用lock(entity,LockModeType.PESSIMISTIC_FORCE_INCREMENT).当它不能支持这样的锁调用时,它必须抛出PersistenceException. 如果支持时,无论对于版本对象还是非版本对象,LockModeType.PESSIMISTIC_FORCE_INCREMENT都必须始终防止现象P1和P2,对于非版本对象, LockModeType.PESSIMISTIC_FORCE_INCREMENT是否具有任何其他行为是特定于供应商的,在非版本对象上调用lock(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT)的应用程序将不可移植。
  • 对于版本化的对象,允许实现使用LockModeType.PESSIMISTIC_FORCE_INCREMENT,其中请求了LockModeType_PESSIMISTIC_READ或LockModeType.PESSIMISTIC_WRITE,反之亦然。
  • 如果使用LockModeType.PESSIMISTIC_READ或LockModeType.PESSIMISTIC_WRITE锁的版本控制对象已更新,则实现必须确保满足LockModeType.PESSIMISTIC_FORCE_INCREMENT的要求。

3.4.4.3、锁模式属性和使用

  • 本规范定义了以下属性,用于悲观锁,如第3.4.3节所述:
    • javax.persistence.lock.scope
  • 此属性可以与EntityManager接口的允许指定锁模式的方法,Query和TypedQuery setLockMode方法以及NamedQuery注解一起使用。指定后,必须遵守此属性,允许提供者锁比请求更多(但不是更少)的行。
  • 本规范定义了以下提示,用于悲观锁:
    • javax.persistence.lock.timeout // 时间是毫秒
  • 此提示可以与EntityManager接口的允许指定锁模式的方法,Query.setLockModel方法和NamedQuery注解一起使用。也可以将它作为属性传递给Persistence.createEntityManagerFactory方法,并在persistence.xml文件的properties元素中使用。请求参阅第3.1.1、3.10.9、8.2.1.9、9.7和10.4.1节。当在createEntityManagerFactory方法,persistence.xml文件和NamedQuery注解中使用时,超时提示用作默认值,可以通过在上面指定的EntityManager,Query和TypedQuery接口的方法中使用来选择性地覆盖它,如果未指定此提示,则假定数据库超时值适用。
  • 超时值0用于指定"不等待"锁,也就立即返回(如果锁定失败)
  • 可移植应用程序不应依赖此提示,取决于正在使用的数据库和锁定由持久性提供程序使用的机制,提示可能会或可能不会被观察到。
  • 允许供应商支持使用其他特定于供应商的锁提示。特定于供应商的提示不得使用javax.persistence命名空间。如果不理解供应商特定的提示,则必须将其忽略。
  • 如果多次指定相同的属性或提示,则按优先级递减的顺序进行以下覆盖:
    • EntityManger,Query或TypedQuery接口的方法参数
    • 对NamedQuery的规范(注解或XML格式)
    • createEntityManagerFactory方法的参数
    • persistence.xml文件定义的。

3.4.5、OptimisticLockException (乐观锁定异常)

  • 当有效的锁定模式和刷新模式设置一致时,提供程序实现可以将对数据库的写操作推迟事务结束为止。在这种情况下,在提交时间之间可能不会进行乐观锁检查,并且可能在提交的“完成之间”阶段引发OptimisticLockException。如果OptimisticLockException必须由应用程序捕获或处理,则应用程序应使用flush方法强制执行数据库写操作。这将允许应用程序捕获和处理乐观锁异常。
  • OptimisticLockException提供了一个API,用于返回导致引发异常的对象。不能保证每次抛出异常时存在对象引用,但是只要持久性提供程序可以提供该对象引用,就应该提供该对象引用。应用程序不能依赖此对象的可用性。
  • 在某些情况下, 当跨越VM边界时,OptimisticLockException将被另一个异常(例如RemoteException)引发并包装。包装异常中可能引用的实体应实现Serializable,以便编组不会失败。
  • OptimisticLockException总是使该事务被标记为回滚。刷新对象或在新的事务上下文中重新加载对象,然后重试该事务对OptimisticLockException的潜在响应。

3.5、实体监听器和回调方法

  • 可以将一种方法指定为生命周期回调方法,以接收实体生命周期事件的通知。可以在实体中, 映射的超类或与实体或映射的超类关联的实体监听器类上定义生命周期回调方法,实体监听器是一种类,其响应与实体上的生命周期事件而调用其他方法的类。可以为实体类或映射的超类定义任意数量的实体监听器类。
  • 可以通过XML描述符指定默认实体监听器(其回调方法适用于持久性单元中所有实体的实体监听器类)
  • 生命周期回调方法和实体监听器类是通过元数据注解或XML描述符定义的。使用注解时,使用实体类或映射的超类上的EntityListeners注解来表示一个或多个实体监听器。如果定义了多个实体监听器,则它们的调用顺序由EntityListeners注解中指定它们顺序确定。XML描述符可以用作替代方法,以指定实体监听器的调用顺序或替代在元数据注解中指定的顺序。
  • 注解的任何子集或组合都可以在实体类,映射的超类或监听器类上指定。对于同一个生命事件,单个类不得具有多个生命周期回调方法。 相同的方法可以用于多个回调事件。
  • 继承层次结构中的多个实体类和映射的超类可以直接在该类上定义监听器和或生命周期回调方法。3.5.5节描述了在这种情况下适用于方法调用顺序的规则

3.5.1、实体监听器

  • 实体监听器类必须有个无参的构造方法。
  • 启用CDI时,Java EE环境中的实体监听器类支持通过上下文和依赖项注入API(CDI)进行依赖项注入。利用CDI注入的完整监听器还可以定义带有PostConstruct 和PreDestroy注解的生命周期回调方法。这些方法将在注入发生之后和销毁实体监听器实例之前分别调用。
  • 持久性提供程序负责使用CDI SPI创建实体监听器类的实例。在这种情况下进行注入,调用其PostConstruct和PreDestroy方法(如果有),并处理实体监听器实例。
  • 仅需要持久性提供程序来支持CDI注入Java EE容器环境中的实体监听器中,如果未启用CDI,则持久性厂商程序不得调用依赖于CDI注入的实体监听器
  • 实体监听器是非上下文对象。在支持注入到实体监听器中时,持久性厂商程序必须执行以下步骤来使用CDI SPI。
    • 获取BeanManager实例(查看9.1节)
    • 为实体监听器类创建AnnotatedTyped实例
    • 为AnnotatedType创建InjectionTarget
    • 创建CreationalContext
    • 调用InjectionTarget的produce方法实例化监听器
    • 调用InjectionTarget inject方法注入一个监听器实例
    • 调用InjectionTarget postConstruct方法去执行PostConstruct回调方法。
  • 当监听器被销毁时候,持久性厂商必须执行如下步骤:
    • 调用InjectionTarget preDestroy方法
    • 调用InjectionTarget dispose 方法
    • 调用CreationalContext release方法
  • 持久性厂商可以优化上述步骤,例如通过避免调用实际的CDI SPI并依赖于容器特定的接口,只要结果是相同即可。
  • 不使用CDI注入的实体监听器是无状态的,此类监听器的生命周期未指定。
  • 从Java EE环境中调用时,实体的回调监听器共享调用组件的企业命名上下文,并且实体回调方法在调用回调方法时调用组件的事务和安全上下文。

3.5.2、生命周期回调方法

  • 实体生命周期回调方法可以在实体监听器上或直接在实体类或映射的超类上定义。
  • 生命周期回调方法带有注解,这些注解指定对其进行调用的回调事件, 或者使用XML描述符将其映射到回调事件。
  • 用于监听器或映射的超类的回调方法以及用于实体监听器的回调方法的注解(XML元素)是相同的,但是,各个方法的签名不同的。
  • 在实体类或映射的超类上定义的回调方法具有以下签名:
    • void ()
  • 在实体监听器上定义的回调方法具有以下签名:
    • void (Object)
  • Object参数是为其调用回调方法的实体实例。可能是声明为实际实体类型。
  • 回调方法可以是public ,private , protected, package 级别修饰的,但是不能是static或final
  • 以下注解指定了相应类型的生命周期事件回调方法。
    • PrePersist
    • PostPersist
    • PreRemove
    • PostRemove
    • PreUpdate
    • PostUpdate
    • PostLoad
  • 以下规则适用于生命周期回调方法:
    • 生命周期回调方法可能会引发未检查的运行时异常。如果在持久性上下文中加入了事务,则在事务内执行的回调方法抛出的运行时异常会导致该事务被标记为回滚。
    • 生命周期回调可以调用JNDI,JDBC,JMS和企业Bean。
    • 通常,可移植应用程序的生命周期方法不应调用EntityManager或查询操作,访问其他实体或在同一持久性上下文内修改关系。生命周期回调方法可以修改在其上被回调的实体的非关系状态。

3.5.3、实体生命周期回调方法的语法

  • 在各自的EntityManager保留并执行对该实体的删除操作之前,将为给定实体调用PrePersist和PreRemove回调方法。对于已应用合并操作并导致创建新托管实例的实体,在将实体状态复制到托管实例后,将未托管实体调用PrePersist回调方法。这些PrePersist和PreRemove回调也将在所有这些操作已经级联到实体上被调用。PrePersist和PreRemove方法将始终作为同步持久,合并和删除操作的一部分被调用。
  • 在使实体成为持久性或删除实体后,将为该实体调用PostPersist和PostRemove回调方法。这些回调还将在这些操作所有级联到的所有实体上被调用。在数据库插入和删除操作之后,将分别调用PostPersist和PostRemove方法。这些数据库操作可以在调用持久,合并或删除操作之后直接发生, 也可以在发生刷新操作后立即发生(可能在事务结束时)。生成的主键值在PostPersist方法中可用。
  • PreUpdate和PostUpdate回调分别发生在对实体数据进行数据库更新操作之前和之后,这些数据库操作可能在实体状态更新时发生,或者可能在状态刷新到数据库时发生(可能在事务结束时)
    • 请注意,当持久保留实体并随后在单个事务中对其进行修改时,还是实体在单个事务中随后被删除并随后进行删除时,是否发生PreUpdate和PostUpdate回调与实现有关。可移植的应用程序不应依赖于这种行为。
  • 在从数据库将实体加载到当前持久性上下文中或对其执行刷新操作之后,将调用该实体的PostLoad方法。在返回或访问查询结果之前或遍历关联之前,将调用PostLoad方法。
  • 是否在将生命周期事件级联相关实体之前或之后调用回调方法取决于实现。应用程序不应依赖于此顺序。

3.5.4、例子

  • @Entity
    @EntityListeners(com.acme.AlertMonitor.class)
    public class Amount{
      Long accountId;
      Integer balance;
      boolean preferred;
      @Id
      public Long getAccountId(){...}
      
     	public Integer getBalance(){...}
      
      @Transient // 因为状态依赖于非持久性上下文
      public boolean isPreferred(){...}
      
      public void deposit(Integer amount){...}
      
      public Integer withdraw(Integer amount) throws NSFException{...}
      
      @PrePersist
      protected void validateCreate(){
        if(getBalance() < MIN_REQUIRED_BALANCE)
          throw new AccountException("余额不足开户")
      }
      
      @PostLoad
      protected void adjustPreferredStatus(){
        preferred = (getBalance() >= AccountManager.getPreferredStatusLevel());
      }
    }
    
    public class AlertMonitor{
      @PostPersist
      public void newAccountAlert(Account acct){
        Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
      }
    }
               

3.5.5、实体生命周期事件的多种生命周期回调方法。

  • 如果为实体生命周期事件定义了多个回调方法,则这些方法的调用顺序如下。
  • 默认监听器(如果有的话)将首先按照XML描述符中指定的顺序进行调用。缺省监听器适用于特定持久性单元中的所有实体。除非通过ExcludeDeFaultListeners注解明确排除或"exclude-default-listeners" XML元素。
  • 在实体监听器类上为实体类或映射的超类定义的生命周期回调方法,其调用顺序与EntityListeners注解中指定实体监听器的顺序相同。
  • 如果继承层次结构中有多个类(实体类或映射超类),请定义实体监听器,为超类定义的监听器在为其子类定义的监听器之前被调用按此顺序class。ExcludeSuperclassListeners注解或exclude-superclass-listeners XML 元素可以应用实体类或映射的超类以排除由实体监听器类为实体的超类定义的监听器的调用或者映射的超类。排除的监听器将从已指定ExcludeSuperclassListeners注解或元素的类以及子类中排除。ExcludeSuperclassListeners注解(或exclude-superclass-listeners XML 元素) 不会导致默认实体监听器被从调用中排除。
  • 如果在实体类和或其一个或多个实体或映射的超类上也指定了用于同一生命周期事情的生命周期回调方法,则在另一个生命周期之后将调用实体类和或父类上的回调方法,最一般的超类优先。允许一个类重写具有相同回调类型的继承的回调方法,在这种情况下,不会调用重写的方法。
  • 持久性厂商程序运行时将以指定的顺序调用回调方法,如果回调方法的执行正常终止,则持久性厂商程序运行时将调用下一个回调方法(如果有的话)
  • XML描述符可用于覆盖注解中指定的生命周期回调方法调用顺序。

3.5.6、例子

  • 几个实体类和监听动物的监听类
  • @Entity
    public class Animal{
      ...
      @PostPersit
      protected void postPersistAnimal(){
        ...
      }
    }
    
    
    @Entity
    @EntityListeners(PetListener.class)
    public class Pet extends Animal{
      ...
    }
    
    @Entity
    @EntityListeners({CatListener.class, CatListener2.class})
    public class Cat extends Pet{
      ...
    }
    
    public class PetListener{
      @PostPersist
      protected void postPersistPetListenerMethod(Object pet){
        ...
      }
    }
    
    public class CatListener{
      @PostPersist
      protected void postPersistCatListenerMethod(Object cat){
        ...
      }
    }
    
    public class CatListener2{
      @PostPersist
      protected void postPersistCatListener2Method(Object cat){
        ...
      }
    }
               
  • 如果在Cat实例上发生PostPersist事件,则会依次调用以下方法:
    • postPersistPetListenerMethod
    • postPersistCatListenerMethod
    • postPersistCatListener2Method
    • postPersistAnimal
  • 假设SiameseCat定义为Cat的子类:
    • @EntityListeners(SiameseCatListener.class)
      @Entity
      public class SiameseCat extends Cat{
        ...
        @PostPersist
        protected void postPersistSiameseCat(){
          ...
        }
      }
      
      public class SiameseCatListener{
        @PostPersist
        protected void postPersistSiameseCatListenerMethod(Object cat){
          ...
        }
      }
                 
  • 如果SiameseCat实例上发生PostPersist事件,则会依次调用以下方法:
    • postPersistPetListenerMethod
    • postPersistCatListenerMethod
    • postPersistCatListener2Method
    • postPersistSiameseCatListenerMethod
    • postPersistAnimal
    • postPersistSiameseCat
  • 假设定义SiameseCat被替换(重写父类方法):
    • @EntityListeners(SiameseCatListener.class)
      @Entity
      public class SiameseCat extends Cat{
        ...
        @PostPersist
        protected void postPersistAnimal(){
          ...
        }
      }
                 
  • 如果SiameseCat实例上发生PostPersist事件,则会依次调用以下方法:
    • postPersistPetListenerMethod
    • postPersistCatListenerMethod
    • postPersistCatListener2Method
    • postPersistSiameseCatListenerMethod
    • postPersistAnimal (这个是SiameseCat中的方法,不是Animal中方法)
  • 总结:
    • 监听器注解顺序优先实体类中注解
    • 父类顺序优先子类
    • 子类可以重写父类方法

3.5.7、异常

  • 生命周期回调方法可能会抛出运行时异常。如果将持久性上下文加入到事务中,则在事务内执行的回滚方法抛出的运行时异常会导致该事务被标记为回滚。抛出运行时异常后,将不在调用其他生命周期回调方法。

3.5.8、XML描述符中的回调监听器类和生命周期的方法规范

  • XML描述付费可以用作元数据注解的替代方法,以指定实体监听器及其与实体的绑定,或覆盖注解中指定的生命周期回调方法的调用顺序。

3.5.8.1、回调监听器的规范

  • 实体监听器XML描述符元素用于指定实体监听器类的生命周期监听器方法。生命周期监听器方法通过使用pre-persist, post-persist, pre-remove, post-remove, pre-update, post-update 或post-load元素指定。
  • 实体监听器类可以定义多个回调方法。但是实体监听器类的最多一种方法可以指定为pre-persist方法,post-persist方法, pre-remove方法,post-remove方法, pre-update 方法, post-update方法或post-load方法,无论是否使用XML描述符定义实体监听器,或者是否使用注解和XML描述符元素的某种组合。

3.5.8.2、实体监听器与实体的绑定规范

  • persistence-unit-defaults的子元素entity-listeners用于指定持久性单元的默认监听器
  • entity的子元素entity-listeners或mapped-superclass元素用于为各个实体或映射的超类以及子类指定实体监听器类
  • 实体监听器与实体类的绑定是附加性的,绑定到实体的超类或映射的超类的实体监听器类也将应用于该实体。
  • exclude-superclass-listeners元素指定不要为实体类(或映射的超类)以及子类调用超类的监听器方法。
  • exclude-default-listeners元素指定不为实体类(或映射的超类)以及子类调用默认实体监听器。
  • 显式列出给定实体类或映射的超类的排除的默认或超类监听器,导致将其应用于该实体或映射超类及其子类。
  • 对于单个生命周期事件有多种回调方法的情况,适用第3.5.5节中描述的调用顺序规则。

3.6、 Bean验证

  • 该规范定义了在Java Persistence应用程序中使用Bean Validation [5]的支持。
  • 托管类(实体,映射的超类和可嵌入的类)可以配置为包括Bean验证的约束
  • 通过在第3.5.3节中所述pre-persist, pre-update和pre-remove实体生命周期时间中指定对Bean 验证实现的Java持久性委托验证,可以实现使用这些约束的自动验证。
  • 验证也可以通过应用程序的托管类的实例上调用Validator实例的validate方法来实现。如果Bean 验证规范[5]所述。

3.6.1、生命周期事件的自动验证

  • 该规范支持使用bean验证来在持久化,更新和删除生命周期验证事件对实体进行自动验证。这些生命周期验证事件分别在所有PrePersist, PreUpdate和PreRemove生命周期回调方法调用分别完成之后立即发生,或者在此类生命周期回调方法将要完成之后立即发生(在发生以下情况时发生)此类回调方法不存在。
    • 如果实体在单个事务中被持久保存并随后被修改,或者在单个事务中被修改并随后被删除,则其实现取决于更新前验证事件是否发生。可移植应用程序不应依赖此行为。

3.6.1.1、开启自动验证

  • persistence.xml文件的validation-mode元素确定自动生命周期时间验证是否有效。验证模式元素的值为AUTO,CALLBACK, NONE。默认验证模式为AUTO。
  • 如果应用程序使用Persistence.createEntityManageFactory方法创建实体管理器工厂,则可以使用javax.persistent.validation.mode映射键指定验证模式,该键将覆盖持久性中指定(或默认值)。.xml文件,该键的映射值是"auto",“callback”, “none”。
  • 如果自动验证模式是由validation-mode元素或javax.persistent.validation.mode属性指定的,或者即未指定validation-mode元素也未指定javax.persistence.validation.mode属性,则为Bean验证提供程序存在于环境中,持久性提供程序必须按照第3.6.1.2节中所述执行实体的自动验证。如果环境中不存在Bean验证提供程序,则不会发生生命周期事件验证。
  • 如果回调验证模式是由validation-mode元素或javax.persistence.validation.mode属性指定的,则持久性提供程序必须执行3.6.1.2节中所述的生命周期事件验证。如果环境中不存在Bean验证提供程序,则会出错,并且如果javax.persistence.validation.mode属性值 “callback"已经传递给Persistent.createEntityManagerFactory方法,则该提供程序必须抛出PersistenceException.
  • 如果validation-mode元素或javax.persistence.validation.mode属性指定了none验证模式,则持久性厂商程序不得执行生命周期事件验证。

3.6.1.2、生命周期事件自动验证的要求

  • 对于每种事件类型,将以组列表为目标进行验证。默认情况下,默认的Bean验证组(默认组)将在持久化和更新前的生命周期验证事件时进行验证,而没有组将在删除前的事件中进行验证。
  • 通过使用persistence.xml文件中的以下验证属性指定目标组,或通过createEntityManagerFactory方法将这些属性传递到实体管理器工厂的配置中,可以覆盖次默认验证行为:
    • javax.persistence.validation.group.pre-persist
    • javax.persistence.validation.group.pre-update
    • javax.persistence.validation.group.pre-remove
  • 验证属性的值必须是目标组的列表。目标组必须有其完全限定的类名指定。名称必须用逗号分隔。
  • 当实体发生上述事件之一时,持久性厂商程序必须通过从使用中的验证器工厂获取Validator实例(请参阅第3.6.2节)并使用目标组调用其validate方法来验证该实体。如果目标组列表为空,则不执行任何验证。如果validate方法返回的ConstraintViolation对象集不为空,则持久性提供程序必须抛出javax.validation.ConstraintViolationException 包含对返回的ConstraintViolation对象集的引用异常,并且必须在持久性上下文中将事务标记为回滚。
  • 用于对生命周期事件进行自动验证的验证器实例必须使用具有以下行为的TraversableResolver:
    • 尚未加载的属性不得加载
    • 对于实体关联(单值或多值),不得出现验证级联(@Valid)
  • 这些要求确保不会因副作用而加载任何未卸载的属性或关联,并且在给定的flush周期中,没有任何实体会被多次验证。
  • 仅当在属性指定了Valid注解时,才必须验证可嵌入属性。
  • 持久性提供程序负责通过调用ValidatorFactory.usingContext().traversableResolver(tr).getValidator()将实现javax.validation.TraversableResolver接口的实例传递给Bean验证提供程序。具有上述行为的解析器。

3.6.2 提供ValidatorFactory

  • 在Java EE环境中,Java EE容器使ValidatorFactory实例可用。容器负责通过映射将验证器工厂传递给持久性提供程序,该映射作为参数传递给createContainerEntityManagerFactory调用。容器使用的映射键必须是标准属性名称javax.persistence.validation.factory.
  • 在Java SE环境中,应用程序可以通过作为参数传递给Persistence.createEntityManagerFactory调用的映射传递ValidatorFactory实例。使用的映射键必须是标准属性名称javax.persistence.validation.factory. 如果应用程序未提供ValidatorFactory实例,并且在类路径中存在Bean验证提供程序,则持久性提供程序必须使用Bean验证规范[5]定义的默认引导方法(即Validation.buildDefaultValidatorFactory)实例化ValidatorFactory()。

3.7、实体图

  • 实体图是捕获操作或查询的路径和边界的模板。它以元数据或动态EntityGraph API 创建的对象的形式定义。
  • 实体图在”获取计划" 规范中用于查询或find操作
  • EntityGraph, AttributeNode和Subgraph接口用于动态构造实体图。10.3节中描述了用于静态定义实体图的注解,即NamedEntityGraph, NamedAttributeNode和NamedSubgraph。命名实体图XML元素及子元素可用于覆盖这些注解或定义其他命名实体图。
  • 关于查找和查询操作的实体图的语义在3.7.4节中描述。

3.7.1、EntityGraph 接口

package javax.persistence;
import javax.persistence.metamodel.Attribute;
import java.util.List;

/**
* 这个类型标识实体图的根,它可以用于作为模板去定义属性节点和实体图的边界和实体的关系。
* 这个根节点必须是实体类型
* 这个方法隐含可以添加子图,当然也可以创建对应属性节点,此类属性节点不应冗余指定
*
* @param <T> 根实体的类型
*/
public interface EntityGraph<T> {
  
  /**
  * 返回命名EntityGraph的名称(通过NamedEntityGraph注解,XML描述符元素定义的实体图,
  * 或通过addNamedEntityGraph方法添加的实体图。如果EntityGraph不是命名的
  * EntityGraph,则返回null
  *
	*
  */
  public String getName();
  
  
  /**
  * 实体图中添加一个或多个属性节点
  * @param attributeName 属性名
  * @throws IllegalArgumentException  如果这个属性不是这个实体的属性
  * @throws IllegalStateException  如果EntityGraph已经静态定义。
  */
  public void addAttributeNodes(String ... attributeName);
  
  
  /**
  * 实体图中添加一个或多个属性节点
  * @param attribute 属性
  * @throws IllegalStateException  如果EntityGraph已经静态定义。
  */
  public void addAttributeNodes(Attribute<T,?> ... attribute);
  
  
  /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型
	* @param attribute 属性
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<X> addSubgraph(Attribute<T,X> attribute);
  
  
  
  /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型, 子类型将会自动包含特定超类图的具体属性
	* @param attribute 属性
	* @param type 实体子类
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<? extends X> 
    addSubgraph(Attribute<T,X> attribute, Class<? extends X> type);
  
  
  
   /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型
	* @param attributeName 属性的名称
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性不是实体的属性
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<X>  addSubgraph(String attributeName);
  
  
  
  /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型,子类型将会自动包含特定超类图的具体属性
	* @param attributeName 属性的名称
	* @param type 实体子类
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性不是实体的属性
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<X> addSubgraph(String attributeName, Class<X> type);
  
  
  /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  * @param attribute 属性
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<X> addKeySubgraph(Attribute<T,X> attribute);
  
  
 /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  * 子类子图将会包含特定属性超类的子图
  * @param attribute 属性
  * @param type 实体子类
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<? extends X> 
    addKeySubgraph(Attribute<T,X> attribute, Class<? extends X> type);
  
  
 /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  *
  * @param attributeName 属性名称
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性不是实体的属性
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<X> addKeySubgraph(String attributeName);
  
  
  /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  * 子类子图将会自动包含超类子图的特定属性
  * @param attributeName 属性名称
  * @param type 实体类
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性不是实体的属性
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<X> addKeySubgraph(String attributeName, Class<X> type);
  
  /**
  * 给这个实体图添加额外属性,它对应子类属性是EntityGraph的实体类型。子类子图将会自动包含
  * 超类子图的特定属性
  * @param type 实体子类的类性
  * @return 子类的子图
  * @throws IllegalArgumentException 如果属性不是实体的属性
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <T> Subgraph<? extends T> 
    addSubclassSubgraph(Class<? extends T> type);
  
  
  /**
  * 返回包含在实体图的属性节点
  * @ return 注解实体类型的属性节点或没有定义返回空集合
  */
  public List<AttributeNode<?>> getAttributeNodes();
}
           

3.7.2、AttributeNode 接口

package javax.persistence;
import java.util.Map;

/**
* 表示实体图的属性节点
* @param <T> 属性的类型
*/
public interface AttributeNode<T>{
  
  /**
  * 返回属性对应属性节点的名称
  * @return 属性节点名称
  */
  public String getAttributeName();
  
  
  /**
  * 返回子图关联这个属性节点,Map<Class, Subgraph>
  * @return 子图关联到这个属性节点的map或如果没有定义返回一个空map
  *
  */
  public Map<Class, Subgraph>getSubgraphs();
  
  
  /**
  * 返回子图关联这个属性节点的map键对象,Map<Class, Subgraph>
  * @return 子图关联到这个属性节点的map键的map或如果没有定义返回一个空map
  *
  */
  public Map<Class, Subgraph> getKeySubgraphs();
  
}
           

3.7.3、Subgraph 接口

package javax.persistence;
import javax.persistence.metamodel.Attribute;
import java.util.List;

/**
*  此类型表示与托管类型相对应的属性节点的子图,使用此类,可以将实体子图嵌入到EntityGraph中。
* @param <T> 属性类型
*/
public interface Subgraph<T>{
  
  /**
  * 添加一个或多个属性节点到实体图中
  * @param attributeName 属性名称
  * @throws IllegalArgumentException 如果属性不是托管类型的属性
  * @throws IllegalStateException 如果EntityGraph有静态定义
	*/
  public void addAttributeNodes(String ... attributeName);
  
 /**
  * 添加一个或多个属性节点到实体图中
  * @param attribute 属性
  * @throws IllegalStateException 如果EntityGraph有静态定义
	*/
  public void addAttributeNodes(Attribute<T,?> ... attribute);
  
   /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型
	* @param attribute 属性
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<X> addSubgraph(Attribute<T,X> attribute);
  
  
  
  /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型, 子类型将会自动包含特定超类图的具体属性
	* @param attribute 属性
	* @param type 实体子类
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<? extends X> 
    addSubgraph(Attribute<T,X> attribute, Class<? extends X> type);
  
  
  
   /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型
	* @param attributeName 属性的名称
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性不是实体的属性
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<X>  addSubgraph(String attributeName);
  
  
  
  /**
  * 添加节点到这个实体图中,这个实体图对应是托管类型,它是允许构建多个节点实体图,也包含关
  * 联托管类型,子类型将会自动包含特定超类图的具体属性
	* @param attributeName 属性的名称
	* @param type 实体子类
	* @return 这个属性子图
	* @throws IllegalArgumentException 如果属性不是实体的属性
	* @throws IllegalArgumentException 如果属性的目标类型不是托管类型
	* @throws IllegalStateException 如果EntityGraph已经静态定义
	*/
  public <X> Subgraph<X> addSubgraph(String attributeName, Class<X> type);
  
  
  /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  * @param attribute 属性
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<X> addKeySubgraph(Attribute<T,X> attribute);
  
  
 /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  * 子类子图将会包含特定属性超类的子图
  * @param attribute 属性
  * @param type 实体子类
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<? extends X> 
    addKeySubgraph(Attribute<T,X> attribute, Class<? extends X> type);
  
  
 /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  *
  * @param attributeName 属性名称
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性不是实体的属性
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<X> addKeySubgraph(String attributeName);
  
  
  /**
  * 图添加一个节点,它对应map的键是托管类型。它允许构建多个节点实体图,它包含相关联托管类型
  * 子类子图将会自动包含超类子图的特定属性
  * @param attributeName 属性名称
  * @param type 实体类
  * @return 关键属性的子图
  * @throws IllegalArgumentException 如果属性不是实体的属性
  * @throws IllegalArgumentException 如果属性的目标类型不是一个实体
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <X> Subgraph<X> addKeySubgraph(String attributeName, Class<X> type);
  
  /**
  * 给这个实体图添加额外属性,它对应子类属性是EntityGraph的实体类型。子类子图将会自动包含
  * 超类子图的特定属性
  * @param type 实体子类的类性
  * @return 子类的子图
  * @throws IllegalArgumentException 如果属性不是实体的属性
  * @throws IllegalStateException 如果EntityGraph已经是静态定义
  */
  public <T> Subgraph<? extends T> 
    addSubclassSubgraph(Class<? extends T> type);
  
  
  /**
  * 返回包含在实体图的属性节点
  * @ return 注解实体类型的属性节点或没有定义返回空集合
  */
  public List<AttributeNode<?>> getAttributeNodes();
}
           

3.7.4、在查询操作中使用实体图

  • 实体图可以用于find方法或作为query提示来覆盖或增强FetchType语义
  • 标准属性 javax.persistence.fetchgraph 和 javax.persistence.loadgraph用于指定图的查询和find操作
  • 实体或可嵌入对象的默认获取图定义为由其所有指定为FetchType.EAGER(或默认为FetchType)的属性的可传递包组成。
  • 允许持久性厂商提供程序获取图或加载图指定的状态之外的其他实体状态,但是要求持久性厂商提供程序获取由访问或加载图到指定的所有状态。

3.7.4.1、Fetch Graph Smantics(获取图语法)

  • 使用javax.persistence.fetchgraph属性指定实体图时,将由实体图的属性节点指定的属性视为FetchType.EAGER, 将未指定的属性视为FetchType.LAZY.
  • 根据属性类型,适用以下规则,本节的规则是递归应用的。
  • 不需要在获取图的属性节点中指定主键或版本属性。(这也适用于复合主键,包括嵌入的id主键)获取实体时,始终获取其主键和版本属性。但是,指定主键属性或版本属性并非不正确。
  • 除非指定了该属性,否则假定不获取除主键和版本属性以外的其他属性。以下规则适用于属性规范。
    • 如果属性是嵌入式属性,并且在属性节点中指定了属性,但未为该属性指定子图,则将获取可嵌入对象的默认获取图。如果为该属性指定了一个子图,则根据可嵌入属性的相应子图中的指定来获取他们。
    • 如果属性是基本类型的元素集合,并且在属性节点中指定属性,则将获取元素集合及其基本元素。
    • 如果属性是可嵌入元素的元素集合,并且在属性节点中指定了该属性,但是未为该属性指定子图,则将元素集合及其可嵌入元素的默认获取图一起获取。如果为该属性指定了一个子图,则根据相应的子图规范获取可嵌入元素的属性。
    • 如果属性是一对一或多对一关系,并且在属性节点中指定了属性,但未为该属性指定子图,则将获取目标实体的默认获取图。如果为该属性指定了一个子图,则根据相应的子图规范获取目标实体的属性。
    • 如果属性是一对多或多对多关系,并且在属性节点中指定了属性,但未指定子图,则将获取集合,并获取被引用实体的默认获取图。如果为该属性指定了一个子图,则根据相应的子图规范获取集合中的实体。
    • 如果在属性节点中指定的map的键是基本类型,则将其获取。如果在属性节点中指定的map的键是嵌入式类型,则将为可嵌入对象获取默认的获取图。否则如果map的键实体, 并且未为属性节点指定映射键子图,则根据其默认获取图获取map的键。如果为map关键字属性指定了关键字子图,则根据map关键字子图规范获取map关键字属性。
  • 例子
    • @NamedEntityGraph@Entitypublic class Phonenumber{  
      @Id  protected String number;    
      protected PhoneTypeEnum type;  
      ...
      }
                 
    • 在上面的示例中,仅仅会提前加载number属性。
  • @NamedEntityGraph=(attributeNodes={@NamedAttributeNode("projects")})
    @Entity
    public class Employee{
      
      @Id
      @GeneratedValue
      protected long id;
      
      @Basic
      protected String name;
      
      @Basic
      protected String employeeNumber;
      
      @OneToMany()
      protected List<Dependents>dependents;
      
      @OneToMany()
      protected List<Project> projects;
      
      @OneToMany()
      protected List<PhoneNumber> phoneNumbers;
      ...
    }
    
    @Entity
    @Inheritance
    public class Project{
      @Id
      @GeneratedValue
      protected long id;
      
      String name;
      
      @OneToOne(fetch=FetchType.EAGER)
      protected Requirements doc;
      ...
    }
    
    @Entity
    public class LargeProject extends Project{
      @OneToOne(fetch=FetchType.LAZY)
      protected Employee approver;
      ...
    }
    
    @Entity
    public class Requirements{
      @Id
      protected long id;
      
      @Lob
      protected String description;
      
      @OneToOne(fetch=FetchType.LAZY)
      protected Approval approval;
      ...
    }
               
  • 在上面的示例中,将提取Employee实体的主键以及相关的Project实例,这些实例将获取其默认提取图(id,name和doc属性)。相关的Requirements对象将根据其默认获取图进行获取。
  • 如果LargeProject的approver属性是FetchType.EAGER,并且任何项目是LargeProject的实例,则它们的approver属性也将被获取。由于approver属性的类型为Employee,因此还将获取approver的默认提取图(id, name和employeeNumber属性)。

3.7.4.1、加载图语法

  • 使用javax.persistence.loadgraph属性指定实体图时,将由实体图的属性节点指定的属性视为FetchType.EAGER, 将根据未指定的属性或指定的默认FetchType处理未指定的属性。
  • 以下规则适用,本节的规则是递归应用的
    • 不需要在加载图的属性节点中指定主键或版本属性。(这也适用于复合主键,包括嵌入的id主键)获取实体时,始终获取其主键和版本属性,但是,指定主键属性或版本属性并非不正确。
    • 如果属性是嵌入式属性,并且在属性节点中指定了属性,但未为该属性指定子图,则将获取可嵌入对象的默认获取图。如果为该属性指定了一个子图,则还将提取该子图所指定的属性。
    • 如果属性是基本类型的元素集合,并且在属性节点中指定属性,则将获取元素集合及其基本元素。
    • 如果属性是可嵌入元素的元素集合,并且在属性节点中指定了该属性,但是未为该属性指定子图,则将元素集合及其可嵌入元素的默认获取图一起获取。如果为该属性指定了一个子图,则根据相应的子图规范获取可嵌入元素的属性。
    • 如果属性是一对一或多对一关系,并且在属性节点中指定了属性,但未为该属性指定子图,则将获取目标实体的默认获取图。如果为该属性指定了一个子图,则根据相应的子图规范获取目标实体的属性。
    • 如果属性是一对多或多对多关系,并且在属性节点中指定了属性,但未指定子图,则将获取集合,并获取被引用实体的默认获取图。如果为该属性指定了一个子图,则根据相应的子图规范获取集合中的实体。
    • 如果在属性节点中指定的map的键是基本类型,则将其获取。如果在属性节点中指定的map的键是嵌入式类型,则将为可嵌入对象获取默认的获取图。否则如果map的键实体, 并且未为属性节点指定映射键子图,则根据其默认获取图获取map的键。如果为map关键字属性指定了关键字子图,则根据map关键字子图规范获取map关键字属性。
  • 例子
    • @NamedEntityGraph
      @Entity
      public class Phonenumber{
        @Id
        protected String number;
        
        protected PhoneTypeEnum type;
        ...
      }
                 
    • 在上面的示例中,number和type属性都会加载
  • @NamedEntityGraph=(attributeNodes={@NamedAttributeNode("projects")})
    @Entity
    public class Employee{
      
      @Id
      @GeneratedValue
      protected long id;
      
      @Basic
      protected String name;
      
      @Basic
      protected String employeeNumber;
      
      @OneToMany()
      protected List<Dependents>dependents;
      
      @OneToMany()
      protected List<Project> projects;
      
      @OneToMany()
      protected List<PhoneNumber> phoneNumbers;
      ...
    }
    
    @Entity
    @Inheritance
    public class Project{
      @Id
      @GeneratedValue
      protected long id;
      
      String name;
      
      @OneToOne(fetch=FetchType.EAGER)
      protected Requirements doc;
      ...
    }
    
    @Entity
    public class LargeProject extends Project{
      @OneToOne(fetch=FetchType.LAZY)
      protected Employee approver;
      ...
    }
    
    @Entity
    public class Requirements{
      @Id
      protected long id;
      
      @Lob
      protected String description;
      
      @OneToOne(fetch=FetchType.LAZY)
      protected Approval approval;
      ...
    }
               
  • 在上面的示例中,获取了Employee的默认获取图(id,name,employeeNumber属性)。还获取相关Project实例(id,name和doc属性)的默认提取图及其需求实例(id和description属性)

3.8、基本属性的类型转换

  • 属性转换工具允许开放人员指定在实体之间转换的方法,基本类型的属性的属性表示形式和数据库表示形式。转换器可以是用于转换由实体类,映射的超类或可嵌入的类定义的基本属性
  • 属性转换必须实现javax.persistence.AttributeConverter接口,一个转换实现类必须使用Converter注解标记或是在XML描述符中定义。如果注解Converter的autoApply的值为true,那么converter将会应用于所有目标类型属性,包括其他更复杂的属性类型中包含的基本属性值。参见10.6节。
  • /**
    * 实现此接口的类可用于将实体属性状态转换为数据库列表示形式,然后再次转换。
    * X 和Y类型可能是一样Java 类型
    * @param X 实体属性类型
    * @param Y 数据库列的类型
    */
    public interface AttributeConverter<X,Y>{
      
      /**
      * 将实体属性的值转换为数据库列值
      * @param attribute 将要转换实体属性的值
      * @return 转换后的数据库列值
      */
      public Y convertToDatabaseColumn (X attribute);
      
      
      /**
      * 将数据库列值转换为实体属性的值
      * 请注意,转换器编写者有责任为JDBC驱动程序使用的对应列指定正确的dbData类型,即不希望持久性提供程序进行这种
      * 类型的转换。
      * @param dbData 数据库列中要转换的数据
      * @return 转换后的实体的属性
      *
      */
      public X convertToEntityAttribute(Y dbData);
    }
               
  • 在Java EE环境中属性转换类需要在CDI开启状态时支持依赖注入。 利用CDI注入的属性转换器类也可以定义带有PostConstruct和PreDestroy注解的生命周期回调方法。这些方法将在注入发生之后和属性转换器实例被销毁之间分别调用。
  • 持久性提供商有责任使用CDI SPI去实例化属性转换类,使用注入的方式实例化对象。同时调用它们的PostConstruct和PreDestroy方法,如果有的话,并处理属性转换器实例。
  • 持久性提供商仅仅是在Java EE容器环境开启注入的时候支持CDI注入属性转换类实例,如果CDI是状态是关闭,那么持久性提供商禁止调用属性转换类去依赖CDI注入。
  • 属性转换器是非上下文对象。在支持注入到属性转换器中时,持久性提供商必须表现的好像执行了涉及使用CDI SPI的以下步骤(见【7】)
    • 获取 BeanManager实例(查看9.1节)
    • 为了属性转换类创建AnnotatedType实例
    • 为AnnotatedType主键创建InjectionTarget实例
    • 创建CreationalContext
    • Injection Target produce 方法去实例化监听器
    • Injection Target inject 方法去注入监听器实例
    • 通过InjectionTarget postConstruct 去调用PostConstruct回调,
  • 当监听器被销毁时候,持久性提供商需要执行如下步骤
    • 调用InjectionTarget preDestroy方法
    • 调用 InjectionTarget dispose 方法
    • 调用CreationalContext release方法。
  • 持久性提供商可能需要优化上述步骤,例如避免直接调用实际CDI SPI 而是依赖于特定容器的接口进行调用, 也就是最后的结果是一样就行。
  • 不使用CDI注入的属性转换器是无状态的,此类属性转换器的生命周期未指定
  • 支持所有基本类型的转换,除了以下内容外:ID属性(包括嵌入式ID和派生的标识的属性),版本属性,关系属性以及在XML描述符中显示标注为Enumerated或Temporal或指定的属性。自定应用转换器将不会应用于此类属性,并且通过使用Convert注解将转换器应用于此类属性的应用程序将无法移植。
  • 可以通过转换注解在各个属性的级别上指定类型转换。Convert注解也可以用于替代或禁用自动应用的转换。参见11.1.10节。
  • 可以将Convert注解直接应用于实体,映射超类或可嵌入类的属性,以指定属性的转换或替代使用已指定为autoApply=true的转换器。使用持久属性时,将Convert注解应用getter方法。
  • 可以将Convert注解应用于扩展映射超类的实体,以指定或覆盖继承的基本或嵌入式属性的转换 映射
  • 持久性提供商运行时负责在从数据库加载实体属性时以及在将实体属性状态存储到数据库之前,为目标属性类型调用指定的转换方法。持久性提供商必须将任何转换方法应用与Java 持久性查询语言查询或条件查询(例如,比较,批量更新等)中使用的路径表达式中的属性值实例, 然后再将其发送到数据库以执行查询。当此类转换的属性与文字或参数用于比较操作时,还必须转换与之比较的文字或参数的值。如果Java持久性查询语句查询或条件查询的结果包含已为其指定转换映射的一个或多个实体属性,则持久性提供商必须将指定的转换应用于查询结果中的相应值,然后再将其返还给应用程序。未定义对转换后的属性使用函数(包括聚合)的方法,如果从转换后的属性使用函数(包括聚合)的方法,如果从转换方法抛出异常,则持久性提供商必须将异常包装在PersistenceException中,并且,如果将持久性上下文加入到事务中,则将事务标记为回滚

3.9、缓存

  • 该规范支持持久性提供商使用二级缓存,第二级缓存(如果使用)是持久性上下文的基础,并且对应用程序基本上是透明的。
  • 通常使用二级缓存来增强性能。但是,使用高速缓存可能会对应用程序看到的数据的最新性产生影响,从而导致”过时的读取“,旧的读取定义为比持久性上下文启动点更早的实体或实体状态的读取。
  • 该规范定义了以下可移植的配置选项,应用程序开发人员可以使用这些选项来控制缓存行为。持久性提供商可能支持其他提供程序特定的选项,但必须遵守所有规范定义的选项

3.9.1、shared-cache-mode 元素

  • 持久性单元的实体和与实体相关的状态是否将被缓存由persistence.xml文件的shared-cache-mode元素的值确定。
  • shared-cache-mode元素具有五个可能的值:ALL, NONE, ENABLE_SELECTIVE, DISABLE_SELECTIVE, UNSPECIFIED
  • ALL的值将导致所有实体以及与实体相关的状态和数据被缓存。
  • 值为NONE导致对持久性单元禁用缓存,如果指定为NONE,则持久提供商禁止缓存
  • 值ENABLE_SELECTIVE和DISABLE_SELECTIVE与Cacheable注解(或XML元素)结合使用。Cacheable 注解指定如果persistence.xml shared-cache-mode 元素启用了这种选择性缓存,是否应缓存实体。Cacheable元素是在实体类上指定的,它适用于给定的实体以及子类,除非随后后被子类覆盖。
    • Cacheable(false)表示提供商不得缓存实体和实体的状态。
    • 值ENABLE_SELECTIVE启用缓存,并导致指定了Cacheable(true)(或其XML等效项)的实体被缓存。未指定Cacheable(true)或指定Cacheable(false)的实体不得进行缓存
    • 值DISABLE_SELECTIVE启用缓存,并使用所有实体(指定了Cacheable(false)的实体除外)都被缓存。指定为可缓存(false)的实体不得被缓存。
  • 如果未在persistence.xml文件中指定shared-cache-mode元素,或者未指定shared-cache-mode元素的值,并且未指定javax.persistence.sharedCache.mode属性,如果未指定shared-cache-mode元素和javax.persistence.sharedCache.mode属性,则Cacheable注解(和XML等效)的语义未定义。
  • 不需要持久性提供程序来支持使用二级缓存。如果持久性提供程序不支持使用二级缓存或未安装二级缓存,则该元素将被忽略,并且不会发生缓存。
  • 在7.10节中介绍了对二级缓存的进一步控制。

3.9.2、缓存检索模式和缓存存储模式属性

  • 可以通过EntityManager setProperty方法在持久性上下文级别指定高速缓存检索模式和高速缓存存储模式属性,可以为EntityManager的find和refresh方法以及Query,TypedQuery和Stored-procedureQuery setHint方法指定这些属性。为find, refresh 和Query, TypedQuery和StoredProcedureQuery setHint 方法指定的缓存检索模式和缓存存储模式属性将覆盖为持久性上下文指定的属性,这些持久性上下文分别用于指定的find和refresh调用以及用于执行指定的查询。
  • 如果通过shared-cache-mode元素的NONE值禁用了缓存,则必须忽略缓存检索模式和缓存存储模式属性。否则,如果指定了ENABLE_SELECTIVE值,但未为特定实体指定Cacheable(true), 则该实体将忽略它们,否则,将为该实体忽略它们。 如果指定了DISABLE_SELECTIVE值,则对于为其指定Cacheable(false)的任何实体都将忽略它们。
  • 启用高速缓存时,必须注意高速缓存检索模式和高速缓存存储模式属性,无论是由于指定了共享高速缓存模式元素而启用高速缓存还是由于提供者特定的选项而启用了高速缓存。使用缓存检索模式或缓存存储模式属性但未指定shared-cache-mode元素的应用程序将不可移植。
  • 缓存检索模式和缓存存储模式属性分别为javax.persistent.cache.retrieveMode和javax.persistence.cache.storeMode. 这些属性具有以下定义的语义。
  • resolveMode属性指定通过find方法和执行查询来检索数据时的行为。refreshMode属性将被refresh方法忽略,该方法始终导致从数据库而不是从缓存中检索数据。
  • package javax.persistence;
    public enum CacheRetrieveMode{
      /**
      *  从缓存中读实体数据, 这是默认的行为
      */
      USE,
      
      /**
      * 绕过缓存,直接从数据库获取数据
      */
      BYPASS
    }
               
  • storeMode属性指定从数据库读取数据以及将数据提交到数据库时的行为。
  • package javax.persistence;
    
    public enum CacheStoreMode{
      
      /**
      * 在读取数据库数据和提交到数据库时候,在新增和更新实体数据到缓存中。
      * 这个是默认行为,不会强制从数据库中刷新到cache中。
    	*/
      USE,
      
      /**
      * 不插入缓存
      */
      BYPASS,
      
      /**
      * 在读取数据库数据和提交到数据库时候,在新增和更新实体数据到缓存中。
      * 从数据库中强制刷新cache
      */
      REFRESH
    }
               

3.10 查询 APIs

  • Query和TypedQuery API用于执行静态查询和动态查询。这些API还支持参数绑定和分页控制。StoredProcedureQuery API 用于执行调用数据库定义的存储过程的查询。
  • package javax.persistence;
    
    import java.util.Calendar; 
    import java.util.Date; 
    import java.util.List; 
    import java.util.Set; 
    import java.util.Map; 
    import java.util.Stream;
    
    /**
    * 接口用于控制查询执行
    */
    public interface Query{
      
      /**
      * 执行一个SELECT查询返回查询无类型的List结果
      * @return list 结果
      * @throws IllegalStateException 如果为Java持久性查询语言UPDATE或DELETE语言调
      * 用
      * @throws QueryTimeoutException 如果执行查询超过设置超时时间的值,只会发生语句
      * 级别的回滚
      * @throws TransactionRequiredException 如果设置了除了NONE以外的锁定模式,并且
      * 没有事务或持久性上下文未加入事务中。
      * @throws PessimisticLockException 如果悲观锁失败或事务回滚
      * @throws LockTimeoutException : 悲观锁加锁失败且只有语句级别回滚
      * @throws PersistenceException: 如果执行查询超过设置超时时间的值且事务回滚
      */
      List getResultList();
      
      
      /**
      * 执行一个SELECT查询返回单个无类型结果
      * @return 单个 结果
      * @throws NoResultException : 如果没有结果
      * @throws NonUniqueResultException 两个结果及以上
      * @throws IllegalStateException 如果为Java持久性查询语言UPDATE或DELETE语言调
      * 用
      * @throws QueryTimeoutException 如果执行查询超过设置超时时间的值,只会发生语句
      * 级别的回滚
      * @throws TransactionRequiredException 如果设置了除了NONE以外的锁定模式,并且
      * 没有事务或持久性上下文未加入事务中。
      * @throws PessimisticLockException 如果悲观锁失败或事务回滚
      * @throws LockTimeoutException : 悲观锁加锁失败且只有语句级别回滚
      * @throws PersistenceException: 如果执行查询超过设置超时时间的值且事务回滚
      */
      Object getSingleResult();
      
      
      /**
      *  执行一个update或delete语句
      * @return 返回已更新或已删除的实体个数
      * @throws NoResultException : 如果没有结果
      * @throws NonUniqueResultException 两个结果及以上
      * @throws IllegalStateException 如果为Java持久性查询语言SELECT语言调
      * 用或标准查询语句
      * @throws QueryTimeoutException 如果执行查询超过设置超时时间的值,只会发生语句
      * 级别的回滚
      * @throws TransactionRequiredException 如果设置了除了NONE以外的锁定模式,并且
      * 没有事务或持久性上下文未加入事务中。
      * @throws PersistenceException: 如果执行查询超过设置超时时间的值且事务回滚
      */
      int executeUpdate();
      
      /**
      * 设置最大取出的结果数
      * @param maxResult
      * @return 相同查询实体
      * @throws IllegalArgumentException 如果参数是负数
      *
    	*/
      Query setMaxResults(int maxResult);
      
      
      /**
      * 查询对象设置要检索的最大结果数,如果未将setMaxResults应用于查询对象,则返回Integer.MAX_VALUE
      * @return 结果级的最大数
      */
      int getMaxResults();
      
      /**
      *  设置取用第一个结果位置
      * @param startPosition 其实位置,数字从0开始
      * @return 返回相同查询实例
      * @throws IllegalArgumentException 如果参数是负数
      */
      Query setFirstResult(int startPosition);
      
      
      /**
      * 获取第一个查询结果的位置,如果返回0表示setFirstResult没有应用查询对象。
      * @return 第一个结果的位置
      */
      int getFirstResult();
      
      /**
      * 设置查询属性或提示,提示元素可能被用于具体查询属性和提示,提供者必须遵守此规范定义的属性。
      * 特定厂商提示可能不能在提供者识别就会默认忽略。可移植应用不应该依赖于标准超时提示。根据使用的数据库和提供者
      * 使用的锁定机制, 提示可能不会遵守。
      * @param hintName 属性或提示的名称
      * @param value 值
      * @return 相同的查询实例
      * @throws IllegalArgumentException, 如果第二参数不是有效的实现
      *
      */
      Query setHint(String hintName, Object value);
      
      
      /**
      * 获取对查询实例有效的属性和提示以及关联的值。
      * @return 查询属性和提示
      */
      Map<String,Object> getHits();
      
      
      /**
      * 绑定参数对象的值
      * @param param 参数对象
      * @param value 参数值
      * @return 查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应
      */
      <T> Query setParameter(Parameter<T> param, T value);
      
      /**
      * 绑定参数对象的一个java.util.Calendar
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应
      *
      */
      Query setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType);
      
      /**
      * 绑定参数对象的一个java.util.Date
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应
      *
      */
      Query setParameter(Parameter<Date> param, Date value, TemporalType temporalType);
      
      /**
      * 绑定参数值到一个命名的参数
      * @param param 参数对象
      * @param value 参数值
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应或参数不是正确类型
      *
      */
      Query setParameter(String name, Object value);
      
      /**
      * 绑定java.util.Calendar到一个命名的参数
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应或参数不是正确类型
      *
      */
      Query setParameter(String name, Calendar value, TemporalType temporalType);
      
      /**
      * 绑定java.util.Date到一个命名的参数
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应或参数不是正确类型
      *
      */
      Query setParameter(String name, Date value, TemporalType temporalType);
      
      /**
      * 在指定位置绑定参数值
      * @param position 位置
      * @param value 参数的值
      * @retrun 相同查询实例
      * @throws IllegalArgumentException 如果对应位置没有一个参数值或参数的类型不正确
      *
      */
      Query setParameter(int position, Object value);
      
       /**
      * 在指定位置绑定java.util.Calendar
      * @param position 位置
      * @param value 参数的值
      * @param temporalType
      * @retrun 相同查询实例
      * @throws IllegalArgumentException 如果对应位置没有一个参数值或参数的类型不正确
      *
      */
      Query setParameter(int position, Calendar value, TemporalType temporalType);
      
       /**
      * 在指定位置绑定java.util.Date
      * @param position 位置
      * @param value 参数的值
      * @param temporalType
      * @retrun 相同查询实例
      * @throws IllegalArgumentException 如果对应位置没有一个参数值或参数的类型不正确
      *
      */
      Query setParameter(int position, Date value, TemporalType temporalType);
      
      
      /**
      * 获取与查询的声明参数相对应的参数对象。
      * 如果查询没有参数,则返回空set集合, 原生查询不需要支持此方法。
      * @return  set集合参数
      * @throws IllegalStateException 当实现不支持这样使用时调用一个原生查询
      */
      Set<Parameter<?>> getParameters();
      
      /**
      * 获取与之对应声明名称的参数对象
      * 原生查询不需要支持此方法。
      * @param name
      * @return  参数对象
      * @throws IllegalArgumentException ,如果指定具体参数名称的参数不存在
      * @throws IllegalStateException 当实现不支持这样使用时调用一个原生查询
      */
      Parameter<?> getParameter(String name);
      
      
      /**
      * 获取与之对应声明名称和类型的参数对象
      * 仅要求标准查询支持此方法
      * @param name 参数名称
      * @param type 类型
      * @return  参数对象
      * @throws IllegalArgumentException ,如果指定具体参数名称的参数不存在或者不是这种类型
      * @throws IllegalStateException 当实现不支持这样使用时调用一个原生查询 和 Java持久查询不支持这种实现
      * 使用
      */
      <T> Parameter<T> getParameter(String name, Class<T> type);
      
      /**
      * 获取与之对应位置的参数对象
      * 原生查询不需要支持此方法。
      * @param position
      * @return  参数对象
      * @throws IllegalArgumentException ,如果指定具体参数名称的参数不存在
      * @throws IllegalStateException 当实现不支持这样使用时调用一个原生查询
      */
      Parameter<?> getParameter(int position);
      
      /**
      * 获取与之对应位置和类型的参数对象
      * 这个方法并不需要提供者支持这个方法
      * @param position
      * @param type 类型
      * @return  参数对象
      * @throws IllegalArgumentException ,如果指定具体参数名称的参数不存在或者不是这种类型
      * @throws IllegalStateException 当实现不支持这样使用时调用一个原生查询和 Java持久查询不支持这种实现
      * 使用
      */
      <T> Parameter<T> getParameter(int position, Class<T> type);
      
      /**
      * 返回一个boolean值表示一个值是否绑定到这个参数上
      * @param param 参数对象
      * @return 返回一个boolean值表示一个值是否绑定到这个参数上
      *
      */
      boolean isBound(Parameter<?> param);
      
      /**
      * 返回绑定这个参数的输入值(注意OUT参数是无边界)
    	* @param param 参数对象
    	* @return 参数对象
    	* @throws IllegalArgumentException 如果参数不是查询的参数
    	* @throws IllegalStateException 如果这个参数没有被绑定
    	*/
      <T> getParameterValue(Parameter<T> param);
      /**
      * 返回绑定这个参数的输入值(注意OUT参数是无边界)
    	* @param param 参数名称
    	* @return 参数对象
    	* @throws IllegalArgumentException 如果特定参数名称不存在
    	* @throws IllegalStateException 如果这个参数没有被绑定
    	*/
      Object getParameterValue(String name);
      
      /**
      * 返回绑定这个参数的输入值(注意OUT参数是无边界)
    	* @param position 参数的位置
    	* @return 参数对象
    	* @throws IllegalArgumentException 如果特定位置不存在参数
    	* @throws IllegalStateException 如果这个参数没有被绑定
    	*/
      Object getParameterValue(int position);
      
      /**
      * 为查询操作设置flush模式类型, flush模式类型适用于查询,而与实体管理器使用的flush模式类型无关
      * @param flushMode
      * @return 相同查询实例
      */
      Query setFlushMode(FlushModeType flushMode);
      
      /**
      * 获取在查询执行中有效的flush模式,如果查询对象没有设置flush模式,则返回实体管理器的有效的flush模式
      * @return flush 模式
      */
      FlushModeType getFlushMode();
      
      
      /**
      * 为查询操作设置lock模式类型
      * @param lockMode
      * @return 相同查询实例
      * @throws IllegalStateException 如果是Java持久性查询或CriteriaQuery查询
      * 发现查询不是语言SELECT查询
      */
      Query setLockMode(LockModeType lockMode);
      
      /**
      * 获取查询的当前锁定模式,如果尚未在查询对象上设置锁定模式,则返回null
      * @return 锁定模式
      * @throws IllegalStateException 如果是Java持久性查询或CriteriaQuery查询
      * 发现查询不是语言SELECT查询
      */
      LockModeType getLockMode();
      
      /**
      * 返回特定类型对象去允许获取特定供应商API,如果提供商的查询并不没有实现和支持特定类,这个
      * PersistenceException将会抛出
      * @param cls 这个对象的类将会返回,这个类一般要不是潜在查询实现类或一个接口的实现。
      * @return 实体的特定类
      * @throws PersistenceException  如果供应商不支持这样调用
      *
      */
      <T> T unwrap(Class<T> cls);
      
      /**
      * 执行一个SELECT查询返回查询无类型java.util.Stream查询结果
      * 默认情况下,此方法委托给getResultList().stream(),但是持久性提供程序可以选择重写此方法以提供其他功能。
      * @return 返回结果的stream流
      * @throws QueryTimeoutException 如果查询时间超过设定时间且只有语句级别的回滚
      * @throws IllegalStateException 如果调用是java 持久查询语言UPDATE或DELETE方法
      * @throws TransactionRequiredException 如果设置锁的模式不是NONE且当前执行没有
      * 事务上下文或加入当前事务
      * @throws PessimisticLockException 如果悲观锁锁定失败且事务回滚
      * @throws LockTimeoutException 如果悲观锁定失败且只有语句级别回滚
      * @throws PersistenceException 如果查询时间超过设置超时时间且事务回滚
      */
      default Stream getResultStream(){
        return getResultList().stream();
      }
      
    }
               

3.10.2、TypedQuery 接口

  • package javax.persistence;
    import java.util.List;
    import java.util.Date;
    import java.util.Calendar;
    import java.util.Stream;
    
    /**
    * 用于控制类型化查询的执行的接口
    * @param<X> 查询结果类型
    *
    */
    public interface TypedQuery<X> extend Query{
      
       /**
      * 执行一个SELECT查询返回查询有类型的List结果
      * @return list 结果
      * @throws IllegalStateException 如果为Java持久性查询语言UPDATE或DELETE语言调
      * 用
      * @throws QueryTimeoutException 如果执行查询超过设置超时时间的值,只会发生语句
      * 级别的回滚
      * @throws TransactionRequiredException 如果设置了除了NONE以外的锁定模式,并且
      * 没有事务或持久性上下文未加入事务中。
      * @throws PessimisticLockException 如果悲观锁失败或事务回滚
      * @throws LockTimeoutException : 悲观锁加锁失败且只有语句级别回滚
      * @throws PersistenceException: 如果执行查询超过设置超时时间的值且事务回滚
      */
      List<X> getResultList();
      
       /**
      * 执行一个SELECT查询返回单个有类型结果
      * @return 单个 结果
      * @throws NoResultException : 如果没有结果
      * @throws NonUniqueResultException 两个结果及以上
      * @throws IllegalStateException 如果为Java持久性查询语言UPDATE或DELETE语言调
      * 用
      * @throws QueryTimeoutException 如果执行查询超过设置超时时间的值,只会发生语句
      * 级别的回滚
      * @throws TransactionRequiredException 如果设置了除了NONE以外的锁定模式,并且
      * 没有事务或持久性上下文未加入事务中。
      * @throws PessimisticLockException 如果悲观锁失败或事务回滚
      * @throws LockTimeoutException : 悲观锁加锁失败且只有语句级别回滚
      * @throws PersistenceException: 如果执行查询超过设置超时时间的值且事务回滚
      */
      X getSingleResult();
    /**
      * 设置最大取出的结果数
      * @param maxResult
      * @return 相同查询实体
      * @throws IllegalArgumentException 如果参数是负数
      *
    	*/
      TypedQuery<X> setMaxResults(int maxResult);
      
      /**
      *  设置取用第一个结果位置
      * @param startPosition 其实位置,数字从0开始
      * @return 返回相同查询实例
      * @throws IllegalArgumentException 如果参数是负数
      */
      TypedQuery<X> setFirstResult(int startPosition);
      
      /**
      * 设置查询属性或提示,提示元素可能被用于具体查询属性和提示,提供者必须遵守此规范定义的属性。
      * 特定厂商提示可能不能在提供者识别就会默认忽略。可移植应用不应该依赖于标准超时提示。根据使用的数据库和提供者
      * 使用的锁定机制, 提示可能不会遵守。
      * @param hintName 属性或提示的名称
      * @param value 值
      * @return 相同的查询实例
      * @throws IllegalArgumentException, 如果第二参数不是有效的实现
      *
      */
      Query setHint(String hintName, Object value);
      
      
      /**
      * 绑定参数对象的值
      * @param param 参数对象
      * @param value 参数值
      * @return 查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应
      */
      <T> TypedQuery<X> setParameter(Parameter<T> param, T value);
      
      /**
      * 绑定参数对象的一个java.util.Calendar
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应
      *
      */
      TypedQuery<X> setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType);
      
      /**
      * 绑定参数对象的一个java.util.Date
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应
      *
      */
      TypedQuery<X> setParameter(Parameter<Date> param, Date value, TemporalType temporalType);
      
      /**
      * 绑定参数值到一个命名的参数
      * @param param 参数对象
      * @param value 参数值
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应或参数不是正确类型
      *
      */
      TypedQuery<X> setParameter(String name, Object value);
      
      /**
      * 绑定java.util.Calendar到一个命名的参数
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应或参数不是正确类型
      *
      */
      TypedQuery<X> setParameter(String name, Calendar value, TemporalType temporalType);
      
      /**
      * 绑定java.util.Date到一个命名的参数
      * @param param 参数对象
      * @param value 参数值
      * @param temporalType 
      * @return 相同查询实例
      * @throws IllegalArgumentException 如果参数与查询的参数不对应或参数不是正确类型
      *
      */
      TypedQuery<X> setParameter(String name, Date value, TemporalType temporalType);
      
      /**
      * 在指定位置绑定参数值
      * @param position 位置
      * @param value 参数的值
      * @retrun 相同查询实例
      * @throws IllegalArgumentException 如果对应位置没有一个参数值或参数的类型不正确
      *
      */
      TypedQuery<X> setParameter(int position, Object value);
      
       /**
      * 在指定位置绑定java.util.Calendar
      * @param position 位置
      * @param value 参数的值
      * @param temporalType
      * @retrun 相同查询实例
      * @throws IllegalArgumentException 如果对应位置没有一个参数值或参数的类型不正确
      *
      */
      TypedQuery<X> setParameter(int position, Calendar value, TemporalType temporalType);
      
       /**
      * 在指定位置绑定java.util.Date
      * @param position 位置
      * @param value 参数的值
      * @param temporalType
      * @retrun 相同查询实例
      * @throws IllegalArgumentException 如果对应位置没有一个参数值或参数的类型不正确
      *
      */
      TypedQuery<X> setParameter(int position, Date value, TemporalType temporalType);
      
     
      
      /**
      * 为查询操作设置flush模式类型, flush模式类型适用于查询,而与实体管理器使用的flush模式类型无关
      * @param flushMode
      * @return 相同查询实例
      */
      TypedQuery<X> setFlushMode(FlushModeType flushMode);
      
      
      /**
      * 为查询操作设置lock模式类型
      * @param lockMode
      * @return 相同查询实例
      * @throws IllegalStateException 如果是Java持久性查询或CriteriaQuery查询
      * 发现查询不是语言SELECT查询
      */
      TypedQuery<X> setLockMode(LockModeType lockMode);
      
     
      /**
      * 执行一个SELECT查询返回查询无类型java.util.Stream查询结果
      * 默认情况下,此方法委托给getResultList().stream(),但是持久性提供程序可以选择重写此方法以提供其他功能。
      * @return 返回结果的stream流
      * @throws QueryTimeoutException 如果查询时间超过设定时间且只有语句级别的回滚
      * @throws IllegalStateException 如果调用是java 持久查询语言UPDATE或DELETE方法
      * @throws TransactionRequiredException 如果设置锁的模式不是NONE且当前执行没有
      * 事务上下文或加入当前事务
      * @throws PessimisticLockException 如果悲观锁锁定失败且事务回滚
      * @throws LockTimeoutException 如果悲观锁定失败且只有语句级别回滚
      * @throws PersistenceException 如果查询时间超过设置超时时间且事务回滚
      */
      default Stream getResultStream(){
        return getResultList().stream();
      }
      
    }
               

3.10.3、 Tuple接口(元组接口)

package javax.persistence;
import java.util.List;
/**
* 用于提取查询结果元组的元素的接口
*
*/
public interface Tuple{
  
  /**
  * 获取指定的元组元素的值
  * @param tupleElement 元组元素
  * @return 元组元素
  * @throws IllegalArgumentException 在查询结果元素中如果元组元素并没有对应到一个元素
  */
  <X> X get(TupleElement<X> tupleElement);
  
  /**
  * 获取指定的别名元组元素的值
  * @param 别名 赋值到元组元素的值
  * @param type 元组元素的类型
  * @return 元组元素值
  * @throws IllegalArgumentException 在查询结果元素中如果元组元素并没有对应到一个元素 
  * 或者不是指定的特定类型
  *
  */
  <X> X get(String alias, Class<X> type);
  
  
  /**
  * 获取指定的别名元组元素的值
  * @param 别名 赋值到元组元素的值
  * @return 元组元素值
  * @throws IllegalArgumentException 在查询结果元素中如果元组元素并没有对应到一个元素 
  *
  */
  Object get(String alias);
  
  
  /**
  * 获取指定的位置元组元素的值, 一个位置是从0开始的
  * @param i 结果元组的位置
  * @param type 元组元素的类型
  * @return 元组元素值
  * @throws IllegalArgumentException i超过结果元组的长度 
  * 或者不是指定的特定类型
  *
  */
  <X> X get(int i, Class<X> type);
  
   /**
  * 获取指定的位置元组元素的值, 一个位置是从0开始的
  * @param i 结果元组的位置
  * @return 元组元素值
  * @throws IllegalArgumentException i超过结果元组的长度 
  *
  */
  Object get(int i);
  
  /**
  * 返回数组形式的结果元组数据
  * @return 元组元素的值
  */
  Object[] toArray();
  
  /**
  * 返回元组元素
  * @return 元组元素
  */
  List<TupleElement<?>> getElements();
}
           

3.10.4、TupleElement 接口

package javax.persistence;

/**
* TupleElement接口定义在查询结果元组中返回的元素
* @param <X> 元素的类型
*/
public interface TupleElement<X>{
  
  /**
  *  返回元组元素的运行时java 类型。
  * @return 返回元组元素的运行时java 类型。
  */
  Class<?extends X> getJavaType();
  
  /**
  * 返回分配给元组元素的别名;如果未分配别名,则返回null。
  *
  */
  String getAlias();
}
           

3.10.5、Parameter Interface ( 参数接口)

package javax.persistence;
/**
* 查询参数对象的类型
* @param<T> 参数的类型
*/
public interface Parameter<T> {
  
  /**
  * 返回参数的名称,如果参数没有被命名或名称没有赋值的话将返回null
  * @return paramater name 参数名称
  *
  */
  String getName();
  
  /**
  *  返回参数position,如果参数不是位置参数,则返回null.
  * @return 参数的位置
  */
  Integer getPosition();
  
  /**
  * 返回参数的Java类型。绑定到参数的值必须可分配给改类型。仅标准查询要求支持此方法。使用此
  * 方法进行Java持久性查询语言查询和本机查询的应用程序将不可移植性。
  *
  */
  Class<T> getParameterType();
}
           

3.10.6 StoredProcedureQuery Interface

package javax.persistence;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* 用于控制存储过程查询操作
*/
public interface StoredProcedureQuery extends Query{
  
  /**
  * 设置查询属性或提示。提示元素可用于指定查询属性和提示。提供者必须遵守此规范定义的属性。
  * 提供者无法识别的特定于供应商的提供必须被静默忽略。可移植应用程序不依赖于标准超时提示。
  * 根据使用的数据库,可能会或可能不会观察到此提示
  * @param hintName 属性或提示的名称
  * @param value 属性和提示的值
  * @return 相同查询实例
  * @throws IllegalArgumentException 如果第二参数不是有效的实现
  */
  StoredProcedureQuery setHint(String hintName, Object value);
  
  /**
  * 绑定参数对象的值
  * @param param 参数对象
  * @param value 参数的值
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数没有对应查询参数
  */
  <T> StoredProcedureQuery setParameter(Parameter<T> param, T value);
  
  /**
  * 绑定一个java.util.Calendar到参数对象中
  * @param param 参数对象
  * @param value 参数的值
  * @param temporalType 时间类型
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数没有对应查询参数
  */
  StoredProcedureQuery setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType);
  
   /**
  * 绑定一个java.util.Date到参数对象中
  * @param param 参数对象
  * @param value 参数的值
  * @param temporalType 时间类型
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数没有对应查询参数
  */
  StoredProcedureQuery setParameter(Parameter<Date> param, Date value, TemporalType temporalType);
  
  
  /**
  * 绑定一个参数到命名的参数上
  * @param param 参数名称
  * @param value 参数的值
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数没有对应查询参数 或参数类型不是正确的
  */
  StoredProcedureQuery setParameter(String name, Object value);
  
  /**
  * 绑定一个java.util.Calendar参数到命名的参数上
  * @param name 参数名称
  * @param value 参数的值
  * @param temporalType 时间类型
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数没有对应查询参数 或参数类型不是正确的
  */
  StoredProcedureQuery setParameter(String name, Calendar value, TemporalType temporalType);
  
  
  /**
  * 绑定一个java.util.Date参数到命名的参数上
  * @param name 参数名称
  * @param value 参数的值
  * @param temporalType 时间类型
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数没有对应查询参数 或参数类型不是正确的
  */
  StoredProcedureQuery setParameter(String name, Date value, TemporalType temporalType);
  
   /**
  * 绑定一个参数到某个位置的参数上
  * @param position 参数位置
  * @param value 参数的值
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数位置没有对应的位置 或参数类型不是正确的
  */
   StoredProcedureQuery setParameter(int position, Object value);
  
  
  /**
  * 绑定一个java.util.Calendar到某个位置的参数上
  * @param position 参数位置
  * @param value 参数的值
  * @param temporalType 时间类型
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数位置没有对应的位置 或参数类型不是正确的
  */
    StoredProcedureQuery setParameter(int position, Calendar value, TemporalType temporalType);
  
   /**
  * 绑定一个java.util.Date到某个位置的参数上
  * @param position 参数位置
  * @param value 参数的值
  * @param temporalType 时间类型
  * @return 相同的查询实例
  * @throws IllegalArgumentException 如果参数位置没有对应的位置 或参数类型不是正确的
  */
    StoredProcedureQuery setParameter(int position, Date value, TemporalType temporalType);
  
  
    /**
  * 为查询操作设置flush模式类型, flush模式类型适用于查询,而与实体管理器使用的flush模式类型无关
  * @param flushMode
  * @return 相同查询实例
  */
  Query setFlushMode(FlushModeType flushMode);
  
  /**
  *  注册一个位置的参数
  * 所有位置的参数必须注册
  * @param position 参数的位置
  * @param type 参数的类型
  * @param mode 参数模式
  * @return 相同查询实例
  */
  StoredProcedureQuery registerStoredProcedureParameter(int position, Class type, ParameterMode mode);
  
  
    /**
  *  注册一个名称的参数
  * @param parameterName 注册的参数名称或者具体元数据
  * @param type 参数的类型
  * @param mode 参数模式
  * @return 相同查询实例
  */
  StoredProcedureQuery registerStoredProcedureParameter(String parameterName, Class type, ParameterMode mode);
  
  
  /**
  * 检索通过INOUT或OUT参数从过程返回的值。
  * 为了便于移植,所有结果均与结果集相对应并且必须在输出参数的值之前检索更新计数。
  * @param position 参数的位置
  * @return 通过参数传回的结果
  * @throws IllegalArgumentException 如果位置没有对应的查询参数或它不是INOUT或OUT参数
  */
  Object getOutputParameterValue(int position);
  
   /**
  * 检索通过INOUT或OUT参数从过程返回的值。
  * 为了便于移植,所有结果均与结果集相对应并且必须在输出参数的值之前检索更新计数。
  * @param parameterName 参数名称
  * @return 通过参数传回的结果
  * @throws IllegalArgumentException 如果参数名称没有对应的查询参数或它不是INOUT或
  * OUT参数
  */
  Object getOutputParameterValue(String parameterName);
  
  
  /**
  * 如果第一个结果与结果集相对应,则返回true,如果它是更新计数,或者除通过INOUT和OUT参数
  *(如果有)以外没有其他结果,则返回false
  * @return 如果第一个结果对应于结果集,则为true
  * @throws QueryTimeoutException 如果查询执行超过了查询超时值设置,并且仅回滚了该语
  * 句
  * @throws PersistenceException 如果查询执行超过了查询超时值设置,并且回滚了事务
  *
  */
  boolean execute();
  
  
  /**
  * 如果没有挂起的结果或第一个结果不是更新计数,则返回-1的更新计数。如果需要提供程序将在查
  * 询上调用execute
  * @return 更新计数;如果没有未决结果或下一个结果不是更新计数,则为-1
  * @throws TransactionRequiredException 如果没有事务或当前加入事务
  * @throws QueryTimeoutException 如果查询执行超过了查询超时值设置,并且仅回滚了该语
  * 句
  * @throws PersistenceException 如果查询执行超过了查询超时值设置,并且回滚了事务
  *
  */
  int executeUpdate();
  
  
  /**
  * 从下一个结果集中检索结果列表。如果需要,提供程序将在查询上调用execute. REF_CURSOR 
  * 结果集(如果有)将按照REF_CURSOR 参数在查询中注册的顺序进行检索。
  * @return 结果列表或null为下一项不是结果集
  * @throws QueryTimeoutException 如果查询执行超过了查询超时值设置,并且仅回滚了该语
  * 句
  * @throws PersistenceException 如果查询执行超过了查询超时值设置,并且回滚了事务
  *
  */
  List getResultList();
  
  
  /**
  * 从下一个结果集中检索单个结果。如果需要,提供程序将在查询上调用execute.REF_CURSOR结果
  * 集(如果有)将按照REF_CURSOR参数在查询中注册的顺序进行检索。
  * @return 结果列表或null为下一项不是结果集
  * @throws NoResultException 如果下一项没有结果集
  * @throws NonUniqueResultException 如果结果一个以上
  * @throws QueryTimeoutException 如果查询执行超过了查询超时值设置,并且仅回滚了该语
  * 句
  * @throws PersistenceException 如果查询执行超过了查询超时值设置,并且回滚了事务
  *
  */
  Object getSingleResult();
  
  /**
  * 如果下一个结果与结果集相对应,则返回true。如果是更新计数,或者除了通过INOUT和OUT参数
  *(如果有)以外没有其他结果,则返回false
  * @return true下一项还有结果集
  * @throws QueryTimeoutException 如果查询执行超过了查询超时值设置,并且仅回滚了该语
  * 句
  * @throws PersistenceException 如果查询执行超过了查询超时值设置,并且回滚了事务
  *
  */
  boolean hasMoreResults();
  
  /**
  * 返回更新计数,如果没有未决结果或下一个结果不是更新计数,则返回-1
  * @return 返回更新计数,如果没有未决结果或下一个结果不是更新计数,则返回-1
  * @throws QueryTimeoutException 如果查询执行超过了查询超时值设置,并且仅回滚了该语
  * 句
  * @throws PersistenceException 如果查询执行超过了查询超时值设置,并且回滚了事务
  *
  */
  int getUpdateCount();
  
}
           

3.10.7、Query Execution ( 查询执行)

  • Java持久性查询语言,Criteria API 和本机SQL选择查询是使用getResultList和getSingleResult方法执行的。使用executeUpdate方法执行更新和删除操作(更新和删除“查询”):
    • 对于TypedQuery实例,在条件查询的情况下,查询结果类型由创建CriteriaQuery对象时指定的查询类型确定,如第6.5.1节“创建CriteriaQuery"中所述。对于Java持久性查询语言查询,结果的类型由createQuery或createNamedQuery方法的resultClass参数确定,并且查询的选择列表必须仅包含一个项目,该项目必须可分配给指定的类型。
    • 对于查询实例,其选择列表由多个选择表达式组成的查询结果元素的类型为Object[]. 如果选择列表仅包含一个选择表达式,则查询结果的元素为对象类型。当使用本地SQL查询时,SQL结果映射(请参阅第3.10.16节)确定返回多少项(实体,标量值等)。如果返回多个项目,则查询结果的元素类型为Object[]. 如果由于SQL结果集映射而仅返回单个项目,或者指定了结果类,则查询结果的元素为Object类型。
  • 可以使用getResultList, getSingleResult和execute方法执行存储过程查询。可以使用executeUpdate方法执行仅执行更新或删除的存储过程。存储过程查询执行将在3.10.17.3节中详细介绍。
  • 如果指定的参数实例不对应查询的参数,如果指定的参数名称与查询的命名参数不对应,或者指定的位置值不对应与查询的命名参数,则抛出IllegalArgumentException 对应于查询的位置参数,或者参数的类型对查询无效。绑定参数时可能会引发此异常,否则查询的执行可能会失败。有关受支持的参数用法,请参见第3.10.11, 3.10.12和3.10.13节。
  • 将setMaxResults或setFirstResult应用于涉及对集合进行提取连接的查询的效果是不确定的,存储过程查询不支持使用setMaxResults和setFirstResult.
  • 除非在查询中指定了除LockModeType.NONE之外的其他锁定模式,否则不需要在事务上下文中调用executeExecute方法之外的Query和TypedQuery方法。特别是,不需要在事务上下文中调用getResultList和getSingleResult方法,除非已为查询指定了这种锁定模式。如果使用具有事务范围的持久性上下文的实体管理器,则将分离结果实体;否则,将分离结果实体。如果使用具有扩展持久性上下文的实体管理器,则将对其进行管理。有关在事务和特性上下文类型之外使用实体管理器的进一步讨论,请参见第7章。
  • 是否应在事务上下文中调用StoredPorcedureQuery应该由事务语义和/或存储过程实现和正在使用数据的要求来确定。特别是,如果存储过程启动事务并且事务已经生效,则可能会出现问题。如上所述,确定存储过程查询调用返回的任何实体的状态。
  • Query, TypedQuery和StoredProcedureQuery接口的方法(以下指定的方法除外)抛出的NoResultException, NonUniqueResultException, QueryTimeoutException和LockTimeoutException 以外的运行时异常会导致如果将持久性上下文连接到当前事务,则将当前事务标记为回滚事务。在查询超时导致事务回滚的数据库平台上,持久性提供程序必须抛出PersistenceException而不是QueryTimeoutException.
  • 下列Query, TypedQuery和Stored-ProcedureQuery接口方法引发的运行时异常不会导致当前事务表标记为回滚: getParameters, getParameter, getParameterValue, getOutputParameterValue, getLockMode.
  • 由Tuple, TupleElement和Parameter接口的方法引发的运行时异常不会导致当前事务被标记为回滚。

3.10.7.1、例子

public List findWithName(String name){
  return em.createQuery(
  "SELECT c FROM Customer c WHERE c.name LIKE :custName")
    .setParameter("custName", name)
    .setMaxResults(10)
    .getResultList();
}
           

3.10.8、查询和Flush模式

  • flush模式设置会影响查询结果,如下所示
  • 在事务内执行查询时,如果在Query,TypedQuery或StoredProcedureQuery对象上设置了FlushModeType.AUTO, 或者持久性上下文的flush模式设置为AUTO(默认值),并且尚未为对于查询对象,持久性提供者负责确保对持久性上下文中所有实体的状态的所有更新(可能会影响查询结果)对于查询的处理都是可见的,持久性提供程序实现可以通过将这些实体flush到数据库或通过其他某种方式来实现。如果设置了FlushModeType.COMMIT, 则未指定在持久性上下文中对实体进行的更新对查询的影响。
  • 如果持久性上下文尚未加入当前事务,则无论flush模式设置如何,持久性提供程序都不得刷新到数据库。
  • package javax.persistence;
    
    public enum FlushModeType{
      COMMIT,
      AUTO
    }
               
  • 如果没有活动的事务,则持久性提供程序不得flush到数据库。

3.10.查询和锁定模式

  • Query或TypedQuery接口的setLockMode方法或NamedQuery注解的lockMode元素可用于锁定查询结果。将未查询结果中指定的每个实体(包括传递给查询SELECT子句中的构造函数的实体)获取一个锁。
  • 如果锁定模式类型为PESSIMISTIC_READ, PESSIMISTIC_WRITE或PESSIMISTIC_FORCE_INCREMENT, 并且查询返回标量数据(例如,实体字段或属性的值,包括传递给查询SELECT子句中的构造函数的标量数据),则基础数据库行将被锁定。但是除非对应第检索和更新了实体本身,否则不会更新与此类标量数据相对应的任何实体的版本列(如果有的话)
  • 如果锁定模式类型为OPTIMISTIC或OPTIMISTIC_FORCE_INCREMENT,并且查询返回标量数据,则查询返回的任何实体都将被锁定,但是与不对应于查询结果中任何实体实例状态的标量数据都不会发生锁定。
  • 如果为查询指定了NONE以外的锁定模式,则该查询必须在事务内执行(并且必须将持久性上下文连接到该事务),否则将抛出TransactionRequiredException.
  • 仅Java持久性查询语言查询和条件查询支持锁定,如果不在Java Persistence查询语言选择查询或条件查询的查询上调用setLockMode或getLockMode方法,则可能引发IllegalStateException或查询执行失败。

3.10.10、查询提示

  • 该规范定义了以下提示,以用于查询配置。
  • 该提示可以与Query, TypedQuery或StoredProcedureQuery setHint方法或NamedQuery, NamedNativeQuery 和NamedStoredProcedureQuery注解一起使用。也可以将它作为属性传递给Persistence.createEntityManagerFactory方法,并在persistence.xml文件的properties元素中使用。请参见第3.10.1、8.2.1.9、9.7、10.4节。当在createEntityManagerFactory方法,persistence.xml文件和注解中使用时,超时提示用作默认值,可以通过在setHint方法中使用来选择性地覆盖它。
  • 可移植应用程序不应依赖此提示。根据使用的持久性提供程序和数据库,可能会或可能不会观察到提示。
  • 运行供应商支持使用其他特定于供应商的提示。特定于供应商的提示不得使用javax.persistence命名空间。如果不理解供应商特定的提示,则必将将其忽略。

3.10.11、参数对象

  • 参数对象可用于条件查询和Java Persistence查询语言查询
  • 实现可能支持将Parameter对象用于本机查询,但是此规范不要求支持带本机查询的Parameter对象。将参数对象用于本机查询将不可移植。参数对象与命名参数或位置参数的混合是不确定的。
  • 可移植应用程序不应尝试在不同的Query或TypedQuery实例的上下文中重用从Query或TypedQuery 实例获得的PARAMETER对象。

3.10.12、命名参数

  • 命名参数可用于Java Persistence查询语言查询,标准查询(尽管最好使用PARAMETER对象)以及支持命名参数的存储过程查询。
  • 命名参数遵循第4.4.1节中定义的标识符规则。命名参数区分大小写。命名和位置参数的混合是不确定的。
  • Java Persistence查询语言查询的命名参数是一个以":“符合为前缀的标识符,传递给Query和TypedQuery接口的setParameter方法的参数名称不包含此”:"前缀

3.10.13、位置参数

  • 原生查询只能使用位置参数绑定和对结果项的位置访问,但已定义了命名参数的存储过程查询除外,当绑定位置参数的值时,编号从"1"开始。假定对于原生查询,参数本身使用SQL语法(即"?“而不是”?1")
  • 条件查询不支持使用位置参数

3.10.14 命名查询

  • 命名查询是用元数据表示的静态查询或通过EntityManager addNamedQuery方法注册的查询。可以使用Java Persistence查询语言或SQL定义命名查询。查询名称的作用域是持久性单元。
  • 以下是元数据中定义的命名查询的定义示例:
  • 以下是使用命名查询的示例
    • @PersistenceContext
      public EntityManager em;
      
      customers = em.createNamedQuery("findAllCustomersWithName")
        .setParameter("custName", "Smith")
        .getResultList();
                 

3.10.15、多态查询

  • 默认情况下,所有查询都是多态的,也就是说,查询的FROM子句不仅指定它明确引用的特定实体类的实例,还指定子类。查询返回的实体包括满足条件查询的子类的实例。
  • 例如,以下查询返回所有雇员的平均工资,包括Employee的子类型,例如Manager和Exempt
  • 在第4.6.17.5节中描述的实体类型表达式以及在第4.4.9节中描述的向下转换的用法可用于限制查询多态性。

3.10.16、SQL查询

  • 查询可以用于原生SQL表示,原生SQL查询的结果可能包含实体,通过构造函数创建的非托管实例。标量值或这些的某种组合。
    • SQL查询工具旨在为需要使用正在使用的目标数据库的原生SQL(或无法使用Java持久性查询语言)的情况提供支持。本地SQL查询不应跨数据库移植。

3.10.16.1、从原生查询返回托管实体

  • 持久性提供程序负责根据一个或多个实体的对象/关系映射元数据执行SQL查询返回的值与实体属性之间的映射。特别是,SQL结果中的列名用于映射到此元数据定义的实体属性。此映射包括作为非集合值实体状态一部分的任何可嵌入类的属性以及做为实体状态一部分包含的外键相对应的属性的映射。
  • 当要从元素查询返回实体时,SQL语句应选择所有映射到该实体对象的列,这应该包括相关实体的外键列。当没有足够的数据可用时获得的结果是不确定的。
  • 在简单的情况下(即,查询结果仅限于单个实体类的实体,并且映射信息可以从SQL结果和对象/关系映射元数据的列中得出),仅指定实体结果的预期类别。
  • 以下示例说明了使用createNativeQuery方法动态创建原生SQL查询并将指定结果类型的实体类作为参数传入的情况。
  • Query q = em.createNativeQuery(
    	"SELECT o.id, o.quantity, o.item " + 
      "FROM Order o, Item i " +
      "WHERE (o.item=i.id) AND (i.name = 'widget')",
      com.acme.Order.class
    );
    
               
  • 执行该查询时,将返回名为"widget"的条目的所有Order实体的集合
  • SqlResultSetMapping 元数据注解(旨在处理更复杂情况)可以在此处用作替代方法,有关SqlResultSetMapping元数据注解和相关注解的;定义,请参见10.4.4节。
  • 对于上面显示的查询,可以如下指定查询结果类型的SqlResultSetMapping元数据:
  • 然后,可以通过以下方式获得与上面的查询所产生的结果相同的结果:
    • Query q = em.createNativeQuery(
      	"SELECT o.id, o.quantity, o.item " + 
        "FROM Order o, Item i " +
        "WHERE (o.item=i.id) AND (i.name = 'widget')",
        "WidgetOrderResults"
      );
      
      
                 
  • 当SQL查询返回多个实体时,或SQL结果的列名与对象/关系映射元数据的列名不对应时,必须提供SqlResultSetMapping元数据定义以指定实体映射。
  • 以下查询和SqlResultSetMapping元数据说明了多个实体类型的返回。它假定默认元数据和列名为默认值。
  • Query q = em.createNativeQuery(
    "SELECT o.id, o.quantity, o.item, i.id, i.name, i.description "+
    "FROM Order o, Item i " + 
    "WHERE (o.quantity > 25) AND (o.item = i.id)", "OrderItemResults");
    
    @SqlResultSetMapping(name="OrderItemResults",
                        entities={
                          @EntityResult(entityClass=com.acme.Order.class),
                          @EntityResult(entityClass=com.acme.Item.class)
                        })
               
  • 当SQL结果的列名与对象/关系映射元数据的列名不对应时,必须提供更明确的SQL结果映射元数据,以使持久性提供程序运行时能够将JDBC结果映射到预期的对象中。例如,当必须在SQL SELECT子句中使用列别名时,否则SQL结果将包含多个相同名称的列,或者当SQL结果中的列是运算符或函数的结果时,可能会发生这种情况。EntityResult注解中的FieldResult注解元素用于指定此类列到实体属性的映射。
  • 以下示例结合了多种实体类型,该示例SQL语句中包含别名。这要求将列名显式映射到与这些列相对应的实体字段。为此,使用了FieldResult注解
  • Query q = em.createNativeQuery(
      "SELECT o.id As order_id, " +
       "o.quantity As order_quantity, " +
      "o.item As order_item," +
      "i.id, i.name, i.description " +
      "FROM Order o, Item i " +
      "WHERE (order_quantity > 25) and (order_item=i.id)", "OrderItemResults"
    );
    
    @SqlResultsSetMapping(name="OrderItemResults",
         entities={
           @EntityResult(entityClass=com.acme.Order.class, fields={
             @FieldResult(name="id", column="order_id"),
             @FieldResult(name="quantity", column="order_quantity"),
             @FieldResult(name="item", column="order_item")
           }),  @EntityResult(entityClass=com.acme.Item.class)
         }                
          )
               
  • 当返回的实体类型包含可嵌入的类时,FieldResult元素必须使用点(.)表示法来指示那一列映射到所包含的可嵌入对象的那个字段或属性。
  • 例子
  • Query q = em.createNativeQuery(
     "SELECT c.id As customer_id , " +
      "c.street AS customer_street, " +
      "c.city As customer_city,"+
      "c.state AS customer_state, "+
      "c.status AS customer_status "+
      "FROM Customer c " +
      "WHERE c.status = 'GOLD' ", "CustomerResults"
    );
    
    @SqlResultSetMapping(name="CustomerResults",
        entities={
          @EntityResult(entityClass=com.acme.Customer.class, 
              fields = {
                @FieldResult(name="id", column="customer_id"),
                @FieldResult(name="address.street", column="customer_street"),
                @FieldResult(name="address.city", column="customer_city"),
                @FieldResult(name="address.state", column="customer_state"),
                @FieldResult(name="status", column="customer_status"),
              })
        })
               
  • 当返回的实体类型是单值关系的所有者,而外键是复合外键(由多列组成)时,应为每个外键列使用FieldResult元素。FieldResult元素必须使用点(".")表示法来表示映射到目标实体主键的每个属性或字段的列。
  • 如果目标实体具有类型为IdClass的主键,则次规范采用关系的字段或属性名称的形式,后跟一个点("."),后跟该字段或属性的名称。目标实体中的主键,后者将按照11.1.22节中的Id进行注解。
  • 例子
  • Query q = em.createNativeQuery(
    	"SELECT o.id AS order_id, " + 
      "o.quantity AS order_quantity, " +
      "o.item_id AS order_item_id, " + 
      "o.item_name AS order_item_name, " +
      "i.id, i.name, i.description " +
      "FROM Order o, Item i " +
      "WHERE (order_quantity > 25) and (order_item_id = i.id) " +
      		"AND (order_item_name = i.name)", "OrderItemResults"
    );
    
    @SqlResultsMapping(name="OrderItemResults",
       entities={
         @EntityResult(entityClass=com.acme.Order.class, fields={
           @FieldResult(name="id", column="order_id"),
           @FieldResult(name="quantity", column="order_quantity"),
           @FieldResult(name="item.id", column="order_item_id")
         }), @EntityResult(entityClass=com.acme.Item.class)
       })
               
  • 如果目标实体具有类型为EmbeddedId 的主键,则此规范由关系的字段或属性的名称组成且后面跟点("."),后跟主对象的名称或字段或属性键(即,字段或属性的名称标记为EmbeddedId) ,然后是嵌入式主键类的相对应字段或属性的名称。
  • 例子:
  • Query q = em.createNativeQuery(
    	"SELECT o.id AS order_id, " +
      "o.quantity AS order_quantity, "+
      "o.item_id AS order_item_id, "+
      "o.item_name AS order_item_name, " +
      "i.id, i.name, i.description " +
      "FROM Order o, Item i " +
      "WHERE (order_quantity > 25) and (order_item_id = i.id) AND (order_item_name=i.name)", "OrderItemResults"
    );
    
    @SqlResultSetMapping(name="OrderItemResults",
       entities={
       @EntityResult(entityClass=com.acme.Order.class, fields={
         @FieldResult(name="id", column="order_id"),
         @FieldResult(name="quantity", column="order_quantity"),
    		 @FieldResult(name="item.itemPk.id", column="order_item_id"),
         @FieldResult(name="item.itemPk.name", column="order_item_name"),
    }), @EntityResult(entityClass=com.acme.Item.class)})
               
  • 组合外键的FieldResult元素被组合以形成目标实体的主键EmbeddedId类,然后,如果要热切地加载该关系,则可以使用它随后检索实体。
  • 除可嵌入对象,复合外键或符合主键外,任何用途均不要支持点符号格式。

3.10.16.2、返回非托管实体

  • 原生查询可以返回其他类的实例(包括非托管实体实例)以及标量结果。这些可以单独使用,也可以与实体结果结合使用。
3.10.16.2.1、标量结果
  • 通过指定SqlResultSetMapping注解的ColumnResult注解元素,可以将标量结果包括在查询结果中。可以使用ColumnResult注解的type元素指定的结果的预期类型
  • Query q = em.createNativeQuery(
    "SELECT o.id AS order_id, " +
      "o.quantity AS order_quantity, " + 
      "o.item AS order_item, " +
      "i.name AS item_name, " +
      "i.availabilityDate AS item_shipdate " +
      "FROM Order o, Item i " + 
      "WHERE (order_quantity > 25) AND (order_item=i.id)", "OrderResults"
    
    );
    @SqlResultSetMapping(name="OrderResults",
     entities={
       @EntityResult(entityClass=com.acme.Order.class, fields={
         @FieldResult(name="id", column="order_id"),
         @FieldResult(name="quantity", column="order_quantity"),
         @FieldResult(name="item", column="order_item")
       })
     },
       columns={
         @ColumnResult(name="item_name"),
         @ColumnResult(name="item_shipdate"), type=java.util.Date.class
       }
                        )
               
3.10.16.2.2、构造结果
  • 使用SqlResultSetMapping注解的ConstructorResult注解元素指定到构造函数的映射。ConstructorResult注解的targetClass元素指定其构造函数对应于指定列的类。必须使用ConstructorResult注解中的columns元素以与构造函数的参数列表相同的顺序指定与预期构造函数的参数对应的所有列。作为构造函数结果返回的所有实体都将处于新的状态或分离状态,具体取决于是否为构造对象检索了主键。
  • 例子
    • Query q = em.createNativeQuery(
      	"SELECT c.id,  c.name, COUNT(0) as orderCount, AVG(o.price) As avgOrder " +
        "FROM Customer c, Order o " +
        "WHERE o.cid =c.id " +
        "GROUP BY c.id, c.name",
        "CustomerDetailsResult"
      );
      @SqlResultSetMapping(name="CustomerDetailsResult", 
       classes = {
         @ConstructorResult(targetClass=com.acme.CustomerDetails.class,
              columns={
                @ColumnResult(name="id"),
                @ColumnResult(name="name"),
                @ColumnResult(name="orderCount"),
                @ColumnResult(name="avgOrder", type=Double.class)
              })
       })
                 

3.10.16.3、合并结果类型

  • 当SQLResultSetMapping 指定一种以上的映射类型(即,EntityResult, ConstructorResult, ColumnResult中的一种以上)时,对于SQL结果中的每一行,查询执行将导致Object[] 实例,其元素如下所示:顺序:任何实体结果(按照在实体元素中定义的顺序)对应于构造函数结果的类型的任何实例(按照classes元素中定义的顺序);以及对应于列结果的所有实例(按照columns元素中定义的顺序)。如果有任何未指定结果映射的列,则将其忽略。

3.10.16.4、限制

  • 返回实体时,SQL语句应选择所有映射到实体对象的列。这应该包括相关实体的外键列,当没有足够的数据可用时获得的结果是不确定的,SQL结果集映射不得用于将结果映射到实体的非持久状态。
  • 未为原生SQL查询定义使用命名参数。便携式应用程序只能使用SQL查询的位置参数绑定。

3.10.17 存储过程

  • StoredProcedureQuery接口支持使用数据库存储过程。
  • 可以通过NamedStoredProcedureQuery注解或动态指定存储过程,有关存储过程规范的注解,请参见第10.4.3节。

3.10.17.1、命名式存储过程查询

  • 与命名原生查询的情况不同,NamedStoredProcedureQuery注解为数据中存在的存储过程命名,而不是提供存储过程定义。NamedStoredProcedureQuery注解指定存储过程的所有参数的类型,其对应的参数模式(IN,OUT,INOUT,REF_CURSOR) 以及如何映射结果集(如果有),在NamedStoredProcedureQuery注解中分配给存储过程的名称作为参数传递给createNamedStoredProcedureQuery方法,以创建可执行的StoredProcedureQuery对象。
  • 存储过程可能返回多个结果集,与原生查询一样,可以使用resultClasses或resultSetMappings注解元素来指定结果集的映射。如果存在多个结果集,则假定将使用相同的机制进行映射-例如,全部通过一组结果类映射或全部通过一组结果集映射。这些映射的指定顺序必须与存储过程返回一个或多个结果集,并且未指定resultClasses或resultSetMappings元素,则任何结果集都将作为Object[]类型的列表返回。未定义用于映射存储过程结果集的不同策略的组合。
  • 需要为所有参数提供StoredProcedureParameter元数据。必须按照在存储过程的戏剧社列表中出现顺序指定参数。如果使用参数名称,则使用参数名称绑定参数值并提取输出值(如果参数是INOUT或OUT参数)。如果为指定参数名称,则假定使用位置参数。命名和位置参数的混合是不确定的。

3.10.17.2、动态指定存储过程

  • 如果未使用元数据定义存储过程,则必须动态提供参数和结果集信息。
  • 动态指定的存储过程查询的所有参数都必须使用StoredProcedureQuery接口的registerStoredProcedureParameter方法进行注册。
  • 可以通过createStoredProcedureQuery方法提供结果集映射信息

3.10.17.3、存储过程的执行

  • 可以如下所述控制存储过程查询的执行。
  • setParameter方法用于设置所有必须的IN和INOUT参数的值,不需要设设置已由存储过程定义了默认的存储过程参数的值。
  • 在StoredProcedureQuery对象上调用getResultList和getSingleResult时,持久性提供程序将在处理getResultList或getSingleResult之前,对未执行的存储过程查询调用execute方法。
  • 在StoredProcedureQuery对象上调用executeUpdate时,持久性提供程序将在未执行的存储过程查询上调用execute,然后在调用getUpdateCount. executeUpdate的结果将是getUpdateCount的结果。
  • execute方法即支持仅通过INOUT和OUT参数传递标量结果的简单情况,也支持最一般的情况(多个结果集或更新计数。还可能与输出参数值结合使用)
  • 如果第一个结果是结果集,则execute方法返回true;如果它是更新计数,或者除了通过INOUT和OUT参数(如果有的话)之外没有其他结果,则返回false。
  • 如果execute方法返回true,则可以通过调用getResultList或getSingleResult获得待处理的结果集。然后,可以使用hasMoreResults方法测试其他结果。
  • 如果execute或hasMoreResults返回false,则如果是更新计数,则可以调用getUpdateCount方法以获取挂起的结果。如果没有更新计数(即下一个结果是结果集或没有下一个更新计数),则getUpdateCount方法将返回更新计数(0或更大)或-1.
  • 为了实现可移植性,在提取任何INOUT或OUT参数的值之前,需要处理与JDBC结果集合更新计数相对应的结果。
  • 通过getResultList和getUpdateCount返回的结果用完后,可以检索通过INOUT和OUT参数返回的结果
  • getOutputParameterValue方法用于检索通过INOUT和OUT参数从过程传递会的值。
  • 将REF_CURSOR参数用于结果集时,应在调用getResultList检索结果集之前用完更新计数,或者,可以通过getOutputParameterValue检索REF_CURSOR结果集。结果集映射将以与REF-CURSOR参数一起在查询中注册的顺序应用于与REF_CURSOR参数相对应的结果。
  • 在简单的情况下, 仅通过INOUT和OUT参数返回结果,执行之后可以立即调用getOutputParameterValue。

3.11、异常总结

  • 以下是此规范定义的异常的摘要:
  • PersistenceException
    • 发生问题时,持久性提供程序将引发PersistenceException.可能会报告由于意外错误(例如,持久性提供程序无法打开数据库连接)而导致调用的操作无法完成。
    • 本规范定义的所有其他异常是PersistenceException的子类,除了NoResultException, NonUniqueResultException, LockTimeoutException 和QueryTimeoutException 实例之外,所有PersistenceException实例都将导致当前事务(如果一个事务处于活动状态并且已将持久性上下文加入其中)被标记为回滚。
  • TransactionRequiredException
    • 持久性提供程序在以下情况下引发TransactionRequiredException事务是必要的,但是未激活
  • OptimisticLockException
    • 当发生乐观锁定冲突时,持久性提供程序将引发OptimisticLockException。可能在刷新或提交时将异常作为API调用的一部分抛出。如果当前事务处于活动状态,则将其标记为回滚。
  • PessimisticLockException
    • 当发生持久性锁定冲突时,持久性提供程序将引发PessimisticLockException.当前事务将被标记为回滚。通常,发生PessimisticLockException的原因是由于死锁导致数据库事务已回滚,或者在无法授予悲观锁的情况下数据库使用事务级别回滚。
  • LockTimeoutException
    • 当发生悲观的锁定冲突的而不会导致事务回滚时,持久性提供程序将引发LocktimeoutException.通常会发生这种情况,因为无法授予悲观锁(并且没有死锁)时,数据库使用语句级回滚,LockTimoutException不会导致将当前事务标记为回滚
  • RollbackExcpetion
    • 当EntityTransaction.commit 失败的时候
  • EntityExistsException
    • 当持久性调用被调用并且实体已经存在时,持久性提供者可能会抛出EntityExistsException。调用持久性操作时,可能会抛出EntityExistsException异常,或者在提交时可能会抛出EntityExistsException异常或另一个PersistenceException异常。如果当前事务处于活跃状态,并且持久性上下文已加入其中,则将其标记为回滚。
  • EntityNotFoundException
    • 当一个实体时,持久性提供者抛出EntityNotFoundException访问了由getReference获取的引用,但该实体不存在,它被抛出当实体不再存在于数据库中时,通过刷新操作进行操作。当使用悲观锁定并且该实体在数据库中不在存在时,锁定操作也会引发该错误。如果当前事务处于活动状态,并且持久性上下文已加入其中,则将其标记为回滚。
  • NoResultException
    • 调用Query.getSingleResult或TypedQuery.getSingleResult时,持久性提供程序引发NoResultException,并且没有结果要返回,如果当前事务处于活动状态,则此异常不会导致将当前事务标记为回滚。
  • NonUniqueResultException
    • 调用Query.getSingleResult或TypedQuery.getSingleResult时,持久性提供程序抛出NonUniqueResultException,并且该查询有多个结果。如果当前事务处于活动状态,则次异常不会导致当前事务标记为回滚。
  • QueryTimeoutException
    • 当查询超时并且仅回滚该语句时,持久性提供程序将引发QueryTimeoutException。QueryTimeoutException不会导致当前事务(如果一个事务处于活动状态)被标记为回滚。

继续阅读