什么是Hibernate框架
Hibernate是数据访问的框架,对JDBC进行了封装,是针对数据库访问提出的面向对象的解决方案。
Hibernate不仅专注于从Java类到数据库表的映射,也有Java数据类型到SQL数据类型的映射,另外也提供了数据查询和检索服务。
Hibernate是ORM模块的框架,处于持久层。
设计原理:
Hibernate框架采用了ORM思想对JDBC进行了封装,是ORM思想的一种实现,解决了对象和数据库映射问题,Hibernate提供了一系列的API,允许我们直接访问实体对象,然后其根据ORM映射关系,转换成SQL并且执行,从而达到访问数据库的目的。
ORM思想:
Object Relation Mapping,即对象关系映射,将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。
ORM采用元数据来描述对象关系映射细节,元数据通常采用XML格式,并且存放在专门的对象关系映射文件中。
持久化层:
分离出的持久化层封装了数据访问的细节,为业务逻辑层提供了面向对象的API。
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
Hibernate五大对象
所有的Hibernate应用都会访问它的5个核心接口,分别如下:
1.Configuration接口: Configuration用于配置并启动Hibernate。Hibernate应用通过Configuration的实例来指定对象-关系映射文件,或通过Configuration动态配置Hibernate的属性,然后通过Configuration来创建相应的SessionFactory实例。
2.SessionFactory接口: 一个SessionFactory对应一个数据源,它是个重量级对象,不可随意生成多个实例。对于一般的单数据库应用来说,只需要一个SessionFactory就足够了。当然如果有多个数据库的话,还是需要为每个数据库生成对应的SessionFactory。它是线程安全的,同一个实例可以被应用中的多个线程共享。
也许你会很好奇,SessionFactory为什么是重量级对象呢?我也同样好奇,通过查看Hibernate的源码,发现SessionFactory存放了大量预定义的SQL语句以及映射元数据,所以自然需要很大的缓存了,同时需要一定的CPU时间来计算生成。想想Hibernate的这个设计是很有意义的,因为有了Mapping文件,很多SQL语句就已经确定了,只需要动态生成一次就可以了,这个设计也是为了提高持久化的效率。
3.Session接口: 从SessionFactory中可以获得Session实例。
Session接口是Hibernate应用中使用最广泛的接口了,它是持久化管理器,提供添加、更新、删除、加载、查询对象。Session不是线程安全的,所以应避免多个线程共享同一个Session实例。Session是轻量级对象,它的创建和销毁不需要太多资源,这意味着在应用中可以经常创建和销毁Session对象。
Session有一个缓存,称之为Hibernate的一级缓存,它存放当前工作单元加载的持久化对象,每个Session都有自己的缓存,缓存中的对象只能被当前工作单元访问。
4.Transaction接口: Transaction是Hibernate的数据库事务接口,它对底层道德事务接口进行了封装,底层事务接口包括:
JDBC API
JTA(Java Transaction API)
CORBA(Common Object Requet Broker Architecture) API
Hibernate应用可以通过一致Transaction接口来声明事务边界,这有助于应用可以在不同的环境或容器中移植。具体的事务实现使用在Hibernate.properties中进行指定。
5.Query和Criteria接口: 这两个是Hibernate的查询接口,用于向数据库查询对象,以及控制执行查询的过程。Query实例包装了一个HQL(Hibernate Query Language)来查询。Criteria接口完全封装了基于字符串形式的查询语句,比Query更面向对象,Criteria更擅长执行动态查询 。
Hibernate操作流程
public void test(){ //1.创建一个SessionFactory对象 SessionFactory sessionFactory = null; //a.创建Configuration对象;对应Hibernate的基本配置信息和对象关系映射信息 Configuration configuration = new Configuration().configure(); //4.0之前 //sessionFactory = configuration.buildSessionFactory(); //b.创建一个ServiceRegistry对象:Hibernate4.x新增对象 //Hibernate的任何配置和服务都需要在该对象中注册后才能有效 ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); //c. sessionFactory = configuration.buildSessionFactory(serviceRegistry); //2.创建一个Session对象 Session session = sessionFactory.openSession(); //3.开启事务 Transaction transaction = session.beginTransaction(); //4.增删改操作。。。。。。 //5.提交事务 transaction.commit(); //6.关闭Session session.close(); //7.关闭SessionFactory对象 sessionFactory.close(); }
Hibernate持久化类:
在Hibernate中,其对象或实例将会被存储在数据库表单中的Java类被称为持久化类。若该类遵循一些简单的规则或者被大家所熟知的Plain Old Java Object(POJO)编程模型,Hibernate将会处于其最佳运行状态。以下所列就是持久化类的主要规则,然而,在这些规则中,没有一条是硬性要求。
- 所有将被持久化的Java类都需要一个默认的构造函数。
- 为了使对象能够在Hibernate和数据库中容易识别,所有类都需要包含一个ID。此属性映射到数据库表的主键列。
- 所有将被持久化的属性都应该声明为private,并具有由JavaBean风格定义的getXXX和setXXX方法。
- Hibernate的一个重要特征为代理,它取决于该持久化类时处于非final的,还是处于一个所有方法都声明为public的接口。
- 所有的类是不可扩展或按EJB要求实现的一些特殊的类和接口。
Hibernate配置文件:
Hibernate的主配置文件是一个XML文件,通常命名为hibernate.cfg.xml,该文件中可以配置数据库连接参数,Hibernate框架参数以及映射关系文件。
<hibernate-configuration> <session-factory> <!-- 配置关于数据库连接的连接信息 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernateTest</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">yezi</property> <!-- 可以将向数据库发送的SQL语句显示出来 --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL语句 --> <property name="hibernate.format_sql">true</property> <!-- hibernate的方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!--hbm2ddl.auto: 生成表结构的策略配置--> update(最常用的取值): 如果当前数据库中不存在表结构,那么自动创建表结构. 如果存在表结构,并且表结构与实体一致,那么不做修改 如果存在表结构,并且表结构与实体不一致,那么会修改表结构.会保留原有列. create(很少):无论是否存在表结构.每次启动Hibernate都会重新创建表结构.(数据会丢失) create-drop(极少): 无论是否存在表结构.每次启动Hibernate都会重新创建表结构.每次Hibernate运行结束时,删除表结构. validate(很少):不会自动创建表结构.也不会自动维护表结构.Hibernate只校验表结构. 如果表结构不一致将会抛出异常. <!-- 配置hibernate的映射文件所在的位置 --> <mapping resource="cn/itheima/domain/Customer.hbm.xml" /> </session-factory> </hibernate-configuration>
对象关系映射文件:
指定实体类和数据表之间的对应关系,以及类中属性和表中字段之间的对应关系。
Hibernate中使用XML文件来描述映射关系,文件通常命名为“类名.hbm.xml”,并放于与实体类相同的路径下。
<hibernate-mapping> <!-- 配置实体类和表的映射关系 --> <class name="mypack.entity.Emp" table="EMP"> <!-- 配置主键属性和字段关系 --> <id name="id" type="java.lang.Integer" column="ID"> <!-- 用来指明主键的生成方式 --> <generator class="identity"> </generator> </id> <!-- 配置实体类其他属性和表中字段的关系 --> <property name="name" type="string" column="name"></property> <property name="age" type="java.lang.Integer" column="age"></property> <property name="salary" type="double" column="salary"></property> </class> </hibernate-mapping>
Hibernate主键生成方式:
①采用序列的方式生成主键,适用于Oracle数据库
<generator class="sequence"> <param name="sequence">emp_seq</param> <generator
②采用数据库自增长的机制生成主键,适用于其他数据库
<generator class="identity"></generator>
③根据当前配置的数据库方言,自动选择
<generator class="native"></generator>
④采用UUID或Hilo算法生成一个不规则主键值
<generator class="uuid"></generator>
⑤主键由外部程序负责生成,在save()之前必须指定一个。Hibernate不负责维护主键生成。在存储对象前,必须采用主键的setter方法给主键赋值。
<generator class="assigned"></generator>
Hibernate映射类型
当你准备一个Hibernate映射文件时,我们已经看到你把Java数据类型映射到了RDBMS数据格式。在映射文件中已经声明被使用的type不是Java数据类型,也不是SQL数据库类型。这种类型被称为Hibernate映射类型,可以从Java翻译成SQL,反之亦然。
Hibernate映射类型 | Java类型 |
---|---|
byte、short、integer、long | java.lang.Byte/Short/Integer/Long |
float、double | java.lang.Float/Double |
string | java.lang.String |
date(年月日) | java.sql.Date |
time(时分秒) | java.sql.Time |
timestamp | java.sql.TimeStamp |
yes_no或true_false | java.lang.Boolean |
延迟加载:延迟加载得到的是仅包含ID的代理对象
在使用某些Hibernate方法查询数据的时候,Hibernate返回的只是一个空对象(主键ID属性例外),并没有真正查询数据库。而在使用这个对象时才会触发查询操作,并将查询到的数据注入到这个空对象中,这种将查询时机推迟到对象访问时的机制称之为延迟加载。
为什么使用延迟加载?
①可以提高对内存资源的使用率
②可以降低对数据库的访问次数
注意:采用具有延迟加载机制的操作,需要避免在使用对象之前关闭Session。
关于LazyInitalizationException(懒加载异常)
其原因还是因为延迟加载,我们通过load方法得到了代理对象,但此时我们的session关闭了,但我们的对象依然存在,所以当我们使用该对象属性的时候,就会发生异常,解决办法就是OpenSessionInviewer。
OpenSessionInviewFilter(处理懒加载异常)
是Spring提供的一种针对Hibernate的支持类,其主要是在发起一个页面请求时打开Hibernate的Session,直到请求结束,具体就是通过在web.xml中通过Filter实现的。
Hibernate增删改查
1、save()和persist():执行INSERT操作
Session的save()方法使一个临时对象转变为持久化对象,Session的save()方法完成以下操作:
①把News对象加入到Session缓存中,使它进入持久化状态
②选用映射文件指定的标识符生成器,为持久化对象分配唯一的OID。在使用代理主键的情况下,setId()方法为News对象设置OID。
Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系,当News对象处于持久化状态时,不允许程序随意修改它的ID。
save()和persist()区别:
在调用persist()方法之前,若对象已经有id了,则不会执行INSERT,而抛出异常。
在调用save()方法之前,执行setId()方法,save()之后依然会被重新分配ID。
在调用save()方法之后,执行setId()方法会报异常,因为对于一个已经持久化的对象,ID是不能被修改的。
public void testPersist(){ News news = new News(); news.setAuthor("AA"); news.setTitle("aa"); session.persist(news); } public void testSave(){ News news = new News(); news.setAuthor("AA"); news.setTitle("aa"); System.out.println(news); //News[id=null,title=AA,Author=aa,Date=0] session.save(news); //执行save方法之后,使一个临时对象变为持久化对象,为对象分配ID System.out.println(news); //News[id=1001,title=AA,Author=aa,Date=0] }
2、get VS load
都可以根据给定的OID从数据库中加载一个持久化对象
①执行get方法会立即加载对象(立即加载)
执行load方法若不使用该对象,则不会立即执行查询操作,而返回一个代理对象(延迟加载)
②load可能会抛出LazyInitializationException 异常:
在需要初始化对象之前已经关闭了Session
③若数据表中没有对应的记录,同时需要使用对象时
get返回null
load抛出异常
public void testGet(){ News news = (News) session.get(News.class, 1); //session.close(); System.out.println(news); } public void testLoad(){ News news = (News) session.get(News.class, 1); //session.close(); System.out.println(news); }
3、list VS iterator:
都可以根据一定条件进而查询出所有满足条件的记录。 ① list采用非延迟加载机制执行查询操作,iterator采用延迟机制执行查询操作。 ② list方法会直接直接到数据库中加载数据,然后将查询结果放入缓存中,当然如果你配置了查询缓存,他会先进入查询缓存寻找,如果没有满足条件的再进入数据库加载数据。 ③ iterator方法在查询时是先从数据库中查询出所有满足条件的数据索引,然后再根据这些数据索引进入一级和二级缓存进行匹配,如果对于数据索引有实体对象则直接返回该对象,如果没有则在具体使用对象的时候才会进入数据库加载数据,并且把数据索引和对于实体对象放进缓存;
public void findAll(){ String hql = "from Emp"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); List<Emp> emps = query.list(); //list采用非延迟加载机制,与iterator区分开 for(Emp emp:emps){ System.out.println(emp); System.out.println(emp.getName()); } session.close(); } Query query = session.createQuery(hql); Iteraotr<Emp> it = query.iterate(); while(it.hasNext()){ Emp emp = it.next(); System.out.println(emp.getName()); }
4、update:
① 若更新一个持久化对象,不需要显式调用update方法。因为在调用Transaction的commit方法时,会先执行session的flush方法。
② 若更新一个游离对象,则需要显式调用update方法。可以把一个游离对象变为一个持久化对象。
public void testUpdate(){ News news = (News) session.get(News.class, 1); news.setAuthor("BB"); //已经持久化过的对象,并存在于session缓存中 //session.update(news); //重新开启session和transaction后,这个对象就不在session缓存中了,处于游离状态 transaction.commit(); session.close(); session = HibernateUtil.getSession(); transaction = session.beginTransaction(); news.setAuthor("AA"); session.update(news); }
5、saveOrUpdate:
Session的saveOrUpdate()方法同时包含了save()与update()方法的功能。若OID不为null,但数据表中还没有和其对应的记录,会抛出异常。
public void testSaveOrUpdate(){ News news = new News("FF","ff",new Date(0)); session.saveOrUpdate(news); }
6、delete:执行删除操作
只要OID和数据表中的一条记录对应,就会执行delete操作
若OID在数据表中没有对应的记录,则抛出异常
public void testDelete(){ News news = (News) session.get(News.class, 1); session.delete(news); }
Hibernate对象中的三种状态
Hibernate对象中有三种状态:临时状态(Transient)、持久状态(Persistent)、游离状态(Detached)。
临时状态:刚刚使用new语句创建,还没有被持久化,不处于Session的缓存中。处于临时状态的状态的Java对象被称为临时对象。
持久化状态:已经被持久化,加入到Session的缓存中。处于持久化状态的Java对象被称为持久化对象。
游离状态:已经被持久化,但不处于session的缓存中。处于游离状态的Java对象被称为游离对象。
@Test public void status(){ //开启事务 Transaction tx = session.beginTransaction(); //此时customer处于临时状态,此时customer没有持有oid Customer customer = new Customer(); customer.setName("NNNNNN"); customer.setPhoneNum("223344"); //此时对象持有了oid,与数据库建立的关联关系 int id = (Integer)session.save(customer); //当使用load()或者get()方法的时候,不会发送任何的sql语句,而是直接从session缓存中获取。 Customer customer1 = session.get(Customer.class, id); //结果为true,也就是说拿到的其实是同一个对象 System.out.println(customer == customer1); customer.setName("YYYYYY"); customer1.setName("BBBBBB"); System.out.println(customer1.getName()); System.out.println("customer的Id为:" + customer.getId()); //当事务提交时,实际会发送两条SQL:一条插入,一条更新 tx.commit(); //当session关闭后,customer处于游离状态 session.close(); System.out.println("当customer变为游离状态,依然持有oid: " + customer.getId()); Session session2 = OracleSessionUtils.openSession(); Transaction tx1 = session2.beginTransaction(); //此时customer被重新纳入到session的缓存中, 由游离状态变为持久化状态 session2.update(customer); customer.setName("MMMMMM"); customer.setName("OOOOOO"); //事务再次提交,实际只会发送一条SQL tx1.commit(); //customer在此从持久状态变为游离状态 session2.close(); }
图片描述
对象状态转换的相关方法
get、load、find: 方法的使用上较为类似,get和find方法笔者暂时未发现任何的不同之处。他们都是将数据库中对应Id的数据映射为Java对象,此时对象变为持久化状态。
save: 保存,此时Java对象已经与数据库记录建立的关系。将对象从临时状态的变为持久化状态或者将游离状态的数据变为持久状态。
saveOrUpdate: 保存或者更新,如果没有与数据库记录所对应的oid,则执行保存,如果有,则执行更新。将对象从临时状态的变为持久化状态或者将游离状态的数据变为持久状态。
delete: 删除对象,将对象从持久化状态或者游离状态变为临时状态。
close: 关闭session, 先将session清空,然后再关闭。将对象从持久状态变为临时状态。
clear: 清空session缓存。将对象从持久状态变为临时状态。
evict: 清除指定的对象。将对象从持久状态变为临时状态。
Hibernate缓存机制
Hibernate缓存的作用:
Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据Hibernate缓存分类:
Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存Hibernate一级缓存又称为“Session的缓存”,它是内置的,不能被卸载(不能被卸载的意思就是这种缓存不具有可选性,必须有的功能,不可以取消session缓存)。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID。 Hibernate二级缓存又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。
什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 常量数据
不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
Hibernate查找对象如何应用缓存?
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存,删除、更新、增加数据的时候,同时更新缓存。Hibernate管理缓存实例无论何时,当你给save()、update()或saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。 当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。