自然主键:把具有业务含义的字段作为主键,称之为自然主键。例如如果在t_user表中把username字段作为主键,其前提条件必须是:每一个用户的姓名不允许为null,不允许用户重名并且不允许修改用户姓名。代理主键:把不具有业务含义的字段作为主键,称之为代理主键。
对比一下:
瞬时态的对象由new关键字创建。
瞬时态转换为持久态:执行Session的save()或saveOrUpdate()方法。瞬时态转换为脱管态:直接为瞬时态对象设置持久化标识OID。比如直接调用user.setId()方法。所以瞬时态和脱管态的本质区别就是看OID有没有值。(都没有被Session管理)
持久化对象可以直接通过Hibernate中的Session的get()方法、load()方法,或者Query查询从数据库中获得。
执行Session的delete()方法。需要注意的是被删除的持久化对象,不建议再次使用。执行Session的evict()、close()或clear()方法。evict()方法用于清除一级缓存中的某一个对象;
close()方法用于关闭Session,清除一级缓存;
clear()方法用于清除一级缓存的所有对象。
脱管态对象无法直接获得,是由其他状态对象转换而来的。
脱管态转换为持久态:执行Session的update()、saveOrUpdate()或lock()方法脱管态转换为瞬时态:将脱管态对象的持久化标识OID设置为null。通过一段程序来看看它们之间的转换:
public class HibernateDemo01 { public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); //持久态对象,此时没有持久化标识OID,且没有被session管理 User user = new User(); user.setUserName("杨达"); //执行完save方法后该对象被放入session的一级缓存中成为持久态对象。具有了持久化标识OID,被session管理。 session.save(user); tx.commit(); //session关闭之后user变成脱管态。(具有持久化标识OID,但是没有被session管理) session.close(); sessionFactory.close(); System.out.println(user); System.out.println(user.getId()); } }结果如下:
持久态对象是可以自动更新数据库的:
public class Test { public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); User user = session.get(User.class, 1); user.setPassword("11111"); tx.commit(); session.close(); sessionFactory.close(); } }可以看看底层的sql语句如下:
可以看到它在 提交事务时自动发送了一条sql语句。更新了数据库,而程序中并没有调用update方法。
缓存是介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存。
Hibernate的缓存分为一级缓存和二级缓存,Hibernate的这两级缓存都位于持久化层,存储的都是数据库数据的备份。其中第一级缓存为Hibernate的内置缓存,不能被卸载。
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出来使用,不会再查询数据库;如果没有找到,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放在一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。只要Session实例没有被结束生命周期,存放在它缓存中的对象也不会结束生命周期。所以一级缓存也被称为Session基本的缓存。
当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询,并加到一级缓存中。
验证一级缓存:
//一级缓存验证 public class HibernateDemo03 { public static void main(String[] args) { SessionFactory sessionFactory = HibernateUtils.getSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); //根据id来查询记录,此时第一次执行查询一级缓存中还没有数据。会发送sql语句。 User user1 = session.get(User.class, 1); System.out.println(user1); //再次执行get可没有发送sql语句。 所以第二次查询到的对象是一级缓存中的对象 User user2 = session.get(User.class, 1); System.out.println(user2); //这里输出为true(比较的是对象引用的地址) System.out.println(user1 == user2); tx.commit(); session.close(); sessionFactory.close(); } }结果如下:
上述谈到Hibernate的持久态对象能够自动去更新数据库,其实就是依赖了一级缓存,那么一级缓存为什么就可以去更新数据库了? 其实是因为一块特殊的区域:快照区。
Hibernate向一级缓存中存放数据时,同时复制一份数据放入到快照区中,当提交事务时同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照区的对象是否一致,如果两个对象中的属性发生变化,则执行update,将缓存中的内容同步到数据库中,并更新快照区;如果一致,则不执行update语句。Hibernate中快照区的作用就是确保一级缓存中的数据和数据库中的数据一致。
测试原因,我将数据库中的password设置为null。所以此时该数据库表中的password字段值不再是上述的“11111”(“11111”效果也是一样的),而是null。
程序分析如下: