江西省住房保障建设厅网站,枣庄市网站建设,柳州网站制作服务商,如果自己做网站Hibernate EntityManager 专题
参考#xff1a;
JPA – EntityManager常用API详解EntityManager基本概念
基本概念及获得 EntityManager 对象
基本概念
在使用持久化工具的时候#xff0c;一般都有一个对象来操作数据库#xff0c;在原生的Hibernate中叫做Session…Hibernate EntityManager 专题
参考
JPA – EntityManager常用API详解EntityManager基本概念
基本概念及获得 EntityManager 对象
基本概念
在使用持久化工具的时候一般都有一个对象来操作数据库在原生的Hibernate中叫做Session在 JPA 中叫做EntityManager在MyBatis中叫做SqlSession通过这个对象来操作数据库。
EntityManager是 JPA 中用于增删改查的接口连接内存中的 java 对象和数据库的数据存储。Hibernate EntityManager是围绕提供JPA编程接口实现的Hibernate Core的一个包装支持JPA实体实例的生命周期并允许用标准的Java Persistence查询语言编写查询。
EntityManager称为实体管理器它由EntityManagerFactory所创建。EntityManagerFactory作为EntityManager的工厂包含有当前O-R映射的元数据信息每个EntityManagerFactory可称为一个持久化单元PersistenceUnit每个持久化单元可认为是一个数据源的映射所谓数据源可理解为一个数据库可以在应用服务器中配置多个数据源同时使用不同的PersistenceUnit来映射这些数据源从而能够很方便的实现跨越多个数据库之间的事务操作
PersistenceContext称为持久化上下文它一般包含有当前事务范围内的被管理的实体对象(Entity)的数据。每个EntityManager都会跟一个PersistenceContext相关联。PersistenceContext中存储的是实体对象的数据而关系数据库中存储的是记录EntityManager正是维护这种OR映射的中间者它可以把数据从数据库中加载到PersistenceContext中也可以把数据从PersistenceContext中持久化到数据库EntityManager通过Persist、merge、remove、refresh、flush等操作来操纵PersistenceContext与数据库数据之间的同步
EntityManager是应用程序操纵持久化数据的接口。它的作用与hibernate session类似。为了能够在一个请求周期中使用同一个session对象在hibernate的解决方案中提出了currentSession的概念hibernate中的current session可以跟JTA事务绑定也可以跟当前线程绑定。在hibernate中session管理着所有的持久化对象的数据。而在EJB3中EntityManager管理着PersistenceContextPersistenceContext正是被管理的持久化对象的集合。
在 Java EE 环境下一个 JTA 事务通常会横跨多个组件的调用比如多个 EJB 组件的方法调用。这些组件需要能够在单个事务范围内访问到同样的PersistenceContext。为了满足这种情况的需要当EntityManager被注入或通过 JNDI 被查询的时候它的 PersistenceContext 将会在当前事务范围内自动传播引用到同一个 Persistence unit 的EntityManager将使用同样的 PersistenceContext。这可以避免在不同的组件之间传递EntityManager引用。
通过容器来传递PersistenceContext而不是应用程序自己来传递EntityManager。这种方式由容器管理着PersistenceContext并负责传递到不同的EntityManager称为容器管理的实体管理器Container-Managed EntityManager它的生命周期由容器负责管理编程人员不需要考虑EntityManger的连接释放以及复杂的事务问题等。
有一种不常见的情况是应用程序自身需要独立访问PersistenceContext。即每次创建一个EntityManager都会迫使创建一个新的PersistenceContext。这些PersistenceContext即使在同一个事务范围内也不会跟其它EntityManager共享这个创建过程可以由EntityManagerFactory的createEntityManager方法来创建。这被称为应用管理的实体管理器application-managed entity manager。
获得EntityManager对象
常用方式SpringBoot容器托管对象方式
依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId
/dependency对象注入
Autowired
private EntityManager entityManager;实体状态和转换 实体状态详解 临时状态 实际上就是new了一个普通的 JavaBean 对象。 托管状态 临时状态在调用 persist() 后即可将一般的 JavaBean 做为了托管状态的Bean该Bean的任何属性改动都会牵涉到数据库记录的改动。 一旦该记录flush到数据库之后并且事务提交了那么此对象不在持久化上下文中即变为了游离没人管的孩子状态了。 在游离状态的时候调用更新、刷新方法后游离状态对象就变为了在持久化上下文的托管状态了。 通过管理器的find方法将实体从数据库查询出来后该实体也就变为了托管形态。 持久化状态 当处在托管状态的实体Bean被管理器flush了那么就在极短暂的时间进入了持久化状态事务提交之后立刻变为了游离状态。可以把持久化状态当做实实在在的数据库记录。 游离状态 游离状态就是提交到数据库后事务commit后实体的状态因为事务已经提交了此时实体的属性任你如何改变也不会同步到数据库因为游离是没人管的孩子不在持久化上下文中。 销毁对象 一般要删除一个持久化对象的时候都是先find出来之后调用remove方法删之此时这个对象就是销毁对象实际上就是瞬时对象的另一种形态罢了。 常用的 API
SELECT、DELETE
SELECT
Ø find() 返回指定的 OID 对应的实体类对象如果这个实体存在于当前的持久化环境则返回一个被缓存的对象否则会创建一个新的 Entity, 并加载数据库中相关信息若 OID 不存在于数据库中则返回一个 null。
Ø getReference()
T T find(ClassT entityClass, Object primaryKey);
T T find(ClassT entityClass, Object primaryKey, MapString, Object var3);
T T find(ClassT entityClass, Object primaryKey, LockModeType var3);
T T find(ClassT entityClass, Object primaryKey, LockModeType var3, MapString, Object var4);T T getReference(ClassT entityClass, Object primaryKey);// 参数说明entityClass // 被查询的实体类类型primaryKey // 待查找实体的主键值异同 当在数据库中没有找到记录时find()方法会返回null 而getReference() 方法会抛出javax.persistence.EntityNotFoundException异常 调用getReference()方法返回的其实并不是实例对象而是一个代理。当你要使用实体时才会真正的调用查询语句来查询实例对象 另外getReference()方法不保证 entity Bean已被初始化。 如果传递进getReference()或find()方法的参数不是实体Bean都会引发 IllegalArgumentException DELETE
Ø Remove()
void remove(Object entity);如果级联关系cascadeCascadeType.ALL在删除person 时候也会把级联对象删除。把cascade属性设为cascadeCascadeType.REMOVE 有同样的效果。 Person person em.find(Person.class, 2);
em.remove (person);如果传递进remove ()方法的参数不是实体Bean会引发一个IllegalArgumentException remove()方法不能移除游离对象只能移除持久化对象 Order order new Order();
order.setId(140);
entityManager.remove(order);上面这段代码会抛出异常因为order是自己创建的对象也就是游离对象。必须这样写 Order order new Order();
order entityManager.find(Order.class140);
entityManager.remove(order);这段代码中的order是从数据库中获取的也就是持久化对象 hibernate的delete()方法只要对象有Id就可以删除 INSERT、UPDATE
INSERT
Ø persist() 将临时状态的实体持久化到数据库
void persist(Object entity);persist方法使对象由临时状态变为托管状态。进而变为持久化状态就是执行INSERT操作。
如果传递进persist()方法的参数不是实体Bean会引发IllegalArgumentException和hibernate的save()方法有些不同当Entity实体类中设置了主键自动生成时如果传入对象有id值则会抛出异常
特殊场景及处理方案 特殊场景Entity上使用自动生成ID值但有些又需要插入的主键值是指定的ID值而非自动生成的ID值 处理方案1Bean主键为Nullpersist()后自动生成ID值然后再使用QueryDsl-Jpa的update()方法根据自动生成的ID值为条件更新新增的实体的ID值为指定值 Test
Transactional
Rollback(false)
public void addByEntityManager(){User bean User.builder().addressee(孙六).build();entityManager.persist(u1);QUser entity QUser.user;long execute queryFactory.update(entity).set(entity.id, 11111).where(entity.id.eq(bean.getId())).execute();entityManager.flush();entityManager.clear();
}处理方案2使用Querydsl-SQL中SqlQueryFactory.insert()方法。 UPDATE
Ø 当实体正在被容器管理即托管状态你可以调用实体的set方法对数据进行修改在容器决定flush时这个由Container自行判断更新的数据才会同步到数据库而不是在调用了set方法对数据进行修改后马上同步到数据库。如果希望修改后的数据马上同步到数据库可以调用 EntityManager.flush() 方法。
// 使用示例
tranational
publicvoid updatePerson() {Person person entityManager.find(Person.class, 1);person.setName(lihuoming); //方法执行完后即可更新数据entityManager.merge(person);
}Ø Merge
T T merge(T entity);传入的对象没有id 在这种情况下调用merge方法将返回一个新的对象有id并对这个新的对象执行insert操作。 传入的对象有identityManager的缓存中没有该对象数据库中没有该记录 在这种情况下调用merge方法将返回一个新的对象并对该对象执行insert操作。 注意如果Entity的主键设置的是自动生成则新对象的id并不是原传入对象的id而是自动生成的比如自增长的id。其实和情况1的结果是一样的 传入的对象有identityManager的缓存没有该对象数据库中有该记录 在这种情况下调用merge方法将会从数据库中查询对应的记录生成新的对象然后将传入的对象复制到新的对象最后执行update操作。 简单来说就是更新操作。 传入的对象有identityManager的缓存有该对象 在这种情况下调用merge方法JPA会把传入的对象赋值到entityManager的缓存中的对象然后对entityManager缓存中的对象执行update操作。和情况3的结果一样
总结执行merge时如果实体ID为空则进行insert操作。 如果有ID则进行update操作。 flush()、clear()
flush()
将实体的改变立刻刷新到数据库中
当EntityManager对象在一个session bean 中使用时它是和服务器的事务上下文绑定的。EntityManager 在服务器的事务提交时提交并且同步它的内容。
在一个session bean 中服务器的事务默认地会在调用堆栈的最后提交如方法的返回。
// 例子1在方法返回时才提交事务
public void updatePerson(Person person) {try {Person person em.find(Person.class, 2);person.setName(lihuoming);em.merge(person);//后面还有众多修改操作} catch (Exception e) {e.printStackTrace();}//更新将会在这个方法的末尾被提交和刷新到数据库中
}默认只在当事务提交时才将改变更新到数据库中容器将所有数据库操作集中到一个批处理中这样就减少了代价昂贵的与数据库的交互。
当调用 persist( )merge( )或 remove( ) 这些方法时更新并不会立刻同步到数据库中直到容器决定刷新到数据库中时才会执行默认情况下容器决定刷新是在 “相关查询” 执行前或事务提交时发生。
当然 “相关查询” 除 find() 和 getreference() 之外这两个方法是不会引起容器触发刷新动作的默认的刷新模式是可以改变的。 如果你需要在事务提交之前将更新刷新到数据库中你可以直接地调用EntityManager.flush()方法。
ORM框架执行的一些更新数据库的方法其实质是在更新缓存只有调用了 flush() 后才会将缓存同步到数据库即真正执行SQL语句但是这时并没有真正将数据保存进数据库需要事务commit后才能全部保存。一般 flush 后立刻就会进行事务的提交。
public void updatePerson(Person person) {try {Person person em.find(Person.class, 2);person.setName(lihuoming);em.merge(person);em.flush();//手动将更新立刻刷新进数据库//后面还有众多修改操作} catch (Exception e) {e.printStackTrace();}
}clear()
分离所有当前正在被管理的实体
1.清除持久上下文环境断开所有关联的实体。如果这时还有未提交的更新则会被撤消。
2.在处理大量实体的时候如果你不把已经处理过的实体从EntityManager中分离出来将会消耗你大量的内存。
3.调用EntityManager 的clear()方法后所有正在被管理的实体将会从持久化内容中分离出来。
4.有一点需要说明下在事务没有提交前事务默认在调用堆栈的最后提交如方法的返回如果调用clear()方法之前对实体所作的任何改变将会丢失所以建议在调用clear()方法之前先调用flush()方法保存更改。 JcreateQuery() – PQL
创建查询对象
除了使用 find() 或 getReference() 方法来获得Entity Bean之外你还可以通过 JPQL 得到实体Bean。要执行 JPQL 语句你必须通过 EntityManager 的 createQuery() 或 createNamedQuery() 方法创建一个Query 对象。
注JPQL 没有插入语句。即不能执行insert语句。 Ø getResultList()
Query query em.createQuery(select p from Person p where p.name’黎明’);
List result query.getResultList();Iterator iterator result.iterator();
while( iterator.hasNext() ){
//处理Person
}Ø getSingleResult()
返回查询的第一条数据可以进行强转如:
// 查询数目:
Query query em.createQuery(select count(1) from Person p);
Long num (Long)query. getSingleResult ();// 强转为实体:
Query query em.createQuery(select p from Person p);
User user (User)query. getSingleResult ();Ø executeUpdate()
// 执行更新和删除操作返回受影响的记录数。
Query query em.createQuery(delete from Person);
int result query.executeUpdate(); //影响的记录数Ø 关于 **JPQL 和SQL **中参数的问题 使用标识符Query query em.createQuery(delete from Person p where p.name : name);
query.setParameter(name,张三);
int result query.executeUpdate(); //影响的记录数使用索引下标Query query em.createQuery(delete from Person p where p.id ?1 );
query.setParameter(1, 张三);
int result query.executeUpdate(); //影响的记录数 createNaiveQuery() – SQL
用法基本同createQuery()只不过这里使用的不是 JPQL 而是SQL
Ø 将查询到的数据映射成实体
Query query em.createNativeQuery(select * from person, Person.class);
List result query.getResultList();if (result ! null){Iterator iterator result.iterator();while( iterator.hasNext() ){Person person (Person)iterator.next();… }
}refresh()
如果怀疑当前被管理的实体已经不是数据库中最新的数据则可以通过 refresh() 方法刷新实体容器会把数据库中的新值重写进实体。这种情况一般发生在获取了实体之后有人更新了数据库中的记录这时需要得到最新的数据。当然再次调用 find() 或 getReference() 方法也可以得到最新数据但这种做法并不优雅。
User user em.find(User.class, 1);//第二次同样的查询不会访问数据库
user em.find(User.class, 1);// 运行以上代码发现调用了两次find但是只执行了一次select语句这是缓存导致的。// 执行refresh()方法刷新缓存容器会把数据库中的新值重写进实体。
User user em.find(User.class, 1);
em.refresh(user);其他方法
contains()
判断实体是否还在EntityManage的管理下或者说是否属于当前持久上下文环境。
contains() 方法使用一个实体作为参数如果这个实体对象当前正被持久化内容管理返回值为true否则为false。
如果传递的参数不是实体 Bean将会引发一个IllegalArgumentException.
User user em.find(User.class, 1);if (em.contains(user)){//正在被持久化内容管理
}else{//已经不受持久化内容管理
}getFlushMode ()
获取持久上下文环境的Flush模式。返回FlushModeType类的枚举值。 setFlushMode()
改变实体管理器的Flush模式
setFlushMode()的Flush模式有2种类型AUTO 和 COMMIT。AUTO为默认模式。
// 改变实体管理器的Flush模式
em.setFlushMode(FlushModeType.COMMIT);FlushModeType.AUTO 默认情况下除了在事务提交时 flush在进行查询时除了 find() 和 getreference() 查询也会进行一次 flush 比如使用JPQL查询前会进行一次flush。 使用场合在大量更新数据的过程中没有任何查询语句除了 find() 和 getreference() 查询的执行。 FlushModeType.COMMIT 刷新只有在事务提交时才发生查询不触发。 使用场合在大量更新数据的过程中存在查询语句除了find() 和 getreference() 查询的执行。
其实上面两种模式最终反映的结果是JDBC 驱动跟数据库交互的次数。
JDBC 性能最大的增进是减少JDBC 驱动与数据库之间的网络通讯。
FlushModeType.COMMIT 模式使更新只在一次的网络交互中完成而 FlushModeType.AUTO 模式可能需要多次交互触发了多少次Flush 就产生了多少次网络交互 isOpen()
判断当前的实体管理器是否是打开状态 close()
关闭实体管理器。
之后若调用实体管理器实例的方法或其派生的查询对象的方法都将抛出 IllegalstateException 异常除了 getTransaction 和 isOpen方法(返回 false)。
不过当与实体管理器关联的事务处于活动状态时调用 close() 方法后持久上下文将仍处于被管理状态直到事务完成。 getTransaction()
返回资源层的事务对象。EntityTransaction实例可以用于开始和提交多个事务 EntityTransaction 接口用来管理资源层实体管理器的事务操作
通过调用实体管理器的getTransaction方法 获得其实例。
① begin ()
用于启动一个事务此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛出 IllegalStateException 异常。
② commit ()
用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。
③ rollback ()
撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作从而不对数据库产生影响。
④ setRollbackOnly ()
使当前事务只能被撤消。
⑤ getRollbackOnly ()
查看当前事务是否设置了只能撤消标志。
⑥ isActive ()
查看当前事务是否是活动的。如果返回true则不能调用begin方法否则将抛出 IllegalStateException 异常如果返回 false 则不能调用 commit、rollback、setRollbackOnly 及 getRollbackOnly 方法否则将抛出 IllegalStateException 异常。
需要注意的是
// 在JPA里面先需要 getTransaction再 begin
EntityTransaction transaction entityManager.getTransaction();
transaction.begin();// 在 hibernate 里面呢直接 begin然后进行 commit
EntityTransaction transaction session.beginTransaction();
transaction.commit();JPA 调用存储过程
参考https://www.cnblogs.com/zhuang229/p/12227938.html
定义存储过程及调用方式
定义一个简单的存储过程
传入一个int参数返回这个参数1
CREATE DEFINERrootlocalhost PROCEDURE plus1inout(IN ARG INT, OUT res INT)
BEGIN SET res ARG 1;
END注意 IN参数个数没有限制。 如果out参数类型为sys_refcursor那么最好只定义这 一个out参数JPA API限制 sys_refcursor 类型的 out 参数在 JPA 中统一注册为 Void.class 类型参数模式定义为 ParameterMode.REF_CURSOR 使用getResultList()方法获取游标fetch到的多行数据返回结果为ListObject[]一个Object[]对应一行数据。 如果使用Oracle 存储包只需在定义存储过程名字时加个对应的package名前缀即可例如包名.存储过程名。 JPA调用存储过程的两种方式
基于Entity实体类在实体类上使用NamedStoredProcedureQuery注解需要数据库中有对应的表可自动映射结果集 EntityManager创建createNamedStoredProcedureQuery传参调用SpringDataJpa中Repository自定义方法传参调用 直接使用EntityManager进行自定义不需要数据库中有对应的表需要自己处理结果集 EntityManager创建StoredProcedureQuery对象注册 IN/OUT 参数模式 实体类使用注解声明存储过程
NamedStoredProcedureQuery注解解析详见注解目录 name为唯一名称调用时使用procedureName 为存储过程名 Entity注解必须有 Entity要求必须有主键id属性存储过程可返回id任意值即可 Entity要求必须对应数据库表必须存在JPA表检查用若用EntityManager调用存储过程此条存疑
import lombok.Data;
import javax.persistence.*;Data
Entity
// 存储过程使用了注解NamedStoredProcedureQuery并绑定到一个随意一个JPA表
NamedStoredProcedureQueries({NamedStoredProcedureQuery(name User.plus1, procedureName plus1inout, parameters {StoredProcedureParameter(mode ParameterMode.IN, name arg, type Integer.class),StoredProcedureParameter(mode ParameterMode.OUT, name res, type Integer.class) }),NamedStoredProcedureQuery(name User.mytest, procedureName mytest) })
class user{Idprivate Integer id;private String name;private String age;
}EntityManager直接调用存储过程
Autowired
private EntityManager em;Test
public void test01(){StoredProcedureQuery query em.createStoredProcedureQuery(plus1inout) // 创建StoredProcedureQuery对象传入被调用的存储过程名称.registerStoredProcedureParameter(ARG, Integer.class, ParameterMode.IN) // 注册参数.registerStoredProcedureParameter(res, Integer.class, ParameterMode.OUT).setParameter(ARG, 20); // 传参query.execute(); // 执行存储过程调用String result query.getOutputParameterValue(res).toString(); // 获取存储过程中的返回值System.out.println(result);
}调用基于实体类注解的存储过程
使用EntityManager调用基于Entity实体类注解的存储过程。实体类详见 JPA调用存储过程
Autowired
private EntityManager em;Test
public void test02(){StoredProcedureQuery query em// 创建NamedStoredProcedureQuery对象传入实体类上NamedStoredProcedureQuery注解中name的值.createNamedStoredProcedureQuery(proKQAttendanceRecord)// IN模式的参数可以在实体类上注解此处相应注释掉// .registerStoredProcedureParameter(PRM_ID, Integer.class, ParameterMode.IN)// 使用EntityManager调用基于Entity实体类注解的存储过程时OUT或INOUT模式的参数必须要在此处注册并删掉原实体类上相应的参数注解。不然运行报错。.registerStoredProcedureParameter(PRM_APPCODE, Integer.class, ParameterMode.OUT).registerStoredProcedureParameter(PRM_ERRMSGE, Integer.class, ParameterMode.OUT).setParameter(PRM_ID, attId); // 传参query.execute();List resultList query.getResultList();Object code query.getOutputParameterValue(PRM_APPCODE);Object msg query.getOutputParameterValue(PRM_ERRMSGE);
}