hibernate day02
本节内容
1.实体类的编写规则
2. hibernate主键生成策略
3 实体类操作
4 hibernate的一级缓存
5 hibernate的事务操作
6 hibernate其他的api(查询)
一、实体类编写规则
持久化类:hibernate是一个持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久性的存储到关系型数据库中。那么什么是持久化类:持久化类指的是一个java类与数据库表建立了映射关系,那么这个类称为持久化类。
持久化类的编写规则:
1.持久化类需要提供无参数的构造方法。因为hibernate底层需要使用反射生成类的实例
2.持久类中的属性需要私有化,对私有属性提供get与set方法。因为在hibernate底层会将查询到的数据进行封装
3.持久化类的属性尽量使用包装类的类型。因为基本类型和包装类型的默认值不同,包装类的类型语义更清晰而基本数据类型不容易描述。
4.持久化类要有一个唯一标识OID与表的主键对应
5.持久化类尽量不要使用final修饰.
二、hibernate主键生成策略
主键:
自然主键:把具有业务含义的字段设置为主键,称为自然主键:比如用户信息表中有一个用户姓名,使用姓名作为主键。
代理主键:把不具备业务含义的字段作为主键,称为代理主键,例如id,通常为整数类型。
hibernate要求实体类里面有一个属性作为唯一值,对应表主键,主键可以不同生成策略
hibernate提供了几个内置的主键生成策略
三、实体类操作
对实体类进行CRUD操作
添加操作:
使用session中的save方法:
// 添加操作 Shop shop = new Shop (); shop.setS_name ("面条"); shop.setS_describe ("意大利面"); shop.setS_stock (123); shop.setS_price (10.0); session.save (shop);
根据id查询:
// 第一个参数:实体类的class 第二个参数:id值 Shop shop1 = session.get (Shop.class,1);
修改操作:
1.首先根据id查询,然后修改
// 修改操作 // 第一个参数:实体类的class 第二个参数:id值 Shop shop1 = session.get (Shop.class,1); // 修改查询到的数据 shop1.setS_name ("披萨"); // 进行修改:到shop1对象中找到id根据id进行修改 session.update (shop1);
删除操作;
先查询后删除:
// 删除操作 Shop shop2 = session.get (Shop.class,1); session.delete (shop2);
实体类对象状态:
hibernate为了更好的来管理持久化类,特将持久化类分为了三中状态:瞬时态、持久态、脱管态。
1.瞬时态:
瞬时态称为:临时态或者自由态,瞬时态的实例是由new命令创建的,开辟内存空间的对象,不存在主键值,没有与hibernate Session关联,在数据库也没有记录,与数据库没有关系。
2.持久态
持久态的对象存在id主键值,并且相关联的session没有关闭,在数据库中有对应的记录,没条记录只对应唯一的持久化对象,持久态对象是在事务没有提交之前变成的持久态。
3.脱管态
托管态也称为离线态或者游离态,当某个持久化状态的实例与session的关联被关闭时,就变成了托管态,托管态的对象存在id主键值,并与数据库的数据存在关联,但是没有了session关联。发生改变hibernate不能检测。
四、Hibernate的一级缓存
1.什么是缓存
缓存是计算机领域非常通用的一个概念。它介于应用程序与永久性数据存储源(如磁盘文件或者数据库)之间,其作用就是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是从数据源存储中数据拷贝。缓存的物理介质通常是内存。
hibernate的缓存分为一级缓存和二级缓存,hibernate的这两级缓存都位于持久化层,存储的都是数据库的备份。其中一级缓存为Hibernate的内置缓存,不能被卸载。
2.什么是hibernate的一级缓存
hibernate的一级缓存就是指Session缓存,Session缓存就是一块内存空间,用来存放相互管理的Java对象,在使用hibernate查询对象的时候,会首先使用对象属性的OID 值在,hibernate的一级缓存中进行查找,如果找到匹配的IOD值对象,就直接将该对象返回,不会再查询数据库,如果没有找到相同的OID值的对象,则会去数据库查询,当从数据库中查到后,将该数据信息保存到一级缓存中。hibernate的一级缓存的作用高就是为了减少对数据库的访问次数。
hibernate一级缓存的特点:
当应用程序调用Session接口中的save、update、saveOrUpdae时,如果缓存中没有相应的对象,Hibernate就会 自动把数据库查询的相应的对象保存到一级缓存中。
当调用Session接口的load、get方法,一级Query中的list、iterator方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询对应的对象,并保存到一级缓存中。
当调用session中的close方法时,Session缓存会被清空。
测试一级缓存:
// 测试一级缓存 @Test public void test1(){ //1.加载配置文件 //自动在src下查找hibernate.cfg.xml文件 Configuration configuration = new Configuration ().configure (); //2.创建一个SessionFactory SessionFactory sessionFactory = configuration.buildSessionFactory (); //3.创建一个session对象 session对象类似于connection Session session = sessionFactory.openSession (); //4.开启事务 Transaction transaction = session.beginTransaction (); // 马上发送一条sql语句查询数据库,并将数据添加到一级缓存 Shop shop1 = session.get (Shop.class,1); //没有发送SQL语句,从一级缓存中获取 System.out.println (shop1); Shop shop2 = session.get (Shop.class,1); System.out.println (shop2); // 缓存缓存的是对象的地址 System.out.println (shop1==shop2); transaction.commit (); session.close (); }
运行结果:
Hibernate: select shop0_.s_id as s_id1_0_0_, shop0_.s_name as s_name2_0_0_, shop0_.s_describe as s_descri3_0_0_, shop0_.s_stock as s_stock4_0_0_, shop0_.s_price as s_price5_0_0_ from shop shop0_ where shop0_.s_id=? com.hibernate.pojo.Shop@72fe8a4f com.hibernate.pojo.Shop@72fe8a4f true
从以上代码的运行结果上可以看出,执行第一次查询时,发送了一条sql语句,当执行第2次查询时,发现没有发送sql,直返返回了对象。第二次直接从缓存中获取的数据。
我们知道hibernate的持久态对象可以自动更新数据库,其实就是依赖于一级缓存,那么一级缓存为什么可以去更新数据库,是因为一级缓存的一块特殊区域就是快照区
一级缓存的内部结构(快照区)
hibernate向一级缓存中存放数据时,同时会复制一份数据放入到Hibernate快照区中,当使用 commit方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照区中的对象是否一致
五、hibernate的事务操作
Hibernate是对JDBC的轻量级封装,其主要功能是操作数据库,在操作数据库过程中,经常会遇到事务处理的问题,那么我们接下来就学习Hibernate的事务管理。
1.什么是事务
事务:由一条或者多条操作数据库的SQL语句组成的一个不可分割的工作单元。当事务中的所有操作都正常完成时,整个事务才能被提交到数据库,如果有一项操作没有完成,则整个事务会被回滚。
总结:一条或者一组sql语句要么全部执行成功,要么全部失败。
2.事务的四大特性(ACID)
原子性:(Atomic)表示将事务中所做的操作捆绑成一个不可分割的单元,即事务所进行的数据修改等操作。要么全部执行,要么全部不执行。
一致性:(Consistency):表示事务完成时,必须使所有的数据都保持一致状态。
隔离性:(Isolation):指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性:(Durability):持久性也称为永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。提交后的其他操作或者故障不会对其有任何影响。
3.事务的并发问题:
在实际使用事务的过程中,数据库数据要被多个用户所共同访问的,在多个事务同时使用相同的数据时,可能会发生并发的问题:
脏读:一个事务读取到另一个事务未提交的数据。
不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致在同一个事务中的多次查询结果不一致。
虚读/幻读:一个事务读取到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次多次查询结果不一致。
4.事务的隔离级别
为了避免事务并发问题的发生,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
读未提交:(Read Uncommitted 1)一个事务在执行过程中,即可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此数据。防止丢失更新
已提交读:(Read Committed 2)一个事务在执行过程中,即可以访问其他事务成功提交,新插入的数据,又可以访问成功修改的数据,读取数据的事务允许其他事务继续访问该数据,但是未提交事务将会禁止其他事务访问该行数据,有效防止脏读
可重复读:(Repeatable Read 4)一个事务在执行过程中,可以访问其他事务成功提交的数据,但是不可以访问成功修改的数据,读数据的事务将会禁止写事务(允许读),写事务则禁止任何其他事务,防止不可重复读和脏读
序列化/串行化:(Serializable 8)提供严格的事务隔离。它要求事务序列化执行。事务只能一个接一个的执行,但不能并发执行。次隔离级别可有效防止脏读,不可重复读,或者幻读。
事务隔离级别,是由数据库提供的,并不是所有数据库都支持四种隔离级别。
在使用数据库时候,隔离级别越高,安全性越高,性能越低。实际开发中,不会选择最高或者最低隔离级别。一般选择默认的级别Oracle(Read Committed) mysql(Repeatable Read)
5.hibernate中的事务管理
在Hibernate中可以通过代码来操作管理事务,如通过:Transaction tx = session.beginTransaction 开启一个事务,持久化操作后,通过 tx.commit 提交事务,如果事务出现异常,通过 tx.rollback 操作来撤销事务(事务回滚)
除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别,在hibernate.cfg.xml文件中的<session-factory>标签中进行。
<!--设置事务的隔离级别--> <property name="hibernate.connection.isolation">4</property>
设置事务的隔离级别,那么我们的事务一般不使用在dao层,而是使用在service层中,在一个service中调用多个dao中的方法来实现一个逻辑操作。
那么我们需要保证的是在service中开启事务时,使用的Session对象和Dao中多个操作使用的是同一个session对象。
有2种方式可以实现:
1 可以在业务层获取到Session,并将Session作为参数传递给Dao
2可以使用 ThreadLocal 将业务层获取到的,Session绑定到当前线程中,然后在dao层获取Session的时候,从当前线程中获取。
一般我们都使用第二种,具体的实现不需要我们来完成,hibernate内部已经做好了,我们只需要完成一段配置即可。
Hibernate5自身提供了3种管理Session对象的方法。
Session对象的生命周期与本地线程绑定。
Session对象的生命周期与JTA事务绑定
Hibernate委托程序管理 Session对象的生命周期。
在Hibernate的配置文件中,通过hibernate.current_session_context_class来指定Session的管理方式:
thread:Session对象的生命周期与本地线程绑定
jta:session对象的生命周期与JTA事务绑定
managed:Hibernate委托程序管理Session对象的生命周期。
在hibernate中配置:
<property name="hibernate.current_session_context_class">thread</property>
hibernate提供sessionFactory.getCurrentSession() 创建一个 session和ThreadLocal绑定方法。
package com.hibernate.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static SessionFactory sessionFactory; static { Configuration configuration = new Configuration ().configure (); sessionFactory = configuration.buildSessionFactory (); } // 获取当前线程绑定的会话 public static Session getCurrentSession(){ return sessionFactory.getCurrentSession (); } }
hibernate中提供的这个与线程绑定的Session可以不用关闭,当线程执行结束后,就会自动关闭。
六、Hibernate的其他API(查询)
1.Query
query代表面向对象的一个Hibernate查询操作,在hibernate中通常使用createQuery方法接收一个HQL语句,然后调用Query的list方法或者uniqueResult 方法执行查询。
HQL:Hibernate Query Language 缩写 语法很像sql,但是他是完全面向对象的。sql操作的是表,HQL操作的是实体类。
Hibernate中使用Query对象的步骤:
1.获取Hibernate的session对象
2.编写HQL语句
3.调用createQuery创建查询对象
4.如果HQL语句包含参数,则使用Query的setXXX方法设置参数
5.调用Query的list方法或者uniqueResult 方法执行查询
Session session = HibernateUtil.getCurrentSession (); Transaction transaction = session.beginTransaction (); //查询所有 // Query query = session.createQuery ("from Shop"); // List<Shop> list = query.list (); // // list.forEach ( shop ->{ // System.out.println (shop.getS_name ()); // }); // //条件查询 // Query query1 = session.createQuery ("from Shop where s_id=?0"); // // query1.setParameter (0,1); //分页查询 Query query = session.createQuery ("from Shop "); query.setFirstResult (1); query.setMaxResults (10); List<Shop> list = query.list (); list.forEach ( shop ->{ System.out.println (shop.getS_name ()); }); transaction.commit (); session.close (); }
2.Criteria
Criteria是一个完全面向对象,可扩展的条件查询API,它不用考虑数据库底层的实现,以及SQL如何编写,它是Hibernate框架的核心查询对象,Criteria查询又称为“QBC”查询,他是Hibernate的另一种对象检索方式。由于它的API比较多,使用比较繁琐,所以在我们的开发中很少有人使用。
使用步骤;
1.创建查询:CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
2.设置查询语句信息 :criteriaBuilder.createQuery
3.定义查询的From子句中能出现的类型,也可以用root.get()获取具体的某个属性:
Root root = criteriaQuery.from (Shop.class);
4.设置条件
criteriaQuery.where
5.创建Query对象并获取结果集list
Query query = session.createQuery (criteriaQuery);
List<Shop> list = query.list ();
@Test public void test4(){ Session session = HibernateUtil.getCurrentSession (); Transaction transaction = session.beginTransaction (); CriteriaBuilder criteriaBuilder =session.getCriteriaBuilder (); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery (Shop.class); Root root = criteriaQuery.from (Shop.class); criteriaQuery.select (root); Path<Integer> path = root.get("s_id"); Path<String> path1 =root.get ("s_name"); //多条件查询 Predicate predicate =criteriaBuilder.or (criteriaBuilder.equal (path,1),criteriaBuilder.equal (path1,"面条")); Predicate predicate1 =criteriaBuilder.or (criteriaBuilder.equal (path,2)); // criteriaQuery.where (criteriaBuilder.equal (root.get ("s_id"),1),criteriaBuilder.equal (root.get ("s_name"),"面条")); criteriaQuery.where (predicate,predicate1); Query query = session.createQuery (criteriaQuery); List<Shop> list = query.list (); list.forEach (shop ->{ System.out.println (shop.getS_price ()); }); }
3.SQLQuery
SQLQuery这个就比较简单了,这个借口用于接收一个sql语句进行查询,然后调用list或者uniqueResult方法进行查询,但是sql语句不会直接封装到实体对象中,需要我们手动写代码才可以封装到实体中
@Test public void test5(){ Session session = HibernateUtil.getCurrentSession (); Transaction transaction = session.beginTransaction (); //全查询 // String sql = "select * from shop"; // NativeQuery nativeQuery =session.createNativeQuery (sql,Shop.class); // // List<Shop> list = nativeQuery.getResultList (); //分页查询 String sql = "select * from shop"; NativeQuery nativeQuery = session.createNativeQuery (sql,Shop.class).setFirstResult (1) .setMaxResults (10); List<Shop> list = nativeQuery.getResultList (); transaction.commit (); list.forEach (shop ->{ System.out.println (shop.getS_name ()); }); }