传智播客-jpa与hibernate(2)-CRUD和关联关系

来源:互联网 发布:linux vim 删除一行 编辑:程序博客网 时间:2024/05/29 21:18

(传智播客ms很多关于“数据”的课程都是徐培成老师讲的,hibernate、jpa、后面还有个数据采集课程,当然还有讲解其他课程,赞一下,课讲得确实很好,尤其是课上没有半句废话--对于号称笔记狂的本人而言恨不得每个字都记下来,而且看得出确实经验丰富。当然,本人博客里提到的其他老师也都很好~最最起码的,能看出大家都很敬业。本人一直都认为,做事最重要的就是看态度。常言道:能力可以培养,态度难以改变 >_<come on!)

 

CRUD
1、下面代码中,注释中的“===>”左边是纯jpa规范的表示,右边是hibernate表示。
2、jpa与hibernate在CRUD中最显著的区别在于“U”--更新,即jpa里的merge()方法,相当于hibernate的update()。
(1)更新是将游离状态的数据对象的引用与缓存关联,使成持久态。所谓在缓存里添加一个对象,其实添加的只是引用。
(2)只调用merge()方法不会有update语句打印,因为merge()方法是在缓存里新建了一个对象的拷贝,并返回这个拷贝,merge()操作的也是这个拷贝,所以下面的代码中,c.setName("jerry2")其实并没有更新,cc.setName("jerry3")才真正被更新了。
3、EntityManager/Session的CRUD操作只是最基本最简单的操作,实际业务逻辑中更多用的是EntityManager.createQuery()--针对相应业务逻辑创建sql语句,还有EntityManager.createNamedQuery()/Session.getNamedQuery()--这样的话查询语句可以做成配置信息,方便统一修改,而hibernate自身还有一个Criteria对象,Session.createCriteria()可以创建更体现00P思想的sql。
public class TestCrud {

 private static EntityManagerFactory emf = null;
 
 @BeforeClass
 public static void iniEmf(){
  try {
   emf = Persistence.createEntityManagerFactory("jpa");
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 @Test
 public void findCustomer(){
  //em ===> session
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  //em.find() ===> session.get(), 不能产生代理
  CustomerBackup c = em.find(CustomerBackup.class, 4);
  System.out.println(c); //如果c不存在,不报错,直接返回null
  tx.commit();
  em.close();
 }
 
 @Test
 public void getReference(){
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  //em.getReference() ===> session.load()
  CustomerBackup c = em.getReference(CustomerBackup.class, 4); //返回的是代理对象
  System.out.println(c); //如果c不存在,会报找不到实体c的错误
  tx.commit();
  em.close();
 }
 
 @Test
 public void updateCustomer(){
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  //em.find() ===> session.load()
  CustomerBackup c = em.find(CustomerBackup.class, 1); //持久化数据的更新,缓存里有快照,即副本
  c.setName("jay");
  tx.commit();
  em.close();
 }
 
 @Test
 public void updateCustomer2(){
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  //em.find() ===> session.load()
  CustomerBackup c = em.find(CustomerBackup.class, 1);
  tx.commit();
  em.close();
  
  em = emf.createEntityManager();
  tx = em.getTransaction();
  tx.begin();
  //em.merge()合并 ===> session.update()
  CustomerBackup cc = em.merge(c); //将游离状态对象的引用和缓存关联,使其变为持久化状态
  //这一句放em.merge(c)前后都无所谓,因为c这时还在缓存里,而merge关联的只是引用
  c.setName("jerry2");
  //merge方法实际上是新建一个对象的拷贝,然后对这个拷贝操作,所以对merge返回的对象拷贝进行操作才能真正执行更新
  cc.setName("jerry3"); //这句才真正被更新了
  tx.commit();
  em.close();
 }
 
 @Test
 public void removeCustomer(){
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  CustomerBackup c = em.find(CustomerBackup.class, 1);
  //em.remove(c) ===> session.delete(c)
  em.remove(c);
  tx.commit();
  em.close();
 }
 
 @Test
 public void findAllCustomer(){
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  //jpql
  //只写from Customer c也可以,但是这种语法(可能)只有hibernate才支持,为了提高程序可移植性,应当完整写明
  Query q = em.createQuery("select c from CustomerBackup c");
  //q.getResultList()(jpa) ===> q.list()(hibernate)
  q.getResultList();
  tx.commit(); //这一句才在真正执行了查询,前面只是构建生成sql语句
  em.close();
 }
}

4、jpa里的@Embedded注释相当于hibernate里的component元素,是一个实体类的实体类型属性(相对于值类型属性而言,从java的角度来看,就是一个类与另一个类的组成关系)
(1)属性重写:如果实体类(例如Customer类)里有多个同一实体类型(例如Address类)的属性(例如homeAddress,comAddress),那么原有实体类型里面的属性(相对于表的字段)需要重写。
(2)空指针判断:以(1)为例,如果数据库里homeAddress或comAddress相关内容字段全为null,那么jpa直接将查找的customer对象的homeAddress或comAddress属性值置为null,而不会为这个属性new一个新对象(空间),所以更新该属性数据时,应该事先做一下空指针判断,并将指针移位,以增强程序的健壮性(当然,如果只要有一个字段值不为null,jpa就会new个新对象了~~~)。
@Test
public void findCustomer(){
 EntityManager em = emf.createEntityManager();
 EntityTransaction tx = em.getTransaction();
 tx.begin();
 Customer c = em.find(Customer.class, 1);
 Address ca = c.getComAddress();
 if(ca == null){
//空指针判断
  ca = new Address();
  c.setComAddress(ca);
//c原来指向null的 指针 转移到ca对象
 }
 tx.commit();
 em.close();
}

 

关联关系
(级联--指数据信息传播的支持程度,即使只CUD操作一个实体,相关的也会CUD。)
1、多对一和一对多
(1)@ManyToOne一方(例如Order),不加@JoinColumn的话,hibernate会自动添加一个连接字段,字段名为“many类中one类型的属性名_对端主键字段值”。
(2)查询order的话打印出的sql语句为左外连接:Order left outer join Customer。
(3)@OneToMany一方,jpa默认采用中间表来实现,所以通常情况应该指定一个连接列,但是因为在many一方中不用维护关联关系,一般由one一方维护,所以应该使用@OneToMany(MappedBy="customer")。
(4)下列代码运行后,order表的业务数据可以插入,但是负责关联关系的customer_id外键值为null,因为customer_id的值应该由有关系控制权的customer来指派。
//order.setCustomer(customer);
customer.getOrders().add(order);
entityManager.persist(customer);
//entityManager.persist(order);

 

2、一对一
(1)外键关联
public class Customer{
 @OneToOne
 @JoinColumn(name="aid",referencedColumnName="id") //"id"是指Address的id属性值
 private Address addr;
 ......
}
public class Address{
 @OneToOne(MappedBy="aid",unique=true) //加了unique=true才能保证一对一关联的双向唯一性
 private Customer customer;
 ......
}
(2)主键关联
主键关联中必有一方为自然主键,保存前必须手动为其赋值。(具体如何使用请参见相关文档,代码就不贴了,不过官方文档中不推荐这种做法)

 

3、多对多
一般是建一个中间表关联双方,表内字段为双方主键字段。其实也可以看成多对一和一对多的复合,这个就不多写了。。(手又酸了呀。。。)

原创粉丝点击