Hibernate框架进阶(上篇)
导读
前面一篇文章介绍了Hibernate框架的入门,主要是讲解Hibernate的环境搭建和简单测试,有兴趣的童鞋出门左转。本文在入门的基础上进行Hibernate的进阶讲解,分为上中下三篇,本篇为上篇,该篇主要以讲解hibernate原理为主。主要内容包括:
一、hibernate中的实体创建规则
二、hibernate主键生成策略(7种)
三、hibernate对象的三种状态
四、hibernate中的缓存
五、事务管理
六、Hibernate支持的三种数据库操作语法:HQL、Criteria、SQL
一、hibernate中的实体创建规则
hibernate中的实体类是需要与数据库中的表格进行建立映射关系的,所以我们在创建实体类时需要注意一些规则,如果你的实体类不符合这些规则可能就会无法映射成功。
1)持久化类提供无参构造函数
一般如果没有创建有参构造函数,默认就可以了。
2)成员变量私有,提供共有的get/set方法访问。需提供属性。
3)持久化类的属性,应尽量适用封装数据类型。
即数据类型是int尽量替换成Integer类型,long类型使用Long类型代替,依次类推。这样适用的好处是Integer、Long等这些封装型数据类型可以传入null值,而基本数据类型不能传入null。
4)持久化类需要提供oid,与数据库中的主键列对应。(即表格必须要有主键)
5)class之前不能使用final来修饰。
这是由于final修饰的类不能被继承,而hibernate框架会使用cglib代理生成代理对象,而被代理对象的创建是通过继承来实现的。
二、hibernate主键生成策略(7种)
主键就是唯一存在能代表这条数据唯一性的字段或字段组合。就像你的身份证一样独一无二。上面的规则中说了在hibernate中的表必须指明主键,一般主键分为自然主键和代理主键,就像你的指纹是天生的,但又可以代表你这个人,这就是自然主键,而身份证是后天专门设计,这种主键就是代理主键。实际项目中,我们几乎都是使用代理主键,即自己设计的主键作为表中每一条数据的主键。在我们的数据库中也会为你自动生成主键,比如主键自增,这就是要下面要讲的主键生成策略之一,在hibernate中有7种主键生成策略。其实大部分我们不会使用到,所以除了几个常用的,其他作为了解即可。
1)identity(sql使用) : 主键自增。由MySQL数据库来维护主键值。录入时不需要指定主键。
2)sequence(Oracle使用):Oracle数据库中的主键生成策略。
3)increment(了解)::主键自增。由hibernate来维护。每次插入前会先查询表中id最大值。+1作为新主键值。(其实存在并发问题)
4)hilo(了解): 高低位算法。主键自增。由hibernate来维护。开发时不使用。
5)native(推荐):hilo+sequence+identity ,自动三选一策略。
6)uuid:产生随机字符串作为主键。主键类型必须为String 类型。
7)assigned:自然主键生成策略。hibernate不会管理主键值,由开发人员自己录入,若忘记手动录入会发生异常。
三、hibernate对象的三种状态
在Hibernate框架中,会将实体对像分为三种状态,分别为:瞬时状态、持久化状态和游离状态。当对象在持久化下,对对象进行操作就会将变化同步到数据库中,所以叫持久化状态。
1、每种状态都有不同的标志:
瞬时状态:该对象没有id,或说id=null。即该对象主键尚未赋值。
持久化状态:该对象主键已经赋值,且与处于session绑定状态。
游离状态:该对象主键已经赋值,但没有雨session进行绑定。
具体可参考下面的实例代码说明:
1 package demo; 2 3 import org.hibernate.Session; 4 import org.hibernate.Transaction; 5 import org.junit.Test; 6 7 import domain.User; 8 import utils.HibernateUtils; 9 10 public class demo { 11 12 @Test 13 public void test(){ 14 //获得对话参数 15 Session session = HibernateUtils.openSession(); 16 Transaction tx = session.beginTransaction(); 17 //--------------------------- 18 User user = new User();//创建实体对象 19 20 session.save(user);//与会话对象session进行绑定 21 22 user.setUname("小黄人"); 23 //--------------------------- 24 //关闭资源 25 tx.commit(); 26 session.close();//关闭session意味着对象与session解绑 27 } 28 }
结论:hibernate的对象在持久化状态下才会自动持久化到数据库中,我们要面向对象操作数据库,必须保证对象处于持久化状态。
2、三种状态的相互装换
四、hibernate中的缓存
缓存是一种提高效率的方法或者说机制,而hibernate中的缓存也是用来提高数据库效率的。
扩展:快照原理
五、事务管理
1、事务(Transaction)
事务,一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
1.1事务的特性:
1)原子性:说的是在一个事务内是不可分割的,要么成功,要么失败。
2)一致性:可以理解为事务的前后数据的变化的一致性,例如把张三给李四转账500元看做一个事务,在这个事务结束后,张三的钱少了500那么李四的钱必然增加500。
3)隔离性:是指事务之间互不干扰和影响,即并发执行事务时应当按照是连续地执行、互不干扰地执行(一个接一个)。(数据库的隔离级别的设置会导致不同的隔离性,看下文)
4)持久性:简单说就是事务一旦执行成功就持久化到数据库中。
1.2事务的隔离级别:
事务的隔离级别从低到高有:(不同的隔离级别涉及不同的并发访问问题)
Read Uncommitted(读未提交):最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都可能会发生。
Read Committed(读已提交):只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
Repeated Read(可重复读)(MySQL默认级别):在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
Serialization(串行化):理想的真正的事务隔离性,事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
1.3隔离性级别导致的并发访问问题:
上面说到的隔离级别处理Serialization(串行化)都存在并发访问问题,但串行化是不允许并发访问的(这样做虽然安全,但效率也极低),所以我们一般不采用。
1)脏读(Drity Read):事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
2)不可重复读(Non-repeatable read) : 在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这导致锁竞争加剧,影响性能。
3)幻读(虚读)(Phantom Read) : 在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读仅指由于并发事务增加记录导致的问题,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。
2、hibernate设置隔离级别(一般默认就好了)
3、开启hibernate中的事务
一个很重要的问题就是,session.beginTransation()是开启事务的方法,但我们必须要保证开启事务和处理事务的session是同一个session,否则无法控制事务。所以获得session的方法不再是openSession()而是getCurrentSession(),这样获得的session就是与线程绑定的session。
所以先指定与当前线程绑定:
3.1在主配置文件hibernate.cfg.xml中配置
3.2测试
1 @Test 2 public void test2(){ 3 //获得普通session 4 Session s1 = HibernateUtils.openSession(); 5 Session s2 = HibernateUtils.openSession(); 6 //获得与线程绑定的session 7 Session session1 = HibernateUtils.getCurrentSession(); 8 Session session2 = HibernateUtils.getCurrentSession(); 9 //比较 10 System.out.println(s1==s2);//false 11 System.out.println(session1==session2);//true 12 }
注:与线下绑定的session不用再手动close,会自动回收,有时候手动关闭还会出现异常。
六、Hibernate支持的三种数据库操作语法:HQL、Criteria、SQL
1、HQL
HQL即Hibernate Query Language,是hibernate框架自己定义的数据库查询语言,是属于面向对象的数据库操作语言;
1)基本查询
1 package demo; 2 3 import java.util.List; 4 5 import org.hibernate.Query; 6 import org.hibernate.Session; 7 import org.hibernate.Transaction; 8 import org.junit.Test; 9 10 import domain.User; 11 import utils.HibernateUtils; 12 13 public class hql { 14 15 //1、使用hql进行简单查询 16 @Test 17 public void getAllUser(){ 18 //1获得session 19 Session session = HibernateUtils.openSession(); 20 //2控制事务 21 Transaction tx = session.beginTransaction(); 22 23 //3执行操作 24 //1>书写hql语句 25 // String hql = "select * from domain.User";//select*可省略,如果在该项目中只有一个User包名也可以直接省略 26 String hql = "from User"; 27 //2>根据语句创建查询对象 28 Query query = session.createQuery(hql); 29 //3>根据查询对象获得查询结果 30 /* 查询结果分为以下两种:list代表查询结果集,uniqueResult代表单个查询结果(这个结果封装成了对象) 31 * query.list(); 32 query.uniqueResult();*/ 33 List<User> list = query.list(); 34 //4>处理查询结果 35 for(User u:list) 36 System.out.println(u); 37 38 //4、关闭相关资源 39 tx.commit(); 40 session.close(); 41 } 42 }
2)条件查询
1 package demo; 2 3 import java.util.List; 4 5 import org.hibernate.Query; 6 import org.hibernate.Session; 7 import org.hibernate.Transaction; 8 import org.junit.Test; 9 10 import domain.User; 11 import utils.HibernateUtils; 12 13 public class hql { 14 15 //2、使用hql根据Uid查询User 16 @Test 17 public void getUserByUid(){ 18 //1获得session 19 Session session = HibernateUtils.openSession(); 20 //2控制事务 21 Transaction tx = session.beginTransaction(); 22 23 /*//3执行操作(方式一) 24 //1>书写hql语句 25 String hql = "from User where uid = ?"; 26 //2>查询对象 27 Query query = session.createQuery(hql); 28 //3>设置参数 29 query.setParameter(0, 1l); 30 //4>获得结果 31 User user = (User)query.uniqueResult(); 32 //5>处理结果 33 System.out.println(user);*/ 34 35 //3执行操作(方式二) 36 //1>书写hql语句 37 String hql = "from User where uid = :uid"; 38 //2>查询对象 39 Query query = session.createQuery(hql); 40 //3>设置参数 41 query.setParameter("uid", 1l); 42 //4>获得结果 43 User user = (User)query.uniqueResult(); 44 //5>处理结果 45 System.out.println(user); 46 47 //4、关闭相关资源 48 tx.commit(); 49 session.close(); 50 } 51 52 }
3)分页查询
1 package demo; 2 3 import java.util.List; 4 5 import org.hibernate.Query; 6 import org.hibernate.Session; 7 import org.hibernate.Transaction; 8 import org.junit.Test; 9 10 import domain.User; 11 import utils.HibernateUtils; 12 13 public class hql { 14 15 //2、使用hql分页查询 16 @Test 17 public void getUserByPage(){ 18 //1获得session 19 Session session = HibernateUtils.openSession(); 20 //2控制事务 21 Transaction tx = session.beginTransaction(); 22 23 //3执行操作(方式二) 24 //1>书写hql语句 25 String hql = "from User "; 26 //2>查询对象 27 Query query = session.createQuery(hql); 28 //3>设置分页信息(从0开始的2条数据) 29 query.setFirstResult(0); 30 query.setMaxResults(2); 31 //4>获得结果 32 List<User> list = query.list(); 33 //5>处理结果 34 for(User u:list) 35 System.out.println(u); 36 37 //4、关闭相关资源 38 tx.commit(); 39 session.close(); 40 } 41 42 }
2、Criteria
Criteria是一种比hql更面向对象的查询方式。
1)基本查询
package demo; import java.util.List; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import domain.User; import utils.HibernateUtils; public class Criteria { //2、使用Criteria简单查询 @Test public void getAllUser(){ //1获得session Session session = HibernateUtils.openSession(); //2控制事务 Transaction tx = session.beginTransaction(); //3执行操作 //1>获得执行操作的对象 org.hibernate.Criteria criteria = session.createCriteria(User.class); //2>获得结果 List<User> list = criteria.list(); //3>处理结果 for(User u:list) System.out.println(u); //4、关闭相关资源 tx.commit(); session.close(); } }
2)条件查询
1 package demo; 2 3 import java.util.List; 4 5 import org.hibernate.Session; 6 import org.hibernate.Transaction; 7 import org.hibernate.criterion.Restrictions; 8 import org.junit.Test; 9 10 import domain.User; 11 import utils.HibernateUtils; 12 13 public class Criteria { 14 15 //2、使用Criteria根据uid查询user 16 @Test 17 public void getUserByUid(){ 18 //1获得session 19 Session session = HibernateUtils.openSession(); 20 //2控制事务 21 Transaction tx = session.beginTransaction(); 22 23 //3执行操作 24 //1>获得执行操作的对象 25 org.hibernate.Criteria criteria = session.createCriteria(User.class); 26 27 //2>添加查询参数 28 criteria.add(Restrictions.eq("uid",2l)); 29 //3>获得结果 30 User user = (User)criteria.uniqueResult(); 31 //4>处理结果 32 System.out.println(user); 33 34 //4、关闭相关资源 35 tx.commit(); 36 session.close(); 37 } 38 39 }
常用的添加查询条件的方法:
3)分页查询
和上面的hql一毛一样,这里就不插代码了。
3、SQL
sql不用多说,还是和JDBC一样的操作语句。方法和hql基本一样,只需将hql语句换成sql语句就可以了。
4、选哪种?
面向对象地操作数据库是我们所追求的理想状态,但目前对于比较复杂的业务,面向对象的方式还会有些力不从心,所以原生的sql依然重要。
单表查询基本可以使用Criteria来查询。对于业务简单的多表查询建议使用Hql来查询,而复杂业务就最好使用严谨的sql来查询好了。