JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 2
原创播客,如需转载请注明出处。原文地址:http://www.cnblogs.com/crawl/p/7704914.html
-----------------------------------------------------------------------------------------------------------------------------------------------------------
笔记中提供了大量的代码示例,需要说明的是,大部分代码示例都是本人所敲代码并进行测试,不足之处,请大家指正~
本博客中所有言论仅代表博主本人观点,若有疑惑或者需要本系列分享中的资料工具,敬请联系 qingqing_crawl@163.com
-----------------------------------------------------------------------------------------------------------------------------------------------------------
前言:继续介绍 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 语句
1 //类似于 Hibernate 中 Session 的 get 方法 2 @Test 3 public void testFind() { 4 Customer customer = entityManager.find(Customer.class, 1); 5 6 System.out.println("----------------------------------------"); 7 8 System.out.println(customer); 9 }
打印结果为:查看横线的位置便可证明结论。
1 Hibernate: 2 select 3 customer0_.id as id1_2_0_, 4 customer0_.age as age2_2_0_, 5 customer0_.birth as birth3_2_0_, 6 customer0_.createTime as createTi4_2_0_, 7 customer0_.email as email5_2_0_, 8 customer0_.LAST_NAME as LAST_NAM6_2_0_ 9 from 10 JPA_CUSTOMER customer0_ 11 where 12 customer0_.id=? 13 ---------------------------------------- 14 Customer [id=1, lastName=AA, email=aa@163.com, age=21, birth=2015-10-22, createTime=2017-10-11 22:39:13.0]
2)getReference() 方法,类似于 Hibernate 的 Session 的 load() 方法
1 //相当于 Hibernate 中 Session 的 load 方法,若不使用查询的对象则返回一个代理对象,到真正使用时才发送 SQL 语句查询 2 //可能会发生懒加载异常 3 @Test 4 public void testGetReference() { 5 Customer customer = entityManager.getReference(Customer.class, 1); 6 System.out.println(customer.getClass().getName()); 7 8 System.out.println("---------------------------------------"); 9 10 // transaction.commit(); 11 // entityManager.close(); 12 13 System.out.println(customer); 14 }
打印结果为:打印的是一个代理对象,并且横线打印在 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=aa@163.com, age=21, birth=2015-10-22, createTime=2017-10-11 22:39:13.0]
3)persistence() 方法,类似于 Hibernate 的 save() 方法,与 Hibernate 的 save() 方法不同的是其不能插入一个有 id 属性的对象
1 //类似于 Hibernate 的 save 方法,使对象由临时状态变为持久化对象 2 //和 Hibernate 的 save 方法的区别为若有 id 属性,则不会执行插入操作而会抛出异常 3 @Test 4 public void testPersistence() { 5 Customer customer = new Customer(); 6 customer.setLastName("BB"); 7 customer.setEmail("bb@163.com"); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setAge(21); 11 12 // customer.setId(100); 13 14 entityManager.persist(customer); 15 16 System.out.println(customer.getId()); 17 18 }
4)remove() 方法,类似于 Hibernate 中 Session 的 delete 方法,但是其不能删除 游离化对象(仅有 id),执行 5,6行会抛出异常,因为 5 行的 customer 对象为游离化对象
1 //类似于 Hibernate Session 的 delete 方法,把对象对应的记录从数据库中删除 2 //注:该方法只能移出 持久化 对象,而 Hibernate 的 delete 方法可以移除游离对象 3 @Test 4 public void testRemove() { 5 // Customer customer = new Customer(); 6 // customer.setId(2); 7 8 Customer customer = entityManager.find(Customer.class, 2); 9 10 entityManager.remove(customer); 11 12 }
5)merge() 方法,类似于 Hibernate 中 Session 的 saveOrUpdate() 方法
① 传入的是一个临时对象(没有 id):会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作,13行执行了 merge() 方法,传入了一个临时对象,返回了一个新的对象,产看 15,16 行的结果可知,新的对象有 id,传入的对象木有id,说明是将新的对象插入了数据库
1 //1.若传入的是一个临时对象(没有 Id) 2 //会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作 3 //所以 新的对象中有 id,而之前的临时对象中没有 id 4 @Test 5 public void testMerge1() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("cc@126.com"); 11 customer.setLastName("CC"); 12 13 Customer customer2 = entityManager.merge(customer); 14 15 System.out.println("customer's id:" + customer.getId());// null 16 System.out.println("customer's id:" + customer2.getId());// 2 17 }
② 传入的是一个游离对象(有 ID):若在 EntityManager 缓存中没有该对象,在数据库中也没有对应的记录,JPA 会创建一个新的对象,把当前游离对象的属性复制到新的对象中,对新创建的对象执行 insert 操作,楼主的数据库对应的表中并没有 id 为 100 customer,15 行同样返回了一个新的对象,根据返回结果可知 ,确实插入的是新的对象
1 //2.若传入的是一个游离对象,即传入的对象有 OID 2 //若在 EntityManager 缓存中没有该对象,在数据库中也没有对应的记录,JPA 会创建一个新的对象, 3 //把当前游离对象的属性复制到新的对象中,对新创建的对象执行 insert 操作 4 @Test 5 public void testMerge2() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("dd@126.com"); 11 customer.setLastName("DD"); 12 13 customer.setId(100); 14 15 Customer customer2 = entityManager.merge(customer); 16 17 System.out.println("customer's id:" + customer.getId());// 100 18 System.out.println("customer's id:" + customer2.getId());// 3 19 }
③ 传入的是游离对象,即传入的对象有 OID,缓存中没有,但数据库中有对应的对象:JPA 会查询对应的记录,然后返回该记录对应的对象把当前游离对象的属性复制到查询到的对象中,对查询到的对象执行 update 操作
1 //3.若传入的是一个游离对象,即传入的对象有 OID 2 //若在 EntityManager 缓存中没有该对象,在数据库中有对应的记录,JPA 会查询对应的记录,然后返回该记录对应的对象 3 //把当前游离对象的属性复制到查询到的对象中,对查询到的对象执行 update 操作 4 @Test 5 public void testMerge3() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("ff@126.com"); 11 customer.setLastName("FF"); 12 13 customer.setId(3); 14 15 Customer customer2 = entityManager.merge(customer); 16 17 System.out.println(customer == customer2); //false 18 }
④ 传入的是游离对象,即传入的对象有 OID,EntityManager 缓存中有对应的对象:JPA 会把当前游离对象的属性复制到查询到的 EntityManager 缓存中的对象,对 EntityManager 缓存中的对象执行 update 操作
1 //4.若传入的是一个游离对象,即传入的对象有 OID 2 //若在 EntityManager 缓存中有对应的对象,JPA 会把当前游离对象的属性复制到查询到的 EntityManager 缓存中的对象, 3 //对 EntityManager 缓存中的对象执行 update 操作 4 @Test 5 public void testMerge4() { 6 Customer customer = new Customer(); 7 customer.setAge(23); 8 customer.setBirth(new Date()); 9 customer.setCreateTime(new Date()); 10 customer.setEmail("dd@126.com"); 11 customer.setLastName("DD"); 12 13 customer.setId(3); 14 Customer customer2 = entityManager.find(Customer.class, 3); 15 16 entityManager.merge(customer); 17 18 System.out.println(customer == customer2); //false 19 }
4.EntityTransaction:JPA 中的事务操作
常用 API: begin() commit() rollback() 代码不再演示
五、JPA 中映射关联关系
1. 映射单向多对一的关联关系:Order : Customer n:1 ,Order 中有 Customer 属性,而 Customer 中没有 Order 属性(单向多对一区别于单向一对多)
1)创建 Order 实体类,标注注解,生成数据表,使用 @ManyToOne 映射多对一的关联关系,使用 @JoinColumn 来标注外键
1 package com.software.jpa.helloworld; 2 3 import javax.persistence.Column; 4 import javax.persistence.Entity; 5 import javax.persistence.FetchType; 6 import javax.persistence.GeneratedValue; 7 import javax.persistence.Id; 8 import javax.persistence.JoinColumn; 9 import javax.persistence.ManyToOne; 10 import javax.persistence.Table; 11 12 @Table(name="JPA_ORDERS") 13 @Entity 14 public class Order { 15 16 private Integer id; 17 18 private String orderName; 19 20 @GeneratedValue 21 @Id 22 public Integer getId() { 23 return id; 24 } 25 26 public void setId(Integer id) { 27 this.id = id; 28 } 29 30 @Column(name="ORDER_NAME") 31 public String getOrderName() { 32 return orderName; 33 } 34 35 public void setOrderName(String orderName) { 36 this.orderName = orderName; 37 } 38 39 private Customer customer; 40 41 /** 42 * 映射单项 n-1 的关联关系(Customer 和 Order,Order 中有 Customer 属性,而 Customer 中没有 Order 属性) 43 * 使用 @ManyToOne 来映射多对一的关联关系 44 * 使用 @JoinColumn 来映射外键 45 * 可以使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略 46 */ 47 @JoinColumn(name="CUSTOMER_ID") 48 @ManyToOne(fetch=FetchType.LAZY) 49 public Customer getCustomer() { 50 return customer; 51 } 52 53 public void setCustomer(Customer customer) { 54 this.customer = customer; 55 } 56 57 }
2)单向多对一的保存(persist):保存多对一时,建议先保存 1 的一端,后保存 n 的一端,这样不会多出额外的 UPDATE 语句
3)获取操作(find):默认情况下使用左外连接的方式来获取 n 的一端的对象和其关联的 1 的一端的对象,可以使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略
4)删除操作(remove):不能直接删除 1 的一端,因为有外键约束
5)修改操作:
2.映射单向 1-n 的关联关系 Customer :Order 1 : n,Customer 中有 Order 的 Set 集合属性,Order 中没有 Customer的属性
1)在 Customer 中添加 Order 的 Set 集合属性,并映射 1-n 关联关系,重新生成数据表
2)保存操作(persist):总会多出 UPDATE 语句,n 的一端在插入时不会同时插入外键列
3)查询操作(find):默认使用懒加载
4)删除操作(remove):默认情况下,若删除 1 的一端,会先把关联的 n 的一端的外键置空,然后再进行删除,可以通过 @OneToMany 的 cascade 属性修改默认的删除策略(CascadeType.REMOVE 为级联删除)
3.映射双向多对一的关联关系(注:双向多对一 同 双向一对多)
1)实体:Customer 中有 Order 的 Set 集合属性,Order 中有 Customer 的属性,注两个实体映射的外键列必须一致,都为 CUSTOMER_ID
2)保存操作(persist):
4.映射双向一对一的关联关系
1)实体:Manager 和 Department ,一个部门有一个经理,一个经理管一个部门
2)创建 Manager 类和 Department 类,Manager 类中有 Department 的引用,Department 中有 Manager 的引用,由 Department 来维护关联关系(实际上双向 1- 1 双方均可以维护关联关系),使用 @OneToOne 来映射 1-1 关联关系。添加必要注解,生成数据表。
3)保存操作:
4)查询操作:
5.映射双向多对多的关联关系
1)实体:Item 和 Category ,一个类别有多个商品,一个商品对应多个类别。双方都包含对方的 Set 集合。创建实体类,添加对应的注解,生成数据表。
2)保存操作:
3)查询操作:
----------------------------------------------------------------------------------------------------------------
相关链接:
JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 1
JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 3