JPA持久化API,针对数据库的增删改查(二):实体的状态;在JavaSE环境下使用EntityManager;理解实体;实体类的要求;实体的三种状态

2.在JavaSE环境下使用EntityManager
JPA核心就是EntityManager,EntityManager负责管理JPA持久化上下文中的所有实体,它负责跟踪所有实体的保存,
	更新和修改的情况,并根据指定的flush模式将这些修改保存到数据库中。

从功能上看,EntityManager类似于Hibernate框架中的Session,他负责完成实体操作和数据库操作之间的转换:
	当EntityManager保存一个实体时,底层会产生一条insert语句:当修改一个实体时,底层会产生一条Update语句:
	当EntityManager删除一个实体时,底层会生成一条delete语句。

为了在应用程序中使用EntityManager,大致分为如下3种情况
1.在EJB(主要是作为EAO组件的EJB)中使用EntityManager:直接使用依赖注入来管理EntityManager即可

2.在Servlet,JSF的托管Bean中使用EntityManager:不能直接使用依赖注入来管理EntityManager。
	因为多个请求线程可能共享一个Servlet或JSF的托管Bean,而EntityManager并没有被设计成线程安全的,
	因此可能导致线程安全问题。在这种情况下有两种解决方法:
		1.使用JNDI来查找获得EntityManager对象
		2.使用依赖注入管理EntityManagerFactory(它是线程安全的)对象,再通过EntityManagerFactory来获取EntityManager对象
		3.在JavaSE应用中使用EntityManager:需要通过应用程序显示地创建EntityManager。
		
3.在JavaSE应用程序中创建EntityManager对象通过如下步骤进行即可
	1.通过javax.persistence.Persistence工厂提供的createEntityManagerFactory()静态方法创建EntityManagerFactory对象。
		调用该方法时需要传入persistence.xml文件中持久化单元的名称
	
	2.调用EntityManagerFactory的createEntityManagerFactory()或createEntityManagerFactory(Map map)方法来创建EntityManager对象。
		第二个createEntityManager方法可以传入一个Map参数,这个Map参数传入的属性将会补充或覆盖persistence.xml文件中配置的属性
	实体操作和数据库的转换
3.理解实体
JPA的主要思想就是让实体来映射底层数据表,而JPA通过EntityManager保存,删除,检索实体,JPA实例则将这种面向对象的操作转换为SQL语句

1.持久化上下文和持久化单元
	在JPA规范中涉及两个常用的概念:持久化上下文(persistence context)和持久化单元(persistence unit)
	持久化上下文是一组处于托管状态下实体所组成的集合(是一种范围 定义了JPA管理的边界),
		当应用程序改变了持久化上下文中的实体状态后,EntityManager与持久化上下文进行交互
		
EntityManager负责跟踪持久化上下文中所有实体的状态,当应用程序改变了持久化上下文中的实体状态后,
	EntityManager将会根据指定的flush模式将实体的状态写入底层数据库。

如果持久化上下文被关闭,该上下文中的所有实体都将脱离EntityManager的管理,进入脱管状态,
	此时对实体所做的修改将不会被自动同步到底层数据库

持久化单元则由persistence.xml文件定义,正如前面配置文件中看到,该文件中包含了一个<persistence-unit.../>元素,
	该元素就用于定义持久化单元,还元素的name属性的值就是该持久化单元的名称,应用程序可通过该名称来访问持久化单元

持久化·单元由persistence.xml文件定义(该文件要放在META-INF路径下(JavaSE,web应用 war下,EJB jar包))
4.实体类的要求
JPA摒弃了EJB2中实体Bean的侵入式设计,吸收了Hibernate框架低侵入式设计的特点,
	这种设计导致JPA对实体类几乎不做任何要求。也就是说,JPA操作的实体类基本上都是普通,传统的Java对象(POJO)。
	对于这种Java类,程序开发可以采用更灵活的领域建模方式
基本准则:
1.提供一个无参构造器:
	所有的实体类都应该提供一个无参构造器,这个构造器不一定要采用Public访问控制符。
		但由于JPA(实例化对象时必须采用一个无参构造器) 实现通常会采用Constructor.newInstance()方式来创建实体的实例,
		通常,为了方便JPA在运行时生成代理,构造器的访问控制修饰符至少是包可见的,即大于或等于默认的访问控制符。
	创建对象1.new方法 2.类.newInstance()(调用类的无参构造方法)
	
2.提供一个标识属性(唯一标识):
	标识属性通常映射数据库表的主键字段。这个属性可以叫任何名字,其类型可以是任何原始类型,原始类型的包装类型,
		java.lang.String或者java.util.Date。如果使用了数据库表的联合主键,甚至可以用一个用户自定义的类,
		该类拥有这些类型的属性。当然,也可以不指定任何标识属性,
		而是在映射文件中直接将多个普通属性映射成一个联合主键,但通常不推荐这么做
	JPA可以允许实体类没有标识属性 但会导致JPA许多功能无法使用 JPA建议使用允许接收null值的类型(是一个引用类型)
		来作为标识符类型,int接收的是整形数字,接收不了null值,改成Integer可以设置为null(包装类:可以生成为对象)
		
3.为每个实体类提供getter setter方法:
	JPA默认采用属性方式来访问实体类属性。JPA持久化JavaBean风格的属性,
		认可如下形式的方法名getXxx,isXxx和setXxx。如果需要,也可以切换属性的访问策略
		
4.使用非final的类:(不能派生子类 变量final 数据不能修改)
	许多JPA实现都需要在运行时生成动态。如果实体类没有实现任何接口的话,那么JPA就需要为他动态的生成CGLOB代理类。
		该代理对象是实体类的子类的实例。如果使用final类,则无法生成CGLIB代理,将无法进行性能优化
		
5.重写equals()和hashCode()方法:
	如果需要把实体类的实例放入Set中(当需要进行关联映射时,推荐这么做),则应该为实体类重写equals()和hashCode()方法。
		实现equals()/hashCode()最显而易见的方法是比较两个对象的标识符的值,如果值相同,则两个对象对应于数据库的同一行,
		因此它们是相等的(如果都被添加到Set中,则在Set中只有一个元素)。
		遗憾的是,对采用自动生成标识值的对象不能使用这种方法。JPA仅为那些持久化后的实体指定标识值,
		一个新创建的实体将不会有任何标识值。因此,如果一个实体没有被保存过,但它却是在一个Set中,
		保存他将会给这个对象赋一个标识值。如果equals()和hashCode()是基于标识值实现的,
		则其hashCode值码会发生改变,这将违反Set的规则

比较两个对象标识符的值 相同 则两个对象对应于数据库的同一行 
对于自增不能使用
5.实体的三种状态
1.新建new:(瞬时对象)内存中
	实体由new创建,且尚未与EntityManager关联过的实体被认为处于新建状态。新建状态的实体不会被持久化数据库中,
		如果程序失去了新建实体的引用,垃圾回收机制将会回收新建实体。通过EntityManager可以将其变为持久化状态。
		除此之外,如果应用程序调用EntityManager的delete()方法删除某个实体,那么这个实体也会转入瞬态
		
2.托管managed:(新建状态通过EntityManager变为持久化状态)
	托管状态下的实体在数据库中有对应的记录,并拥有一个持久化标识(identity)。托管下的实体也可以是刚刚保存的,
		也可以是刚刚被加载的。无论是哪一种,托管的实体都是处于EntityManager管理之下。
		JPA会自动检测到处于托管状态下实体的改动,当程序修改托管实体的属性时,
		JPA负责将这种修改转换成对数据记录的修改,开发者不需要手动执行UPDATE语句
	eg:
	@Test//托管状态
	public void test2() {
		EntityManager em = emf.createEntityManager();
		
		try {
			//该对象处于瞬时(新建状态)
			Emp emp = new Emp();
			emp.setEmpno(6);
			emp.setEname("l1");
			emp.setJob("l1");
			em.getTransaction().begin();
			//持久化对象但没有进行保存
			em.persist(emp);
			//持久化之后马上进行修改设置
			//相当于先执行insert后执行了Update
			emp.setEname("l2");
			em.getTransaction().commit();
			//并没有保存 没有提交事务 提交事务才会形成对应的SQL语句
			emp.setEname("l3");
			System.out.println("SUCCESS");
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			em.getTransaction().rollback();
		}
		
	}
3.脱管detached:
	如果某个实体曾处于托管状态,那么他将有一个持久化标识,但随着与之关联的EntityManager被关闭,
		该实体就变成了脱管状态。托管状态下的实体的引用依然有效,实体可继续被修改。之所以把实体的这种状态称为托管,
		指的就是该实体脱离了EntityManager的管理 
	该状态实体引用依然有效 可继续被修改
	
4.被删除removed:
	当EntityManager调用remove()方法指定删除后,该实体进入被删除状态。被删除的实体还关联着某个持久化上下文,
		而且它对应的记录即将从数据库中被删除
	但在程序中还存在
	eg:
	@Test//被删除状态
	public void test3() {
		
		EntityManager em = emf.createEntityManager();
		
		try {
			Emp emp = new Emp();
			emp.setEmpno(4);
			emp.setEname("zz");
			emp.setJob("sm");
			em.getTransaction().begin();
			em.persist(emp);
			em.remove(emp);
			em.getTransaction().commit();
			//持久化状态中删除是删除数据表中记录删除,但对象里面的内容并没有删除,持久化状态对象又变为瞬时状态对象
			System.out.println(emp.getEmpno());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			em.getTransaction().rollback();
		}finally {
			em.close();
		}
		
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值