半步多 玄玉V笔记

JPA入门例子

2010-11-22
玄玉

简介

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();
    }
}

Content