JPA_EntityManager(一)
一、Persistence 和EntityManagerFactory
1、JPA相关接口/类:Persistence
Persistence 类是用于获取 EntityManagerFactory 实例。该类包含一个名为 createEntityManagerFactory 的 静态方法 。
EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory(persistenceUnitName);
createEntityManagerFactory 方法有如下两个重载版本。
①、带有一个参数的方法以 JPA 配置文件 persistence.xml 中的持久化单元名为参数 ====》persistenceUnitName 持久化单元名 【与spring data 做集成的时候这个方法不使用】
了解 :②、带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型,用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。
Main方法中更改,不打印SQL语句
结果: 没有SQL语句的输出
若改为true :
map.put("hibernate.show_sql", true);
运行:有SQL语句的输出
2、EntityManagerFactory
EntityManagerFactory 接口主要用来创建 EntityManager 实例。该接口约定了如下4个方法:
①、createEntityManager():用于创建实体管理器对象实例。
②、createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
③、isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
④、close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。
二、EntityManager的四个方法
1、find
find (Class<T> entityClass,Object primaryKey):返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的 Entity, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
①、customer 实体类 加上toString的方法
@Entity
@Table(name="jpa_customer")
public class Customer {
private Integer id;
private String lastName;
private String email;
private int age;
private Date creatTime;
private Date Birth;
//添加customer 的toString 方法
@Override
public String toString() {
return "Customer [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", age=" + age + ", creatTime=" + creatTime
+ ", Birth=" + Birth + "]";
}
@Temporal(TemporalType.TIMESTAMP)
public Date getCreatTime() {
return creatTime;
}
public void setCreatTime(Date creatTime) {
this.creatTime = creatTime;
}
@Temporal(TemporalType.DATE)
public Date getBirth() {
return Birth;
}
public void setBirth(Date birth) {
Birth = birth;
}
// @TableGenerator(name="ID_GENERATOR",
// table="jpa_id_generator",
// pkColumnName="PK_NAME",
// pkColumnValue="CUSTOMER_ID",
// valueColumnName="PK_VALUE",
// allocationSize=100)
// @GeneratedValue(strategy=GenerationType.TABLE ,generator="ID_GENERATOR")
@GeneratedValue(strategy=GenerationType.AUTO)
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="last_name")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Basic
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//工具方法,不需要映射为数据表的一列
@Transient
public String getInfo(){
return "lastName:" + lastName + ", email" + email;
}
}
②、创建test测试类
public class JPATest {
private EntityManager entityManager;
private EntityManagerFactory entityManagerFactory;
private EntityTransaction transaction;
@Before
public void init(){
entityManagerFactory = Persistence.createEntityManagerFactory("jpa-1");
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
}
@After
public void destory(){
transaction.commit();
entityManager.close();
entityManagerFactory.close();
}
//类似于在hibernate 中session 的get方法
//先生成数据表 === 》 之前的表可以都删除掉
//@Test
//public void testFind(){
//}
@Test
public void testFind(){
Customer customer = entityManager.find(Customer.class, 1);
System.out.println("------------------------");
System.out.println(customer);
}
}
运行结果 --- 在调用find 方法的同时就进行select 语句的查询,而不是当我需要这个属性的时候才发的select
2、getReference
getReference (Class<T> entityClass,Object primaryKey):与find()方法类似,不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,所以如果此 OID 在数据库不存在,getReference() 不会返回 null 值, 而是抛出EntityNotFoundException
test测试类中
//相当于hibernate中session的load方法
@Test
public void testGetReference(){
//这个实际上是返回customer对象的一个代理,当你真正需要使用这个对象属性的,他才向数据库里发布SQL语句,进行select查询
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println("------------------------");
System.out.println(customer);
}
运行结果:先打印的虚线,然后是语句,最后是结果 ====》相同的结果,不一样的过程
3、persistence
persist (Object entity):用于将新创建的 Entity 纳入到 EntityManager 的管理。该方法执行后,传入 persist() 方法的 Entity 对象转换成持久化状态。
①、如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做。
②、如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态。
③、如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)。
test测试类:
//类似于hibernate 的save 方法,使对象 由临时状态变为持久化状态
//和 hibernate 的save 方法的不同之处,若对象有id,则不能执行insert 操作,会抛出异常
@Test
public void testPersistence(){
Customer customer = new Customer();
customer.setAge(15);
customer.setBirth(new Date());
customer.setCreatTime(new Date());
customer.setEmail("bb@flx.com");
customer.setLastName("BB");
entityManager.persist(customer);
System.out.println(customer.getId());
}
运行结果:
若对象由id,则不能执行insert 操作,而会抛出异常
添加 customer.setId(100);
Hibernate 的save 方法:
@Test
public void testSave(){
Customer customer = new Customer();
customer.setAge(13);
customer.setBrith(new Date());
customer.setCreateTime(new Date());
customer.setEmail("nbv@163.com");
customer.setId(100);
customer.setLastName("nbv");
session.save(customer);
System.out.println(customer.getId());
}
4、remove
remove (Object entity):删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录。
不能移除游离对象
测试:
//类似于 hibernate 的 delete 方法,把对象从数据看中移除
//但注意:该方法只能移除持久化对象,而hibernate 的 delete 方法 实际上还可以移除 游离对象
@Test
public void testRemove(){
Customer customer = new Customer();
customer.setId(2);
entityManager.remove(customer);
}
运行结果
解决:test测试:
//类似于 hibernate 中 session 的 delete 方法,把对象从数据看中移除
//但注意:该方法只能移除持久化对象,而hibernate 的 delete 方法 实际上还可以移除 游离对象
@Test
public void testRemove(){
// Customer customer = new Customer();
// customer.setId(2);
Customer customer = entityManager.find(Customer.class,2);
entityManager.remove(customer);
}
运行结果
总结:
在 JPA 规范中, EntityManager 是完成持久化操作的核心对象。实体作为普通 Java 对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
实体的状态:
①、新建状态: 新创建的对象,尚未拥有持久性主键。
②、持久化状态:已经拥有持久性主键并和持久化建立了上下文环境
③、游离状态:拥有持久化主键,但是没有与持久化建立上下文环境
④、删除状态: 拥有持久化主键,已经和持久化建立上下文环境,但是从数据库中删除。
三、EntityManager的merge方法
merge (T entity):merge() 用于处理 Entity 的同步。即数据库的插入和更新操作
第一种情况:临时对象
test测试类:
/**
* 总的来说:类似于 hibernate session 的 saveorUpdate 方法
*/
//1.传入的是一个临时对象
@Test
public void testMerge1(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatTime(new Date());
customer.setEmail("cc@flx.com");
customer.setLastName("CC");
//执行merge 方法
Customer customer2 = entityManager.merge(customer);
System.out.println(customer.getId());
System.out.println(customer2.getId());
}
运行结果:数据库插入成功,customer 的id 为空
总结:
会创建一个新的对象,把临时对象的属性复制到新的对象当中,然后对新的对象执行持久化操作,所以新的对象中有 id,但是以前的临时对象中没有id 【看输出结果就可以知道】,简单来说就是customer 没有id , 而 customer2 有对象
第二种情况:游离对象
test测试:
@Test
public void testMerge2(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatTime(new Date());
customer.setEmail("cc@flx.com");
customer.setLastName("CC");
customer.setId(100);
//执行merge 方法
Customer customer2 = entityManager.merge(customer);
System.out.println(customer.getId());
System.out.println(customer2.getId());
}
运行结果: 没有被持久化
总结:
若传入的是一个游离对象,即传入的对象由OID
1.若在entityManager 缓存中没有该对象
2.若在数据库中也没有对应的记录
3.JPA会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象中】
4.对新创建的对象执行 insert 操作
什么是OID?
为了在系统中能够找到所需对象,我们需要为每一个对象分配一个唯一的表示号。在关系数据库中我们称之为关键字,而在对象术语中,则叫做对象标识(Object identifier-OID).
第三种情况:游离对象
若传入的是一个游离对象,即传入的对象由OID
1.若在entityManager 缓存中没有该对象
2.若在数据库中也有对应的记录
3.JPA会查询对应的记录,然后返回该记录对应的对象,在然后会把游离对象的属性复制到查询到的对象中
4.对查询到的对象执行 update 操作
test测试:
@Test
public void testMerge3(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatTime(new Date());
customer.setEmail("dd@flx.com");
customer.setLastName("DD");
customer.setId(6);
//执行merge 方法
Customer customer2 = entityManager.merge(customer);
System.out.println(customer == customer2);
}
结果:
用debug查询,在实体类的lastName 上debug,一共被查询了3次
1、是Customer customer = new Customer(); 的时候 lastName=CC
2、是查询的时候,lastName=CC
3、是复制的 lastName= DD
第四种情况 : 游离对象
若传入的是一个游离对象,即传入的对象由OID
1.若在EntityManager 缓存中有对应的对象
2.JPA把游离对象的属性复制到EntityManager 缓存中的对象
3.EntityManager 缓存中的对象执行 update 操作
@Test
public void testMerge4(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatTime(new Date());
customer.setEmail("dd@flx.com");
customer.setLastName("DD");
customer.setId(6);
//执行merge 方法
Customer customer2 = entityManager.find(Customer.class, 6);
entityManager.merge(customer);
System.out.println(customer == customer2);
}
总结:
①、对比hibernate 中的 saveOrUpdate ,传入的是一个游离对象。
②、在 Customer customer2 = entityManager.find(Customer.class, 4);这一行跟EntityManager关联的还有持久化对象,他们的id是一样的,这个时候对对象进行merge方法 ,在里面会有一个复制的操作。
③、而hibernate 是行不通的,hibernate不允许 session在同一个时刻跟两个id相同的对象进行关联。【会出现下面的报错】
一下代码是 : hibernate 与两个id是1的进行关联
@Test
public void testSaveOrUpdate(){
//若有id 则调用 update ,若没有 id,则调用 save
Customer customer = new Customer();
customer.setAge(15);
customer.setBirth(new Date());
customer.setCreatTime(new Date());
customer.setEmail("dd@flx.com");
customer.setLastName("DD");
customer.setId(1);
session.get(Customer.class,1);
session.saveOrUpdate(customer);
System.out.println(customer.getId());
System.out.println("============================");
}
则运行结果:
④、当然jpa也不可能,只不过entityManager 关联只有id是4的对象,但是这里面有复制的操作。
四、EntityManager的其他方法
①、flush ():同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。
@Test
public void testFlush(){
Customer customer = entityManager.find(Customer.class,1);
System.out.println(customer);
customer.setLastName("BB");
}
②、setFlushMode (FlushModeType flushMode):设置持久上下文环境的Flush模式。参数可以取2个枚举
FlushModeType.AUTO 为自动更新数据库实体,
FlushModeType.COMMIT 为直到提交事务时才更新数据库记录。
③、getFlushMode ():获取持久上下文环境的Flush模式。返回FlushModeType类的枚举值。
/**
* 同hibernate 中的 session 的 flush 方法
*/
@Test
public void testFulsh(){
Customer customer = entityManager.find(Customer.class, 1);
System.out.println(customer);
customer.setLastName("AA");
entityManager.flush();
}
强制执行update
但是数据库表里的记录没有变,因为没有提交事务 BB 没有改变成 AA
④、refresh (Object entity):用数据库实体记录的值更新实体对象的状态,即更新实例的属性值。
/**
* 同 hibernate 中 session 的 refresh 方法
*/
@Test
public void testRefresh(){
Customer customer = entityManager.find(Customer.class, 1);
customer = entityManager.find(Customer.class, 1);
entityManager.refresh(customer);
}
运行之后
⑤、clear ():清除持久上下文环境,断开所有关联的实体。如果这时还有未提交的更新则会被撤消。
⑥、contains (Object entity):判断一个实例是否属于当前持久上下文环境管理的实体。
⑦、isOpen ():判断当前的实体管理器是否是打开状态。
⑧、getTransaction ():返回资源层的事务对象。EntityTransaction实例可以用于开始和提交多个事务
⑨、close ():关闭实体管理器。之后若调用实体管理器实例的方法或其派生的查询对象的方法都将抛出 IllegalstateException 异常,除了 getTransaction 和 isOpen方法(返回 false)。不过,当与实体管理器关联的事务处于活动状态是,调用close 方法后持久上下文将仍处于被管理状态,直到事务完成。
⑩、createQuery (String qlString): 创建查询对象
11、createNamedQuery (String name):根据命名的查询语句块创建查询对象。参数为命名的查询语句、
12、createNativeQuery (String sqlString):使用标准SQL语句创建查询对象,参数为标准SQL语句字符串。
13、createNativeQuery (String sqls, String resultSetMapping): 使用标准SQL语句创建查询对象,并指定返回结果集Map 的名称。
五、EntityTransaction
EntityTransaction 接口用来管理资源层实体管理器的事务操作。
通过调用实体管理器的getTransaction方法 获得其实例。
①、begin () 用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛出 IllegalStateException 异常。
②、commit () 用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。
③、rollback () 撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作,从而不对数据库产生影响。
④、setRollbackOnly () 使当前事务只能被撤消。
⑤、getRollbackOnly () 查看当前事务是否设置了只能撤消标志。
⑥、isActive () 查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛出 IllegalStateException 异常;如果返回 false 则不能调用 commit、rollback、setRollbackOnly 及 getRollbackOnly 方法,否则将抛出 IllegalStateException 异常。
需要注意的是:在JPA里面,先需要getTransaction ,在begin
private EntityTransaction transaction;
transaction = entityManager.getTransaction();
transaction.begin();
在hibernate里面呢 , 直接begin ,然后进行commit
transaction = session.beginTransaction();
transaction.commit();