阅读 291

【JPA专题】05.JPA的基本操作CRUD

基本操作CRUD

根据上一篇文章中涉及到的方法进行相关的测试,这里我们需要准备一些测试的持久化类

老师基本信息持久化类:

package com.os.model;

import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name = "jpa_teacher")
public class Teacher {
    private Integer teacherId;
    private String teacherName;
    private Integer age;
    private String sex;
    private Date birthday;
    private Date createDate;

    @Column(name = "teacher_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    public Integer getTeacherId() {
        return teacherId;
    }

    public void setTeacherId(Integer teacherId) {
        this.teacherId = teacherId;
    }
    @Column(name = "teacher_name",length = 16,nullable = false)
    public String getTeacherName() {
        return teacherName;
    }

    public void setTeacherName(String teacherName) {
        this.teacherName = teacherName;
    }
    @Column(length = 3)
    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    @Column(length = 1)
    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    @Temporal(TemporalType.DATE)
    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    @Column(name = "create_date")
    @Temporal(value = TemporalType.TIMESTAMP)
    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "teacherId=" + teacherId +
                ", teacherName='" + teacherName + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", createDate=" + createDate +
                '}';
    }
}
复制代码

Snap2.jpg

数据也准备完毕了,使用程序添加的简单数据


引入单元测试,方便比较学习

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
复制代码

单元测试的基本模版代码如下

package com.os.test;

import org.junit.After;
import org.junit.Before;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JPATest {

    private EntityManagerFactory factory ;
    private EntityManager entityManager ;
    private EntityTransaction tx;
    @Before
    public void init(){
        factory = Persistence.createEntityManagerFactory("jpa02");
        entityManager = factory.createEntityManager();
        tx = entityManager.getTransaction();
        tx.begin();
    }
    @After
    public void close(){
        tx.commit();
        entityManager.close();
        factory.close();
    }
}
复制代码

find 和 getReference 区别

find (Class<T> entityClass,Object primaryKey):返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的 Entity, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。(类似于Hibernate中的get方法

之后的版本,我们就不在设置粘贴模版代码了,代码如下:

package com.os.test;

import com.os.model.Teacher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JPATest {

    private EntityManagerFactory factory ;
    private EntityManager entityManager ;
    private EntityTransaction tx;
    @Before
    public void init(){
        factory = Persistence.createEntityManagerFactory("jpa02");
        entityManager = factory.createEntityManager();
        tx = entityManager.getTransaction();
        tx.begin();
    }
    @After
    public void close(){
        tx.commit();
        entityManager.close();
        factory.close();
    }

    //类似于 hibernate 中 Session 的 get 方法.
    @Test
    public void testFind01(){
        Teacher teacher = entityManager.find(Teacher.class,1);//该ID是数据库存在
        System.out.println("=========================");
        System.out.println(teacher);
    }
    @Test
    public void testFind02(){
        Teacher teacher = entityManager.find(Teacher.class,100);//该ID是数据库不存在
        System.out.println("=========================");
        System.out.println(teacher);
    }
}
复制代码

getReference (Class<T> entityClass,Object primaryKey):与find()方法类似,不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,所以如果此 OID 在数据库不存在,getReference() 不会返回 null 值, 而是抛出EntityNotFoundException类似于Hibernate中的load方法

//类似于 hibernate 中 Session 的 load 方法
@Test
public void testGetReference01(){
    Teacher teacher = entityManager.getReference(Teacher.class,1);//该ID是数据库存在
    System.out.println("=========================");
    System.out.println(teacher);
}

/**
  * javax.persistence.EntityNotFoundException: Unable to find com.os.model.Teacher with id 100
  */
@Test
public void testGetReference02(){
    Teacher teacher = entityManager.getReference(Teacher.class,100);//该ID是数据库存在
    System.out.println("=========================");
    System.out.println(teacher);
}

/**
* org.hibernate.LazyInitializationException: could not initialize proxy [com.os.model.Teacher#1] - no Session
*/
@Test
public void testGetReference03(){
    Teacher teacher = entityManager.getReference(Teacher.class,1);//该ID是数据库存在
    System.out.println("=========================");
    tx.commit();
    entityManager.close();

    System.out.println(teacher);
}
复制代码

getReference涉及到延迟加载,需要注意EntityManager没有被关闭

为什么使用延迟加载?大家可以执行百度


persist (Object entity):用于将新创建的Entity纳入到EntityManager的管理。该方法执行后,传入persist()方法的 Entity 对象转换成持久化状态。(类似于Hibernate中的save方法)

  • 如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做。

    //类似于 hibernate 的 save 方法. 使对象由临时状态变为持久化状态.
    //和 hibernate 的 save 方法的不同之处: 若对象有 id, 则不能执行 insert 操作, 而会抛出异常.
    @Test
    public void testPersist01(){
        Teacher teacher = new Teacher();
        teacher.setTeacherName("唐僧");
        teacher.setAge(99);
        teacher.setSex("女");
        teacher.setBirthday(new Date());
        teacher.setCreateDate(new Date());
    
        entityManager.persist(teacher);//teacher已经处于持久化状态
        entityManager.persist(teacher);
        entityManager.persist(teacher);
    
        System.out.println(teacher.getTeacherId());
    }
    复制代码

    上述代码说明:我们执行了三次的保存操作,但是我们实际上添加到数据库中记录数为一条,原因就是执行persist方法后,该teacher对象已经处于了持久化状态(在一级缓存中),再次执行entityManager.persist(teacher);在一级缓存看看是否存在,如果一致,那么不不执行任何操作

        //类似于 hibernate 的 save 方法. 使对象由临时状态变为持久化状态.
        //和 hibernate 的 save 方法的不同之处: 若对象有 id, 则不能执行 insert 操作, 而会抛出异常.
        @Test
        public void testPersist02(){
            Teacher teacher = new Teacher();
            teacher.setTeacherName("沙僧");
            teacher.setAge(99);
            teacher.setSex("男");
            teacher.setBirthday(new Date());
            teacher.setCreateDate(new Date());
    
            entityManager.persist(teacher);//teacher已经处于持久化状态
            teacher.setTeacherName("卷帘大将");
    
            System.out.println(teacher.getTeacherId());
        }
    复制代码

    上述代码说明:在事务提交之前,检查一级缓存中存储的对象和实际的对象是否一致,如果不一致会产生Update语句,很重要的

        //类似于 hibernate 的 save 方法. 使对象由临时状态变为持久化状态.
        //和 hibernate 的 save 方法的不同之处: 若对象有 id, 则不能执行 insert 操作, 而会抛出异常.
        /*
        * javax.persistence.PersistenceException: 
        * org.hibernate.PersistentObjectException: 
        *   detached entity passed to persist: com.os.model.Teacher
        * */
        @Test
        public void testPersist03(){
            Teacher teacher = new Teacher();
            teacher.setTeacherName("悟空");
            teacher.setAge(99);
            teacher.setSex("男");
            teacher.setBirthday(new Date());
            teacher.setCreateDate(new Date());
            teacher.setTeacherId(88);//注意我设置了主键ID
    
            entityManager.persist(teacher);//teacher已经处于持久化状态
    
            System.out.println(teacher.getTeacherId());
        }
    复制代码
  • 如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态。

    @Test
    public void testPersist04(){
        Teacher teacher = entityManager.find(Teacher.class,5);//1.持久化状态
        entityManager.remove(teacher);
        System.out.println("teacher = " + teacher);//2.处于删除状态
    
        entityManager.persist(teacher);//3.处于游离状态
    }
    复制代码

    上述代码说明:其实根本就没有产生delete语句,只有一个查询操作,因为上述代码给人的感觉“还原

  • 如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)。

    @Test
    public void testPersist05(){
        Teacher teacher = entityManager.find(Teacher.class,5);//1.持久化状状态
        tx.commit();
        entityManager.close();
        System.out.println("teacher = " + teacher);//2.处于游离状态
    
        entityManager = factory.createEntityManager();
        tx = entityManager.getTransaction();
        tx.begin();
        entityManager.persist(teacher);
        /*
         *javax.persistence.PersistenceException: 
         *   org.hibernate.PersistentObjectException: 
         *       detached entity passed to persist: com.os.model.Teacher
         * */
    }
    复制代码

remove (Object entity):删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录。(类似于Hibernate中的Delete方法)

//类似于 hibernate 中 Session 的 delete 方法. 把对象对应的记录从数据库中移除
//但注意: 该方法只能移除 持久化 对象. 而 hibernate 的 delete 方法实际上还可以移除 游离对象.
@Test
public void testRemove01(){
    Teacher teacher = new Teacher();//瞬时状态
    teacher.setTeacherId(4);//数据库中存在该条记录
    entityManager.remove(teacher);
    /*
     * java.lang.IllegalArgumentException: 
     *   Removing a detached instance com.os.model.Teacher#4
     * */
}
复制代码

正确使用方式

//类似于 hibernate 中 Session 的 delete 方法. 把对象对应的记录从数据库中移除
//但注意: 该方法只能移除 持久化 对象. 而 hibernate 的 delete 方法实际上还可以移除 游离对象.
@Test
public void testRemove02(){
    Teacher teacher = entityManager.getReference(Teacher.class,4);//持久化对象
    entityManager.remove(teacher);//使用的时候先查询后执行删除操作
}
复制代码

merge (T entity):merge() 用于处理 Entity 的同步。即数据库的插入和更新操作,这个操作的情况有点多,我们通过代码进行说明

状态转换的思维导图.png

总的来说: 类似于 hibernate Session 的 saveOrUpdate 方法.

1.临时对象,执行保存操作

/*
 * 若传入的是一个临时对象
 * 会创建一个新的对象, 把临时对象的属性复制到新的对象中, 然后对新的对象执行持久化操作. 所以
 * 新的对象中有 id, 但以前的临时对象中没有 id.
 * */
@Test
public void testMerge1(){
    Teacher teacher = new Teacher();
    teacher.setTeacherName("林黛玉");
    teacher.setAge(19);
    teacher.setSex("女");
    teacher.setBirthday(new Date());
    teacher.setCreateDate(new Date());

    Teacher teacher1 = entityManager.merge(teacher);
    System.out.println("teacher.getTeacherId() = " + teacher.getTeacherId());
    System.out.println("teacher1.getTeacherId() = " + teacher1.getTeacherId());

}
复制代码

2. 若传入的是一个游离对象,若在 EntityManager 缓存中没有该对象, 即传入的对象有 OID,数据库中没有对应的主键ID

执行Insert操作,老对象的teacherId是100,保存之后新对象的主键ID是数据库中的主键ID

@Test
public void testMerge2(){
    Teacher teacher = new Teacher();
    teacher.setTeacherName("林黛玉");
    teacher.setAge(19);
    teacher.setSex("女");
    teacher.setBirthday(new Date());
    teacher.setCreateDate(new Date());

    teacher.setTeacherId(100);//数据库没有该主键ID

    Teacher teacher1 = entityManager.merge(teacher);
    System.out.println("teacher.getTeacherId() = " + teacher.getTeacherId());
    System.out.println("teacher1.getTeacherId() = " + teacher1.getTeacherId());

}
复制代码

3. 若传入的是一个游离对象,若在 EntityManager 缓存中没有该对象, 即传入的对象有 OID,数据库中有对应的主键ID

JPA 会查询对应的记录, 然后返回该记录对一个的对象, 再然后会把游离对象的属性复制到查询到的对象中,执行Update操作

@Test
public void testMerge3(){
    Teacher teacher = new Teacher();
    teacher.setTeacherName("林黛玉");
    teacher.setAge(19);
    teacher.setSex("女");
    teacher.setBirthday(new Date());
    teacher.setCreateDate(new Date());

    teacher.setTeacherId(5);//数据库有该主键ID

    Teacher teacher1 = entityManager.merge(teacher);
    System.out.println("teacher.getTeacherId() = " + teacher.getTeacherId());
    System.out.println("teacher1.getTeacherId() = " + teacher1.getTeacherId());

}
复制代码

4. 若传入的是一个游离对象,若在 EntityManager 缓存中有该对象, 即传入的对象有 OID,数据库中有对应的主键ID

JPA 会把游离对象的属性复制到查询到EntityManager 缓存中的对象中,EntityManager 缓存中的对象执行 UPDATE

@Test
public void testMerge4(){
    Teacher teacher = new Teacher();
    teacher.setTeacherName("林黛玉");
    teacher.setAge(19);
    teacher.setSex("女");
    teacher.setBirthday(new Date());
    teacher.setCreateDate(new Date());

    teacher.setTeacherId(7);//数据库有该主键ID
    System.out.println("teacher = " + teacher);

    Teacher teacher1 = entityManager.find(Teacher.class,7);//一级缓存中存在该对象
    System.out.println("teacher1 = " + teacher1);

    entityManager.merge(teacher);

    System.out.println(teacher==teacher1);//返回的是false
}
复制代码

flush ():同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中

这里需要使用断点调试,断点设置如图

Snap3.jpg

@Test
public void testFlush(){
    Teacher teacher = entityManager.find(Teacher.class,1);
    System.out.println("teacher = " + teacher);

    teacher.setTeacherName("林冲");

    entityManager.flush();
}
复制代码

refresh (Object entity):用数据库实体记录的值更新实体对象的状态,即更新实例的属性值。

@Test
public void testReflush(){
    Teacher teacher = entityManager.find(Teacher.class,1);
    System.out.println("teacher = " + teacher);
    teacher = entityManager.find(Teacher.class,1);

    entityManager.refresh(teacher);//重新进行查询操作
}
复制代码