JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

前言:继续介绍 JPA ,这一篇将介绍 JPA 的常用 API,以及在 JPA 中映射关联关系。上一篇讲到 JPA 和 Hibernate 关系密切,尤其是在 API 和映射关联关系上,大家可以参看楼主关于 Hibernate 介绍的博客 Hibernate 学习笔记 - 1 和 Hibernate 学习笔记 - 2 ,与 Hibernate 类似的地方楼主也会特别指出。

四、JPA 的 API

1.Persistence :用于获取 EntiryManagerFactory 的实例

1)常用方法:Persistence.createEntityManagerFactory(persistenceUnitName) 方法

1 String persistenceUnitName = "jpa-1";        
2 EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);

2. EntiryManagerFactory :常用方法

1)获取 EntiryManager

1 //创建 EntityManager,类似于 Hibernate 的 SessionFactory
2 EntityManager entityManager = entityManagerFactory.createEntityManager();

2)close() 方法,关闭自身,此方法不再演示

3. EntityManager 的常用 API

1)find() 方法,类似于 Hibernate 中的 Session 的 get() 方法在执行 find 方法时就发送 SQL 语句

//类似于 Hibernate 中 Session 的 get 方法
    @Test
    public void testFind() {
        Customer customer = entityManager.find(Customer.class, 1);
        
        System.out.println("----------------------------------------");
        
        System.out.println(customer);
    }

打印结果为:查看横线的位置便可证明结论。

Hibernate: 
    select
        customer0_.id as id1_2_0_,
        customer0_.age as age2_2_0_,
        customer0_.birth as birth3_2_0_,
        customer0_.createTime as createTi4_2_0_,
        customer0_.email as email5_2_0_,
        customer0_.LAST_NAME as LAST_NAM6_2_0_ 
    from
        JPA_CUSTOMER customer0_ 
    where
        customer0_.id=?
----------------------------------------
Customer [id=1, lastName=AA, [email protected], age=21, birth=2015-10-22, createTime=2017-10-11 22:39:13.0]

2)getReference() 方法,类似于 Hibernate 的 Session 的 load() 方法

//相当于 Hibernate 中 Session 的 load 方法,若不使用查询的对象则返回一个代理对象,到真正使用时才发送 SQL 语句查询
    //可能会发生懒加载异常
    @Test
    public void testGetReference() {
        Customer customer = entityManager.getReference(Customer.class, 1);
        System.out.println(customer.getClass().getName());
        
        System.out.println("---------------------------------------");
        
//      transaction.commit();
//      entityManager.close();
        
        System.out.println(customer);
    }

打印结果为:打印的是一个代理对象,并且横线打印在  SQL 前面。

com.software.jpa.helloworld.Customer_$$_javassist_1
---------------------------------------
Hibernate: 
    select
        customer0_.id as id1_2_0_,
        customer0_.age as age2_2_0_,
        customer0_.birth as birth3_2_0_,
        customer0_.createTime as createTi4_2_0_,
        customer0_.email as email5_2_0_,
        customer0_.LAST_NAME as LAST_NAM6_2_0_ 
    from
        JPA_CUSTOMER customer0_ 
    where
        customer0_.id=?
Customer [id=1, lastName=AA, [email protected], age=21, birth=2015-10-22, createTime=2017-10-11 22:39:13.0]

3)persistence() 方法,类似于 Hibernate 的 save() 方法,与 Hibernate 的 save() 方法不同的是其不能插入一个有 id 属性的对象

//类似于 Hibernate 的 save 方法,使对象由临时状态变为持久化对象
    //和 Hibernate 的 save 方法的区别为若有 id 属性,则不会执行插入操作而会抛出异常
    @Test
    public void testPersistence() {
        Customer customer = new Customer();
        customer.setLastName("BB");
        customer.setEmail("[email protected]");
        customer.setBirth(new Date());
        customer.setCreateTime(new Date());
        customer.setAge(21);
        
//      customer.setId(100);
        
        entityManager.persist(customer);
        
        System.out.println(customer.getId());
        
    }

4)remove() 方法,类似于 Hibernate 中 Session 的 delete 方法,但是其不能删除 游离化对象(仅有 id),执行 5,6行会抛出异常,因为 5 行的 customer 对象为游离化对象

//类似于 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);
        
    }

5)merge() 方法,类似于 Hibernate 中 Session 的 saveOrUpdate() 方法

① 传入的是一个临时对象(没有 id):会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作,13行执行了 merge() 方法,传入了一个临时对象,返回了一个新的对象,产看 15,16 行的结果可知,新的对象有 id,传入的对象木有id,说明是将新的对象插入了数据库

//1.若传入的是一个临时对象(没有 Id)
    //会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作
    //所以 新的对象中有 id,而之前的临时对象中没有 id
    @Test
    public void testMerge1() {
        Customer customer = new Customer();
        customer.setAge(23);
        customer.setBirth(new Date());
        customer.setCreateTime(new Date());
        customer.setEmail("[email protected]");
        customer.setLastName("CC");
        
        Customer customer2 = entityManager.merge(customer);        
        
        System.out.println("customer's id:" + customer.getId());// null
        System.out.println("customer's id:" + customer2.getId());// 2
    }

② 传入的是一个游离对象(有 ID):若在 EntityManager 缓存中没有该对象,在数据库中也没有对应的记录,JPA 会创建一个新的对象,把当前游离对象的属性复制到新的对象中,对新创建的对象执行 insert 操作,楼主的数据库对应的表中并没有 id 为 100 customer,15 行同样返回了一个新的对象,根据返回结果可知 ,确实插入的是新的对象

//2.若传入的是一个游离对象,即传入的对象有 OID
    //若在 EntityManager 缓存中没有该对象,在数据库中也没有对应的记录,JPA 会创建一个新的对象,
    //把当前游离对象的属性复制到新的对象中,对新创建的对象执行 insert 操作
    @Test
    public void testMerge2() {
        Customer customer = new Customer();
        customer.setAge(23);
        customer.setBirth(new Date());
        customer.setCreateTime(new Date());
        customer.setEmail("[email protected]");
        customer.setLastName("DD");
        
        customer.setId(100);
        
        Customer customer2 = entityManager.merge(customer);
        
        System.out.println("customer's id:" + customer.getId());// 100
        System.out.println("customer's id:" + customer2.getId());// 3
    }

③ 传入的是游离对象,即传入的对象有 OID,缓存中没有,但数据库中有对应的对象:JPA 会查询对应的记录,然后返回该记录对应的对象把当前游离对象的属性复制到查询到的对象中,对查询到的对象执行 update 操作

//3.若传入的是一个游离对象,即传入的对象有 OID
    //若在 EntityManager 缓存中没有该对象,在数据库中有对应的记录,JPA 会查询对应的记录,然后返回该记录对应的对象
    //把当前游离对象的属性复制到查询到的对象中,对查询到的对象执行 update 操作
    @Test
    public void testMerge3() {
        Customer customer = new Customer();
        customer.setAge(23);
        customer.setBirth(new Date());
        customer.setCreateTime(new Date());
        customer.setEmail("[email protected]");
        customer.setLastName("FF");
        
        customer.setId(3);
        
        Customer customer2 = entityManager.merge(customer);
        
        System.out.println(customer == customer2); //false
    }

④ 传入的是游离对象,即传入的对象有 OID,EntityManager 缓存中有对应的对象:JPA 会把当前游离对象的属性复制到查询到的 EntityManager 缓存中的对象,对 EntityManager 缓存中的对象执行 update 操作

//4.若传入的是一个游离对象,即传入的对象有 OID
    //若在 EntityManager 缓存中有对应的对象,JPA 会把当前游离对象的属性复制到查询到的 EntityManager 缓存中的对象,
    //对 EntityManager 缓存中的对象执行 update 操作
    @Test
    public void testMerge4() {
        Customer customer = new Customer();
        customer.setAge(23);
        customer.setBirth(new Date());
        customer.setCreateTime(new Date());
        customer.setEmail("[email protected]");
        customer.setLastName("DD");
        
        customer.setId(3);
        Customer customer2 = entityManager.find(Customer.class, 3);
        
        entityManager.merge(customer);
        
        System.out.println(customer == customer2); //false
    }

4.EntityTransaction:JPA 中的事务操作

常用 API: begin()      commit()     rollback()  代码不再演示

五、JPA 中映射关联关系

1. 映射单向多对一的关联关系:Order : Customer  n:1 ,Order 中有 Customer 属性,而 Customer 中没有 Order 属性单向多对一区别于单向一对多

1)创建 Order 实体类,标注注解,生成数据表,使用 @ManyToOne 映射多对一的关联关系,使用 @JoinColumn 来标注外键

package com.software.jpa.helloworld;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Table(name="JPA_ORDERS")
@Entity
public class Order {
    
    private Integer id;
    
    private String orderName;
    
    @GeneratedValue
    @Id
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name="ORDER_NAME")
    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }
    
    private Customer customer;
    
    /**
     * 映射单项 n-1 的关联关系(Customer 和 Order,Order 中有 Customer 属性,而 Customer 中没有 Order 属性)
     * 使用 @ManyToOne 来映射多对一的关联关系
     * 使用 @JoinColumn 来映射外键
     * 可以使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略
     */
    @JoinColumn(name="CUSTOMER_ID")
    @ManyToOne(fetch=FetchType.LAZY)
    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    
}

2)单向多对一的保存(persist)保存多对一时,建议先保存 1 的一端,后保存 n 的一端,这样不会多出额外的 UPDATE 语句

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

3)获取操作(find)默认情况下使用左外连接的方式来获取 n 的一端的对象和其关联的 1 的一端的对象,可以使用 @ManyToOne 的 fetch 属性修改默认的关联属性的加载策略

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

4)删除操作(remove):不能直接删除 1 的一端,因为有外键约束

 JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

5)修改操作:

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

2.映射单向 1-n 的关联关系 Customer :Order  : nCustomer 中有 Order 的 Set 集合属性,Order 中没有 Customer的属性

1)在 Customer 中添加 Order 的 Set 集合属性,并映射 1-n 关联关系,重新生成数据表

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

2)保存操作(persist)总会多出 UPDATE 语句,n 的一端在插入时不会同时插入外键列

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

3)查询操作(find):默认使用懒加载

 JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

4)删除操作(remove):默认情况下,若删除 1 的一端,会先把关联的 n 的一端的外键置空,然后再进行删除,可以通过 @OneToMany 的 cascade 属性修改默认的删除策略(CascadeType.REMOVE 为级联删除)

 JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

3.映射双向多对一的关联关系注:双向多对一 同 双向一对多

1)实体:Customer 中有 Order 的 Set 集合属性,Order 中有 Customer 的属性,注两个实体映射的外键列必须一致,都为 CUSTOMER_ID

 JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

2)保存操作(persist)

 JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

 JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

4.映射双向一对一的关联关系

1)实体:Manager 和 Department ,一个部门有一个经理,一个经理管一个部门

2)创建 Manager 类和 Department 类,Manager 类中有 Department 的引用,Department 中有 Manager 的引用,由 Department 来维护关联关系(实际上双向 1- 1 双方均可以维护关联关系),使用 @OneToOne 来映射 1-1 关联关系。添加必要注解,生成数据表。

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

3)保存操作:

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

4)查询操作:

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

5.映射双向多对多的关联关系

1)实体:Item 和 Category ,一个类别有多个商品,一个商品对应多个类别双方都包含对方的 Set 集合。创建实体类,添加对应的注解,生成数据表。

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

2)保存操作:

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

3)查询操作

JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2

 

----------------------------------------------------------------------------------------------------------------