Hibernate 常用的数据库操作方法

1. 前言

本节课程聊聊如何使用 Session 完成基本数据操作。通过本课程,你将学习到:

  • 如何实现查询;
  • 如何实现保存和更新。

2. 基本数据操作方法

Session 中提供了很多方法,协助开发者快速完成基本的增加、删除、修改、查询(CRUD) 等基本数据操作。

2.1 查询

Session 提供了 2 个语义很明确的查询方法:

  • get() 方法;
  • load() 方法。

有选择,就会有比较。但,请先不用着急区分两者差异性,试着用用。
跑之前先学会走吗?

查询之前,可预先在表中多添加几条数据!避免池塘没鱼,捕不到鱼还不停怀疑自己,伤情绪。

get() 方法有很多重载,选择其中一个方法:

public Object get(Class  clazz, Serializable id);  
  • 参数一: 指定待查询的 PO 对象的类型;
  • 参数二: 指定一个实现 Serializable 接口的对象,充当查询条件,一般是主键

编写 get() 方法的查询测试实例:

@Test    
public void testGet() {
    //会话对象
    Session session = sessionFactory.openSession();
    // 事务对象
    Transaction transaction = null;
    try {
        // 打开事务
        transaction = session.beginTransaction();
        //查询学号为1的学生
        Student stu=(Student)session.get(Student.class, new Integer(1));
        assertEquals("男", stu.getStuSex());
        transaction.commit();  
    } catch (Exception e) {
        transaction.rollback(); 
    } finally {
        session.close();
    } 
}  

编写 load() 方法的查询测试实例:

@Test    
public void testLoad() {
    //会话对象
    Session session = sessionFactory.openSession();
    // 事务对象
    Transaction transaction = null;
    try {
        // 打开事务 
        transaction = session.beginTransaction();
        //查询学号为1的学生
        Student stu=(Student)session.load(Student.class, new Integer(2));
        assertEquals("男", stu.getStuSex());
        transaction.commit();  
    } catch(Exception e) {
        transaction.rollback(); 
    } finally {
        session.close();
    }
}  

测试代码和上面没有很明显区别,结果也没有什么不同。

这两个方法从测试角度暂时无法区分,但本质上还是有很大区别。

2.2 更新、删除

添加数据的代码前面课程中已经使用多次,现在讨论更新、删除。更新、删除的前提条件:

  • 更新、删除数据一定是数据库中的数据;
  • 更新、删除包括一个前置操作,查询操作。

Session 提供了 public void delete(Object obj) 方法用来删除数据。

编写删除测试实例,先查询,再删除:

@Test    
public void testDelete() {
    Session session = sessionFactory.openSession();
    Transaction transaction = null;
    try {
        transaction = session.beginTransaction();
        //查询学号为1的学生
        Student stu=(Student)session.load(Student.class, new Integer(1)); 
        System.out.println(stu);
        session.delete(stu);
        transaction.commit();
    } catch (Exception e) {
        transaction.rollback();
    } finally {
        session.close();
    } 
}  

运行后,结果很明显,数据库中数据被删除。

事务问题:

事务是一个较复杂的主题(后有专题课程),原生 JDBC 中,事务管理方式有:

  • 数据库管理;
  • JDBC API 管理。

Hibernate 提供了 Transaction 对象,用来对事务进行管理。
默认:autoCommit=false,意思是底层 JDBC 把事务交给 Hibernate 管理。
查询时,可以忽略事务。使用 Hibernate 进行增、删、改时。须显示调用 Transactioncommit()rollback() 方法。

Session 提供了 public void update(Object object) 方法用于数据更新。

编写更新的测试代码:

@Test    
public void testUpdate() {
    //会话对象
    Session session = sessionFactory.openSession();
    // 事务对象 
    Transaction transaction = null;
    try {
        // 打开事务
        transaction = session.beginTransaction();
        //查询学号为1的学生
        Student stu=(Student)session.load(Student.class, new Integer(1));
        stu.setStuName("session同学");
        session.update(stu);
        transaction.commit(); 
    } catch (Exception e) {
        transaction.rollback();  
    } finally {
        session.close();
    }
}  

结果没有什么意外,在程序中修改的数据通过 update() 方法同步到数据库。

如果查询 API 文档,会发现除了这些语义上很明确的方法外,还有其它几个方法

  • public void saveOrUpdate(Object object);
  • public Object merge(Object object);
  • public void persist(Object object);

可以使用测试方式得到基本结论,如编写一个添加数据的实例时,使用 save、saveOrUpdate、persist 都可达到相同结果。

@Test
public void testAdd() {
    Session session = sessionFactory.openSession();
    // 事务对象
    Transaction transaction = null;
    try {
        // 打开事务
        transaction = session.beginTransaction();
        //添加新学生
        Student stu=new Student("慕课网", "男");
        //可换成saveOrUpdate方法,save方法
        session.persist(stu);
        transaction.commit();
    } catch (Exception e) {  
        transaction.rollback(); 
    } finally {
        session.close();
    } 
}  

本节课,只从语义层面做区分,其内在差异性留到后续课程中慢慢揭晓,算是留下一个悬念。

休息一下,小结一下:

  • Get()、Load()方法可用于查询;
  • Save()可用于添加;
  • Update()可用于更新数据;
  • Delete()可用于删除;
  • saveOrUpdate()有两重性,没有数据时添加数据,有数据时更新数据;
  • persist()方法可用于更新、添加数据;
  • merge()方法可用于更新、添加数据。

是不是有点上头了,心累呀!Hibernate 不地道呀,搞出这么多方法,这是要逼得有选择困难症的人哭,其实每一个方法都有特定的应用场景,Hibernate 总是体贴入微的想着为开发者解决每一种开发场景的需求。

记住刚开始说的,抓住主分支(知道层面),不管细节(内部机制层面)。

2.3 保存大对象

能不能把一张图片保存到数据库?

答案是明确的。

真实应用场景中不会这么做。
数据库中只会保存图片路径,具体的图片文件会存储在文件服务器中。

Hibernate 支持的大对象有:

  • Clob:文本大对象;
  • Blob:二进制数据大对象。

现在为每一个学生保存个人图片:

  1. student 类中添加 stuPic 属性(注意类型):
  private Blob stuPic;
  1. 编写测试实例:
  @Test
  public void testInsertPic() {		
  	//会话对象
  	Session session = sessionFactory.openSession();
  	// 事务对象
  	Transaction transaction = null;
  	try {
          // 打开事务
          transaction = session.beginTransaction();
          //添加新学生
          Student stu=new Student("MK", "男");
          InputStream is=new FileInputStream("pic.png");
          Blob stuPic=Hibernate.getLobCreator(session).createBlob(is, is.available());
          stu.setStuPic(stuPic);
          session.merge(stu);
          transaction.commit();
  	} catch (Exception e) {
  		transaction.rollback();
  	} finally {
  		session.close();
  	}
  }

如果要保存文本大对象,则使用如下代码:

Clob c=Hibernate.getLobCreator(session).createClob("我是中国人……");

执行结果,不出意外,数据保存成功。

不要试着把很多图片直接保存到数据库中,图片的存储与查询会比较慢,会严重拖累数据库性能。另外数据库的体积也会变得臃肿不堪,现在可是一个以瘦为美的世界!

如何读取数据库中保存的图片?
相信你一定能找到答案。

3. 总结

本节课程以体验式的方式感受了 Session 为开发者提供的常用方法。

对于类似的操作,Hibernate 会有备选方法选择,其内在的具体细节将在后续课程一一揭晓。

不要质疑 Hibernate 为什么要提供看似雷同的方法,真实场景中的需求要比 Hibernate 所能想到的更复杂。Hibenate 只是想以周全的态度为开发者保驾护航。

Hibernate 对开发者爱得深,细言碎语也就多!!