持久化类和主键

持久化类

  1. 持久化类的概念
    • 相当于一个Java类(咱们编写的JavaBean),这个Java类与表建立了映射关系就可以成为持久化类。持久化类 = JavaBean + 映射(xxx.hbm.xml)。
  2. 持久化类编写规范
    • 提供一个无参public访问控制符的构造器:底层需要进行反射。
    • 提供一个标识属性,映射数据表主键字段:唯一标识OID。数据库中通过主键。Java对象通过地址确定对象。持久化类通过唯一标识OID确定记录。
    • 所有属性提供public访问控制符的set或者get方法。
    • 标识属性应尽量使用基本数据类型的包装类型。
    • 基本上与JavaBean的编写规范一致

主键

  1. 自然主键
    • 对象本身的一个属性。例如创建一个人员表,每个人都有一个身份证号(唯一的)。使用身份证号作为表的主键。即自然主键。(开发中不会使用这种方式)
  2. 代理主键
    • 不是对象本身的一个属性。例如创建一个人员表,为每个人员单独创建一个字段。用这个字段作为主键,这个字段没有任何实际含义只用来当主键。即代理主键。(实际开发中使用)
  3. 主键生成策略
    • increment:适用于short,int,long类型作为主键,先查询主键的已有最大值,再最大值+1。不能在集群环境下或者有并发访问的情况下使用。
    • identity:适用于short,int,long类型作为主键,采用的是数据库底层的自动增长机制,所选的数据库必须支持自动增长机制才可以,例如MySQL支持自动增长机制,Oracle则不支持。
    • sequence:适用于short,int,long作为主键。底层使用的是序列的增长方式。所选的数据库必须支持序列增长才可以,例如Oracle支持序列增长,MySQL则不支持。
    • uuid:适用于char,varchar类型的作为主键。使用随机的字符串作为主键。(实际开发中经常使用)
    • native:适用于short,int,long类型作为主键,采用本地策略。根据底层的数据库不同,自动选择适用于该种数据库的生成策略。(实际开发中经常使用)
    • assigned:取消Hibernate自动管理主键,自己手动设置主键。

持久化对象

持久化对象的三个状态

  1. 瞬时态:Transient Object
    • 没有持久化标识OID, 没有被纳入到Session对象的管理。
  2. 持久态:Persistent Object
    • 有持久化标识OID,已经被纳入到Session对象的管理。
  3. 脱管态:Detached Object
    • 有持久化标识OID,没有被纳入到Session对象的管理。
  4. 案例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Test
    public void test3(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //持久化Student对象
    //进入瞬时态,没有持久化标识OID, 没有被纳入到Session对象的管理。
    Student s = new Student();
    s.setS_name("小明");
    //使用session保存持久化对象s,把s对象也保存到session的缓存中。
    //进入持久态,有持久化标识OID, 被纳入到Session对象的管理。
    Serializable id = session.save(s);
    tr.commit();
    //进入脱管态,有持久化标识OID,没有被纳入到Session对象的管理。
    session.close();
    }

Hibernate持久化对象的状态的转换

  1. 瞬时态
    • 获取瞬时态对象:User user = new User()
    • 瞬时态——>持久态:save()/saveOrUpdate()
    • 瞬时态——>脱管态:user.setId(1)
  2. 持久态
    • 获取持久态对象:get()/load()
    • 持久态——>瞬时态:delete()(进入特殊的状态删除态:Hibernate中不建议使用)
    • 持久态——>脱管态:session.[close()/evict()/clear()]
  3. 脱管态
    • 获取脱管态对象:User user = new User();user.setId(1);(不建议直接获取脱管态对象)
    • 脱管态——>持久态:update()/saveOrUpdate()/lock()
    • 脱管态——>瞬时态:user.setId(null)
  4. 注意:持久态对象具有自动更新数据库的能力!!!!!!!!

Session的一级缓存

Session的一级缓存机制(重点)

  1. 缓存的概念
    -其实就是一块内存空间,将数据源(数据库或者文件)中的数据存放到缓存中。再次获取的时候 ,直接从缓存中获取,可以提升程序的性能!
  2. Hibernate的缓存机制
    • 一级缓存:自带的不可卸载的。一级缓存的生命周期与session一致。一级缓存称为session级别的缓存。
    • 二级缓存:默认没有开启,需要手动配置才可以使用的。二级缓存可以在多个session中共享数据,二级缓存称为是sessionFactory级别的缓存。
  3. Session对象的缓存(一级缓存)概述
    • Session接口中,有一系列的java的集合,这些java集合构成了Session级别的缓存(一级缓存).将对象存入到一级缓存中,session没有结束生命周期,那么对象在session中存放着。
    • 内存中包含Session实例——>Session的缓存(一些集合)——> 集合中包含的是缓存对象!
  4. 证明一级缓存的存在
    • 在同一个Session对象内连续进行两次查询,只有第一次有sql语句,后面的没有sql语句,这是因为第一次从数据库获取数据已经把对象缓存到session中了,后面的只要从session缓存中直接获取数据即可。
  5. Hibernate框架是如何做到数据发生变化时进行同步操作的呢?
    • 使用get方法查询一个对象,然后设置该对象的一个属性,发现数据库的数据也改变了
    • 利用快照机制来完成

快照机制

  • 利用快照机制来保存自动更新数据库的能力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * 测试快照机制
    */
    @Test
    public void test4(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //获取持久化对象
    //先查询到对象,把查询结果保存到Student对象中,也缓存到session的缓存区域中和session的快照区域
    Student s = session.get(Student.class,"402881e763fe56140163fe5618a40000");
    //重新设置名称
    s.setS_name("小张"); //修改session缓存区域中的数据
    /*提交之前:自己比对一下缓存区域和快照区域的数据是否一致,如果是一致的,没问题
    * 如果不一致,修改数据库中的对应值,同时把快照区域的值更新。
    * */
    tr.commit();
    //session销毁了缓存,所有内容都没了
    session.close();
    }

控制Session的一级缓存

  1. Session.clear():清空缓存
  2. Session.evict(Object entity):清空指定对象的缓存
  3. Session.flush():刷新缓存

Hibernate中的事务与并发

事务

  1. 事务的概念
    • 事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全都失败。例如银行转账。
  2. 事务的特性
    • 原子性 :事务不可分割。
    • 一致性 :事务执行的前后数据的完整性保持一致。
    • 隔离性 :一个事务执行的过程中,不应该受到其他事务的干扰。
    • 持久性 :事务一旦提交,数据就永久保持到数据库中。
  3. 如果不考虑隔离性会引发的问题
    • 脏读:一个事务读到了另一个事务未提交的数据.
    • 不可重复读: 一个事务读到另一个事务已提交的update数据,导致多次查询结果不一致。
    • 虚读:一个事务读到了另一个事务已经提交的insert数据,导致多次查询结果不一致。
  4. 通过设置数据库隔离级别来解决上述问题
    • 未提交读:以上的读的问题都有可能发生。
    • 已提交读:避免脏读,但是不可重复读,虚读都有可能发生。
    • 可重复读:避免脏读,不可重复读.但是虚读是有可能发生。
    • 串行化:以上读的情况都可以避免。
  5. 在Hibernate框架中设置隔离级别
    • 通过在hibernate.cfg.xml的配置文件中,通过hibernate.connection.isolation标签来配置
    • 1:Read uncommitted isolation
    • 2:Read committed isolation
    • 4:Repeatable read isolation
    • 8:Serializable isolation

丢失更新

  1. 丢失更新的概念
    • 不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。
    • 两个事务同时对某一条记录做修改,就会引发丢失更新的问题。
      • A事务和B事务同时获取到一条数据,同时再做修改
      • 如果A事务修改完成后,提交了事务
      • B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响,B事务会覆盖掉A事务的修改操作,使A事务的操作丢失。
  2. 解决方案
    • 悲观锁(不推荐)
      • 采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加for update子句
        • 当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。
        • 只有当A事务提交后,锁释放了,其他事务才能操作该条记录
    • 乐观锁
      • 采用版本号的机制来解决的。会给表结构添加一个字段version=0,默认值是0
        • 当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version=1.
        • 当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。
  3. 使用Hibernate框架解决丢失更新的问题
    • 悲观锁(不推荐)
      • 使用session.get(Customer.class, 1,LockMode.UPGRADE) 方法
    • 乐观锁
      • 1.在对应的JavaBean中添加一个属性,名称可以是任意的。例如:private Integer version;生成对应的get和set方法。
      • 2.在映射的配置文件中,提供<version name="version"/>标签即可。

绑定本地的Session

  • 在Hibernate框架中提供了ThreadLocal的方式来传递Session对象,使用Session对象来开启事务
    1. 修改hibernate.cfg.xml配置文件
      • <property name="hibernate.current_session_context_class">thread</property>
    2. 修改HibernateUtil的工具类,使用SessionFactory的getCurrentSession()方法,获取当前的Session对象。并且该Session对象不用手动关闭,线程结束了,会自动关闭。
    3. 注意:getCurrentSession()方法必须配置后才可以使用。
    4. 流程:在业务层从ThreadLocal获取Session对象,然后开启事务,在捕获异常中回滚事务,不需要在finally块中手动关闭session了。在数据层直接从ThreadLocal获取Session对象,然后把相应对象保存到Session对象中即可。

Hibernate框架的查询方式

Query查询接口

  1. 查询所有数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * 测试Query的查询接口,查询所有数据
    * 持久化类相当于表
    */
    @Test
    public void run1(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //查询方式HQL:相当于select * from user
    Query query = session.createQuery("from User");
    //查询
    List<User> list = query.list();
    //打印查询结果
    for (User user : list) {
    System.out.println(user);
    }
    tr.commit();
    session.close();
    }
  2. 通过条件查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 测试Query的查询接口,条件查询方式一
    * 持久化类相当于表
    */
    @Test
    public void run2(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //查询方式HQL:相当于select * from user where u_id = 1;
    Query query = session.createQuery("from User where id = ?");
    //设置?参数的值,?的下标从0开始
    query.setInteger(0, 1);
    //查询
    List<User> list = query.list();
    //打印查询结果
    for (User user : list) {
    System.out.println(user);
    }
    tr.commit();
    session.close();
    }
  3. 通过:字符串条件查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 测试Query的查询接口,条件查询方式二
    * 持久化类相当于表
    */
    @Test
    public void run3(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //查询方式HQL:相当于select * from user where u_name="张三";
    Query query = session.createQuery("from User where name like :a");
    //设置a参数的值
    query.setString("a", "%三");
    //查询
    List<User> list = query.list();
    //打印查询结果
    for (User user : list) {
    System.out.println(user);
    }
    tr.commit();
    session.close();
    }

Criteria查询接口(适合条件查询)

  1. 查询所有记录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
    * 测试Criteria查询接口(适合条件查询)
    */
    @Test
    public void run1(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //先获取到Criteria接口
    Criteria criteria = session.createCriteria(User.class);
    //没有添加条件,查询所有数据select * from t_user
    List<User> list = criteria.list();
    for (User user : list) {
    System.out.println(user);
    }
    tr.commit();
    session.close();
    }
  2. 多条件查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 测试Criteria查询接口(适合条件查询)
    */
    @Test
    public void run2(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //先获取到Criteria接口
    Criteria criteria = session.createCriteria(User.class);
    //select * from t_user where u_age > 18 and like '%测试%';
    //criteria是Hibernate框架提供的条件查询对象,想传入条件需要使用工具类Restrictions
    //Restrictions提供静态方法拼接查询条件
    criteria.add(Restrictions.gt("age",18));
    //继续添加条件
    criteria.add(Restrictions.like("name","%测试%" ));
    List<User> list = criteria.list();
    for (User user : list) {
    System.out.println(user);
    }
    tr.commit();
    session.close();
    }