Hibernate入门三
使用Hibernate最大的目的就是对数据库进行crud操作。在讲解之前,我们需要了解持久化以及持久化对象的三种状态。
什么是持久化
持久化就是把内存中的数据永久的存储到数据库中
什么是持久化类
持久化类就是Java类与数据库建立的一种映射关系,并把这个类称做持久化类。实际上,就是一张数据表中的字段与类中的属性字段相对象的类。
持久化类的编写规范
1.需要提供无参数的构造方法
因为Hibernate通过反射机制生成类的实例
2.需要私有属性,并提供对应的get和set方法
因为Hibernate底层会对获取到的数据惊醒封装
3.属性尽量使用包装类类型
区分空和null
4.类中必须存在一个唯一标识 OID与表中的主键对应
Hibernate利用这个这个唯一标识来区分内存中是否是同一个持久化类
5.不要使用final修饰类
因为Hibernate有加载延迟机制,这个机制会产生代理对象。代理对象是通过字节 码增强技术来完成的,其实就是通过产生子类的方式,如果使用final修饰持久化类,这个机制就会失败。加载延迟机制是一种优化的手段。
Hibernate主键生成策略
主键的类型
- 自然主键
把具有业务含义的字段作为主键,称为自然主键。比如,客户的姓名(必须保证姓名不重复,唯一,非空),这类主键往往会因为业务的改变重新设计表,造成数据库维护困难。- 代理主键
把具有非业务字段作为主键,称为代理主键。通常是ID,类型是整数类型(比字符串节省空间)- Hibernate提供了几个内置主键生成策略
1.increment:用于short int long类型。以自动增长的方式,每次增量是1。
2.identity:采用底层数据库本身提供的主键生成标识,前提是数据库支持自动增长的类型。
3.sequence:Hibernate根基底层序列生成标识符,前提是数据库支持序列。
4.native:根据底层数据库生成能力来判断生成identity、sequence、hilo中的一种。适合跨数据库开发。
5.uuid:采用128位uuid算法生成标识符
6.assigned:由Java程序负责生成标识符。如果不指定id的generator属性,默认使用该属性。适合自然类型。
Hibernate持久化对象的三种状态
- 瞬时态
也称为临时态,托管态,实例是new出来的。不存在唯一标识OID,也没有和Hibernate Session关联 - 托管态
也称为离线态,游离态。当某个持久态状态的实例与session失去关联的时候就变成为托管态。但是,托管态对象仍然存在唯一 标识OID,与数据库中的数据存在关联,只不过托管态对象发生改变的时候,Hibernate无法检测到。 - 持久态
对象存在OID,并且与Session关联。值得注意的是,持久态对象在是事务还没有提交之前变成持久态的。
下面通过列举例子来说明上述内容
@Test public void demo() { SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); // 瞬时态,此时没有OID,也没有与session关联 User user = new User(); user.setUsername("robin"); Serializable save = session.save(user); // 持久态 transaction.commit(); session.close(); sessionFactory.close(); // 托管态,此时session关闭,没有与session关联,但是有OID System.out.println(user); }
转换关系
瞬时态
1.瞬时态 -- 持久态
调用save()或者saveOrUpdate()
2.瞬时态 -- 托管态
为瞬时态添加 OID
持久态:可以从Session get() load()方法获取,或者Query对象等
1.持久态 -- 瞬时态
session.delete()删除持久态化对象
2.持久态 -- 托管态
session.evict()、clear()、close()方法。evict()用于清除一级缓存中的某一个对象,clear()用于清除一级缓存中的所有对象, close()关闭session,并且清除一级缓存。
托管态
1.托管套 -- 持久态
执行update()、saveOrUpdate() lock()等
2,托管态 -- 瞬时态
把对象的OID设置为null
持久态对象可以自动更新数据库
依赖Hibernateu一级缓存
@Test public void demo() { Session session = null; Transaction transaction = null; try { session = HibernateUtil.getSession(); transaction = session.beginTransaction(); User user = session.get(User.class, 2); user.setUsername("Robin"); // session.update(user); 这句话可以省略,因为持久态的对象会自动更新数据库 transaction.commit(); } catch (Exception e) { transaction.rollback(); } }}
Hibernate一级缓存
缓存是计算机为了提高读取速度而设计的。通常介于应用程序和永久性存储介质之间。提高应用的运行能力,通常是内存。
Hibernate缓存分为一级缓存和二级缓存(redis替代),一级缓存是Hibernate内置缓存,不能删除。
Hibernate一级缓存实际上就是session缓存。Session缓存是一个内存空间,存放相互管理的Java对象。在使用对象查找的时候,会使用OID在Hibernate一级缓存中进行查找,如果查找到了,就返回此对象,否则,就会去数据库查找与之相同OID的对象。
只要session实例没有结束生命周期,存放在它上面的缓存也不会消失。
一级缓存的特点 当应用程序调用Session的save()、update、saveOrUpdate()的时候,Session缓存中没有相应的对象的时候,Hibernate会自动的从数据库中提取对应的数据加载到一级缓存中 当应用程序调用Session load()、get(),以及Query中的、list()、iterator()的时候,Hibernate首先会从以及缓存中寻找对象,如果存在则不需要去数据库中查找。,如果没有则从数据库中获取并加载到一级缓存中 当调用Session.close()的时候,session缓存会被清空。
测试一级缓存
观察控制台输出的SQL语句
@Test public void demo() { SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); /** * 查询两次 */ /** * 第一次执行你会查询数据库 * 第二次查询只会去缓存中寻找 */ User user = session.get(User.class, 1); User user1 = session.get(User.class, 1); transaction.commit(); session.close(); sessionFactory.close(); }
一级缓存内部结构
Hibernate向一级缓存中存储数据的时候,会拷贝一份数据放在Hibernate快照区,当使用事务提交之后,会根据OID比较一级缓存中的数据和快照区中的数据是否一致,如果不一致,则执行更新操作,并且同步快照区的数据,否则则不执行任何操作。
Hibernate对事务的控制
- 什么是事务?
通常对数据库的操作过程中,一项事务会有一条或者多条SQL语句,只有当所有的语句正常执行的时候,所有的操作才会正常完成。如果中间出现异常现象,那么所有的语句执行都不成功。换句话说,对于一组操作,要么全部成功,要么全部失败。 - 事务的四个基本特性
原子性 一致性 隔离行 持久性
原子性:对事务的操作,要不全部成功,要么全部失败。
一致性:事务完成的时候,必须使所有的数据保持一致。
隔离性:一个事务不能被另外一个事务干扰。
持久性:一旦提交了事务,对数据库进行了操作,所有的操作就不可逆。永久性的被改变。 - 事务并发引发的问题
脏读 不可重复读 幻读(虚读)
脏读:一个事务读取到另一个未提交事务的数据
不可重复读:一个事务中读取到另外一个事务已提交的update数据,造成同一个事务中多次读读取数据不一致
幻读:一个事务中读取到另外一个事务已提交的insert数据,造成同一个事务中多次读读取数据不一致 - 事务的隔离级别
读未提交(read uncommitted 1级):允许读取还未提交的数据,会造成脏读,不可重复读,幻读
已提交读(read committed 2级):允许在事务并发时,读取已经提交的数据,可防止脏读
可重复读(repeatable read 4级):对相同字段的多次读取结果是一样的,可以防止脏读,不可重复读
幻读(serializable 8级):提供严格的事务隔离,可防止脏读,不可重复读,幻读
Hibernate事务管理
- 设置事务隔离级别(在核心配置文件中配置)
4 在业务层处理事务的方法
1.在业务层中获取session,把它传递给持久层
2.使用ThreadLocal将业务层获取到的session绑定到当前线程中去。然后到业务层获取的时候获取当前线程的session.thread sessionFactory.getCurrentSession() 可以不需要关闭session,线程结束之后会自动关闭 thread: Session对象的生命周期与本地线程绑定 jta:Session对象的生命周期与JTA事务绑定 managed:Hinernate委托程序来管理Session对象的生命周期
Hibernate其它的API
- Query
- Criteria
- SQLQuery
Query是面对对象的Hibernate查询操作。通过session.createQuery(),传递一个HQL语句,获取Query对象。
@Test public void demo() { Session session = null; Transaction transaction = null; try { session = HibernateUtil.getSession(); transaction = session.beginTransaction(); /** * 创建Query * 调用对象的方法 */ Query query = session.createQuery("from User"); List list = query.list(); for (User user : list) { System.out.println(user); } transaction.commit(); } catch (Exception e) { transaction.rollback(); } }
Criteria是一个完全面对对象,可扩展的条件查询API,它不需要考虑数据库底层的实现,以及SQL语句的编写。它是Hibernate核心查询对象,又称为OBC(Object By Criteria)
@Test public void demo() { Session session = null; Transaction transaction = null; try { session = HibernateUtil.getSession(); transaction = session.beginTransaction(); /** * Criteria * 创建Criteria对象 * 调用方法 */ Criteria criteria = session.createCriteria(User.class); List list = criteria.list(); for (User user : list) { System.out.println(user); } transaction.commit(); } catch (Exception e) { transaction.rollback(); } }
SQLQuery用来接收SQL语句,需要我们手动封装数据
@Test public void demo03() { Session session = null; Transaction transaction = null; try { session = HibernateUtil.getSession(); transaction = session.beginTransaction(); /** * 对象 * 调用方法 */ String sql = "select * from `tb_user`"; SQLQuery sqlQuery = session.createSQLQuery(sql); sqlQuery.addEntity(User.class); List list = sqlQuery.list();// List