简介
JPA(Java Persistence API
)是SUN官方于2006年05月份提出的,由Hibernate作者主导制定的Java持久化规范
它的总体思想和现有Hibernate、TopLink、JDO等ORM框架大体一致
总的来说包括以下3方面的技术
1)ORM映射元数据
JPA支持XML和JDK5注解
两种元数据(描述对象和表之间的映射关系)
的形式
JPA框架会根据元数据将实体对象持久化到数据库表中
2)Java持久化API
用来操作实体对象,执行CRUD操作,开发者可以从繁琐的JDBC和SQL代码中解脱出来
3)查询语言
通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合,这是持久化操作中很重要的一方面
Hibernate实现所需的jar
JPA规范实现的框架中,较为常用的是Hibernate实现,通常需要依赖Hibernate的14个jar文件,如下所示
注意:這些JAR文件不要放在含有中文或空格的路径下
#1)Hibernate核心包(8个文件)
hibernate-distribution-3.3.1.GA//..//Hibernate3.jar
hibernate-distribution-3.3.1.GA//..//lib//required//*.jar
hibernate-distribution-3.3.1.GA//..//lib//bytecode//cglib///hibernate-cglib-repack-2.1_3.jar
#2)Hibernate注解包(3个文件)
hibernate-annotations-3.4.0.GA//..//hibernate-annotations.jar
hibernate-annotations-3.4.0.GA//..//lib//ejb3-persistence.jar
hibernate-annotations-3.4.0.GA//..//lib//hibernate-commons-annotations.jar
#3)Hibernate针对JPA的实现包(3个文件)
hibernate-entitymanager-3.4.0.GA//..//hibernate-entitymanager.jar
hibernate-entitymanager-3.4.0.GA//..//lib//test//log4j.jar
ibernate-entitymanager-3.4.0.GA//..//lib//test//slf4j-log4j12.jar
示例代码
首先是JPA规范要求的:类路径下放置一个文件名固定的//META-INF//persistence.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<!-- persistence-unit是指持久化单元,简单的说就代表一堆实体bean的集合 -->
<persistence-unit name="xuanyuJPADemo" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<!--
这里hibernate的前缀必须带上
其中hbm2ddl.auto=update,表示在获取到EntityManagerFactory对象时
若元数据未更改并且表已存在,则不更新表
若表不存在则创建表,表名则是根据实体名称来建立的
且当映射元数据中添加新字段时,会把新添加的映射字段添加到数据表中
-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/>
<property name="hibernate.connection.username" value="scott"/>
<property name="hibernate.connection.password" value="xuanyu"/>
<property name="hibernate.connection.url" value="jdbc:oracle:thin:@127.0.0.1:1521:xuanyu"/>
</properties>
</persistence-unit>
</persistence>
然后是用到的一个用于保存性别的自定义枚举类Gender.java
package com.xuanyuv.model;
public enum Gender {MAN, WOMEN}
下面是采用注解映射元数据的实体类Person.java
package com.xuanyuv.model;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import java.util.Date;
/**
* 注解可以标注在属性或者getter上面,但是:要么全标注在属性上,要么全标注在getter上
* 比如这里若把@Id标注在了属性上面,那么其它属性的注解就没有必要再标注到getter上面了
* 因为当发现@Id标注在属性上时,它就会认为所有注解都标准在属性上,也就会忽略对getter的检查
* Created by 玄玉<https://www.xuanyuv.com/> on 2010/11/22 17:26.
*/
//将普通JavaBean变为实体Bean,注意这里导入的是SUN的注解
@Entity
//自定义生成的表名为personxx,默认创建的表名与该实体类名相同
@Table(name="personxx")
public class Person {
//JPA规范中可能并不提供uuid生成策略,但我们可以利用JDK5提供的UUID类,即UUID.randomUUID().toString()
//设置主键生成策略,默认值就是AUTO
//@Id @GeneratedValue(strategy=GenerationType.AUTO)
@Id
@GeneratedValue
private Integer id;
//指定表字段为personName,长度10,且不可为空
@Column(name="personName", length=10, nullable=false)
private String name;
//指定生成的字段类型为DATE
@Temporal(TemporalType.DATE)
private Date birthday;
//指定字段为枚举类型,并且保存时保存的是枚举的字符串,同时也设定了表字段的默认值
//若定义为EnumType.ORDINAL则表示,保存时保存的是枚举的索引,索引值是从0开始的
@Enumerated(EnumType.STRING)
@Column(length=5, nullable=false)
private Gender gender = Gender.MAN;
//指定字段为大文本类型
@Lob
private String info;
//这是专门用来处理大文本和二进制数据的映射注解
@Lob
//指定该字段延迟加载。即只有在访问该属性时,才会把它的数据装载进内存中
@Basic(fetch= FetchType.LAZY)
private Byte[] context;
//指定该属性不作为持久化字段,也就是说不与数据库表中的字段做任何关系映射
@Transient
private String imagepath;
/*
* 因为对象创建时,是由Hibernate内部通过反射技术帮我们创建的,反射时就用到了默认的构造函数
* 所以我们在重写构造方法时,必须再显式的提供public的无参的构造器
*/
public Person(){}
//为了添加数据的方便,这里提供一个构造函数
public Person(String name) {
this.name = name;
}
/* 关于上面七个属性的setter和getter略 */
}
最后是用JUnit4写的单元测试类JPADemoTest.java
package com.xuanyuv.junit;
import com.xuanyuv.model.Person;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.junit.Test;
/**
* Created by 玄玉<https://www.xuanyuv.com/> on 2010/11/22 17:26.
*/
public class JPADemoTest {
@Test
public void save(){
//这里EntityManagerFactory可以看作是类似于Hibernate中的SessionFactory对象
//由于hibernate.hbm2ddl.auto设为update,所以这里得到factory时,表会自动创建,这点与Hibernate是相同的
//所以通过这一特性,在开发时也可以用来验证我们编写的实体映射元数据是否是正确的,即这里的Person.java类
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
//EntityManager相当于Hibernate中的获取session对象,它内部只是对session对象做了一个封装
EntityManager em = factory.createEntityManager();
//开启事务
em.getTransaction().begin();
//在Hibernate中的session有一个save()方法,但Hibernate的作者更推荐使用persist()方法
//并不是因为这两个方法在功能或代码上有什么不同,它们的功能都是相同的,事实上主要是名字的问题
//因为我们把ORM技术叫做持久化产品,那么当对某个对象进行持久化时,应该叫做持久化,不应该叫做保存
em.persist(new Person("杨过"));
//提交事务
//注意两次得到的事务对象都是同一个事务对象
em.getTransaction().commit();
em.close();
factory.close();
}
/**
* 读取数据,不需要开启事务(只有存在更改数据的动作时,才开启事务)
*/
@Test
public void getPerson(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
//持久化管理器的find()方法采用了泛型。我们传进去什么类型,它就会返回什么类型。所以这里不需要类型转换
//这里find()类似于Hibernate中的get()方法。同样若数据不存在,则返回null
Person person = em.find(Person.class, 1);
//我们在获取某条记录之后,在我们操作记录的过程中,假设有人在数据库中删掉了或更新了该记录
//这时若使用find()去重新获取记录的话,那么它会从EntityManager的一级缓存中查找记录
//这时若想得到数据库中的最新数据的话,可以使用em.refresh()可以重新获取记录
//它相当于重新获取了一个EntityManager对象,然后用新的实体管理器再去获取记录
//em.refresh(arg0);
System.out.println(person.getName());
em.close();
factory.close();
}
@Test
public void getPerson22(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
//这里getReference()类似于Hibernate中的load()方法
//同样若数据不存在,则抛出异常
Person person = em.getReference(Person.class, 2);
System.out.println(person.getName());
em.close();
factory.close();
}
/**
* JPA中的四种实体状态
* 1.新建状态-->new Person("杨过")
* 2.托管状态-->find()
* 3.游离状态-->clear()
* 4.删除状态
*/
@Test
public void update(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Person person = em.find(Person.class, 2);
//当对象处于托管状态,并且实体管理器已经与事务关联
//这时对属性的修改就能够同步到数据库
person.setName("沈浪");
em.getTransaction().commit();
em.close();
factory.close();
}
@Test
public void update22(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Person person = em.find(Person.class, 2);
//将目前处于实体管理器中的所有的实体变成游离状态
em.clear();
//对游离状态的对象进行更新时,不会同步到数据库中
person.setName("王怜花");
//把游离状态时的更新,同步回数据库中
//注意前提条件是游离状态
em.merge(person);
em.getTransaction().commit();
em.close();
factory.close();
}
@Test
public void delete(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Person person = em.find(Person.class, 2);
//删除托管状态的实体
//经测试:它是不能删除游离状态中的实体的
em.remove(person);
em.getTransaction().commit();
em.close();
factory.close();
}
/**
* 在JPA规范中,它的JPQL语言里面,进行查询操作时需要以select开头
* 但所使用的JPA实现产品如果是Hibernate,那么查询语句不以select也是可以的。但不能确保在其它产品中也是可以的
*/
@Test
public void JPQLquery(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
//使用的是javax.persistence.Query接口
Query query = em.createQuery("select p from Person p where p.id=?88");
//在JPA中,可以在参数问号的后面加上数字,用来指定它的索引值
query.setParameter(88, 1);
//JPA中的query.getResultList()就类似于Hibernate中的query.list()方法
//JPA中的query.getSingleResult()类似于Hibernate中的query.uniqueResult()方法
Person person = (Person)query.getSingleResult();
System.out.println(person.getName());
em.close();
factory.close();
}
@Test
public void JPQLupdate(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Query query = em.createQuery("update Person p set p.name=:name where p.id=:id");
query.setParameter("name", "李寻欢");
query.setParameter("id", 1);
query.executeUpdate();
em.getTransaction().commit();
em.close();
factory.close();
}
@Test
public void JPQLdelete(){
EntityManagerFactory factory = Persistence.createEntityManagerFactory("xuanyuJPADemo");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Query query = em.createQuery("delete from Person p where p.id=?88");
query.setParameter(88, 1);
query.executeUpdate();
em.getTransaction().commit();
em.close();
factory.close();
}
}