Hibernate
orm
目前流行的编程语言,如Java,C#等,都是面向对象的语言,而主流的数据库产品,Oracle,DB2等,依然是关系数据库。编程语言和底层数据库的发展不协调,催生了ORM框架,ORM框架可作为面向对象编程语言和数据库之间的桥梁。
ORM全称是Object/Relation Mapping,即对象/关系数据库映射。ORM可以理解为一种规范,概述了这类框架的基本特征:完成面向对象语言到关系数据库的映射。
ORM工具的唯一作用就是:把持久化对象的保存、删除、修改等操作,转换成对数据库的操作。从此,程序员可以直接以面向对象的方式操作持久化对象,而ORM框架则负责转换成对应的SQL操作。
Hibernate
Hibernate是一个对象关系映射(ORM)框架,对JDBC进行了封装,实现了Java对象与关系数据库记录的映射关系。
提供了5个核心接口:Session、SessionFactory、Transaction、Query和Configuration。实现对数据库的访问和事务的控制。
Session是一个轻量级的线程安全的对象,主要负责被持久化对象和数据库的操作。使用SessionFactory创建,Session在访问数据库时会建立与数据库的连接,只有在需要时才会建立。
SessionFactory负责初始化Hibernate,SessionFactory是线程安全的。一般SessionFactory会在Hibernate启动时创建一次,因此为了便于使用,SessionFactory应该使用一个单例模式来实现。
Transaction负责事务相关的操作。通过Session的beginTransaction方法来创建。
Query负责执行各种数据库查询。可以使用HQL语句或者SQL语句。通过Session的createQuery方法来创建。
Configuration用于读取Hibernate配置文件,并生成SessionFactory对象。
Hibernate应用的开发流程
1、开发实体类和实体类的映射文件(Product.java/Product.hbm.xml)
Product.java
import java.io.Serializable;import java.util.Date;//Hivernate要求实体类实现Serializable接口public class Product implements Serializable{ //标志属性一般使用基本类型的包装类或java.lang.String private Integer id; private String productName; private double productPrice; private Date productBrith; public Product(Integer id, String productName, double productPrice, Date productBrith) { super(); this.id = id; this.productName = productName; this.productPrice = productPrice; this.productBrith = productBrith; } public Product(String productName, double productPrice, Date productBrith) { this.productName = productName; this.productPrice = productPrice; this.productBrith = productBrith; } public Product() { } //省略setter和getter方法 }
Product.hbm.xml
2、配置Hibernate
hibernate.cfg.xml配置文件
oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@localhost:1521:orcl scott itcast org.hibernate.dialect.OracleDialect update true true
Hibernate配置文件的默认文件名为hibernate.cfg.xml,当程序调用Configuration对象的configure()方法时,Hibernate将自动加载该文件。
3、进行单元测试
package com.ghq.test;import java.util.Date;import java.util.List;import org.hibernate.Query;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.cfg.Configuration;import org.junit.Test;import com.ghq.model.entity.Product;public class ProductTest { @Test public void creatTable(){ Configuration conf = new Configuration(); //加载配置文件 使用Configuration完成 conf.configure(); //创建SessionFactory对象 Configuration完成 SessionFactory sessionFac = conf.buildSessionFactory(); } //新增方法 @Test public void addProduct(){ Configuration conf = new Configuration(); conf.configure(); SessionFactory sessionFac = conf.buildSessionFactory(); Session session = sessionFac.openSession(); Product product = new Product("Switch2", 2500, new Date()); Transaction tr = null; try { //开启事务 tr = session.beginTransaction(); session.save(product); //提交事务 tr.commit(); } catch (Exception e) { tr.rollback(); e.printStackTrace(); } finally{ //关闭session和SessionFactory session.close(); sessionFac.close(); } } //删除方法 @Test public void delProduct(){ Configuration conf = new Configuration(); conf.configure(); SessionFactory sessionFac = conf.buildSessionFactory(); Session session = sessionFac.openSession(); Transaction tr = null; try { //开启事务 tr = session.beginTransaction(); Product product = new Product(6,"Switch2", 5000, new Date()); session.delete(product); //提交事务 tr.commit(); } catch (Exception e) { tr.rollback(); e.printStackTrace(); } finally{ //关闭session和SessionFactory session.close(); sessionFac.close(); } } //修改方法 @Test public void updateProduct(){ Configuration conf = new Configuration(); conf.configure(); SessionFactory sessionFac = conf.buildSessionFactory(); Session session = sessionFac.openSession(); Transaction tr = null; try { //开启事务 tr = session.beginTransaction(); Product product = new Product(1,"Switch2", 1234, new Date()); session.update(product); //提交事务 tr.commit(); } catch (Exception e) { tr.rollback(); e.printStackTrace(); } finally{ //关闭session和SessionFactory session.close(); sessionFac.close(); } } @Test public void getProduct(){ Configuration conf = new Configuration(); conf.configure(); SessionFactory sessionFac = conf.buildSessionFactory(); Session session = sessionFac.openSession(); Product pro = session.get(Product.class, 1); System.out.println(pro.getId()+" "+pro.getProductName()+" "+pro.getProductPrice()); //关闭session和SessionFactory session.close(); sessionFac.close(); }}
实体类中的属性分别是数组,Set,List集合时,映射文件中的配置。下面的例子是部门和员工之间的关系。
private String[] emps;private Setemps = new HashSet ();private List emps = new ArrayList ();
映射文件配置
数据表之间的关系
多对一 (多个商品对应一个商品类型)
在映射文件中配置
在<many-to-one>设置级联 cascade
none:默认的值,表示无级联
save-update:
delete:删除多端,级联删除一端
all:包括save-update和delete
一对多关联(一个部门对应多个员工)
一端实体中一个Set类型的属性,元素类型是多端实体。
<one-to-many class=”set集合元素类型”>
级联:同上
all-delete-orphan:当删除一端集合中的元素时,会删除这些元素对应的记录。
hibernate主键生成策略
1、自动增长identity
适用于MySQL、DB2、MS SQL Server,采用数据库生成的主键,用于为long、short、int类型生成唯一标识
使用SQL Server 和 MySQL 的自增字段,这个方法不能放到 Oracle 中,Oracle 不支持自增字段,要设定sequence(MySQL 和 SQL Server 中很常用)
2、sequence
DB2、Oracle均支持的序列,用于为long、short或int生成唯一标识,要求数据库提供Sequence机制。
3、hilo
使用一个高/低位算法生成的long、short或int类型的标识符,给定一个表和字段作为高位值的来源,默认的表是hibernate_unique_key,默认的字段是next_hi。它将id的产生源分成两部分,DB+内存,然后按照算法结合在一起产生id值,可以在很少的连接次数内产生多条记录,提高效率。
4、native
会根据底层数据库的能力,从identity、sequence、hilo中选择一个,灵活性更强,但此时,如果选择sequence或者hilo,则所有的表的主键都会从Hibernate默认的sequence或者hilo表中取。并且,有的数据库对于默认情况主键生成测试的支持,效率并不是很高
5、seqhilo
sequence和hilo的结合,hilo的高位由sequence产生,所以也需要底层数据库的支持
通过hilo算法实现,但是主键历史保存在Sequence中,适用于支持 Sequence 的数据库,如 Oracle(比较少用)
6、increment
这个是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,因此所有的数据库都可以使用,但问题也随之而来,由于是Hibernate生成的,所以只能有一个Hibernate应用进程访问数据库,否则就会产生主键冲突,不能在集群情况下使用插入数据的时候hibernate会给主键添加一个自增的主键,但是一个hibernate实例就维护一个计数器,所以在多个实例运行的时候不能使用这个方法。
7、uuid.hex
使用一个128-bit的UUID算法生成字符串类型的标识符,UUID被编码成一个32位16进制数字的字符串。UUID包含:IP地址、JVM启动时间、系统时间(精确到1/4秒)和一个计数器值(JVM中唯一)hibernate会算出一个128位的唯一值插入
8、assigned
由应用程序负责生成主键标识符,往往使用在数据库中没有代理主键,使用的主键与业务相关的情况
9、foreign
使用外部表的字段作为主键
10、select
使用触发器生成主键(主要用于早期的数据库主键生成机制,少用)
Hibernate的使用过程
1、应用程序通过Configuration类读取Hibernate配置文件并创建SessionFactory对象。
SessionFactorysf = new Configuration().confugure().buildSessionfactory();
2、通过SessionFactory生成一个Session对象。
Session session = sf.openSession();
3、通过Session对象的beginTransaction()方法创建一个事务。
Transaction tr = session.beginTransaction();
4、面向对象方式操作数据库
通过Session对象的get()、load()、save()、update()、delete()和saveOrUpdate()等方法来实现数据的加载,保存,更新和删除等。也可以通过session生成一个Query对象,利用Query对象执行查询操作,最后通过commit或rollback完成事务的操作。
5、完成所有的持久化操作与事务操作后需要关闭Session与SessionFactory。
持久化对象的生命周期。
三态:(瞬态、持久化态、脱管态)
瞬态:对象由new操作符创建,尚未与Hibernate Session关联的对象为瞬态。只存在于内存中,而在数据库中没有相应数据。
持久化态:与session关联并且在数据库中有相应数据,并拥有一个持久化标识。已经持久化,加入到了session缓存中。
脱管态:持久化对象脱离了session的对象。如session缓存被清空的对象。已经持久化,但不在session缓存中。
| 瞬态 (Transient) | 持久化状态 (Persistent) | 脱管态 (Detached) |
是否处于Session缓存中 | × | √ | × |
数据库中是否有对应记录 | × | √ | √ |
三态之间的转换方法:
①如何成为瞬态?
对象通过构造方法成为瞬态;持久态和脱管则通过session的delete方法成为瞬态
②如何成为持久态?
对象可以由session的load或get方法直接成为持久态;瞬态对象可以通过save,saveOrUpdate或persist方法成为持久态;脱管对象则可以通过update,saveOrUpdate成为持久态
③如何成为脱管态?
脱管态只能由持久态转换而来,通过close、clear或clear方法实现。
save,update和saveOrUpdate区别
save是将瞬态转为持久态,而update是将托管态转为持久态,saveOrUpdate可以说是两者的综合,它执行时先判断对象的状态,若是瞬态,则save;若是脱管态,则update。
get和load区别
get和load都是根据ID取得一个记录
get方法是直接从数据库中检索,而load方法的执行则比较复杂,首先查找session的persistent Context中是否有缓存,如果有则直接返回,如果没有则判断是否是lazy,如果不是直接访问数据库检索,查到记录返回,查不到则抛出异常。
load方法,hibernate认为该id对象在数据库中是一定存在的,所以可以放心的使用,可以使用代理来延迟加载该对象。在用到对象中的其他属性时才查询数据库,但是万一数据库中不存在该记录,则抛出异常。
get方法,hibernate一定要获得真是的数据,否则返回null。
Hibernate实现分页机制
使用Query对象的中的方法
Query q = session.createQuery(hql); //设置要查询的第一行数据 q.setFirstResult();//设置查询结果集的大小q.setMaxReslut();
HQL语句
HQL是Hibernate Query Language的缩写,HQL的语法很像SQL的语法,但HQL是一种面向对象的查询语言。因此,SQL的操作对象是数据表和列等数据对象,而HQL的操作对象是类、实例、属性等。
HQL是完全面向对象的查询语言,因此可以支持继承和多态等特征。
HQL查询依赖于Query类,每个Query实例对应一个查询对象。使用HQL查询可按如下步骤进行:
(1)获取Hibernate Session对象。
获取Session的三种方法
①直接通过SessionFactory构建Session对象(用openSession()或者getCurrentSession())
try { SessionFactory sf = new Configuration().configure().buildSessionFactory(); Session session = sf.openSession();}
//也可用 sf.getCurrentSession();区别在于前者每次都创建一个新的Session,而后者在当前无Session时才创建,否则会绑定到当前已有线程;前者必须手动关闭,后者在事务结束后自动关闭。
这样做太过繁琐每一步都得自行建立,因此引入spring管理Session。sessionfactory的创建等都交给spring管理。用户可以不再考虑session的管理,事务的开启关闭,只需配置事务即可。
②利用HibernateTemplate
在applicationContext.xml中配置好相关事务,就可以很方便地获取Session了。
//从Spring容器获得HibernateTemplate对象
@Resource private HibernateTemplate hibTem; public HibernateTemplate getHibTem() { return hibTem; } Session session = getHibTem().getSessionFactory().getCurrentSession();
③利用HibernateCallback()接口中的doInHibernate方法
Listls = getHibTem().execute(new HibernateCallback() { @Override public List doInHibernate(Session session) throws HibernateException { Query query = session.createQuery(hql); //为占位参数赋值 if (args != null && args.length > 0) { for (int i = 0; i < args.length; i++) { query.setParameter(i, args[i]); } } return query.setFirstResult((page-1)*rows).setMaxResults(rows).list(); } });
在Spring+Hibernate环境中,推荐用这种方式来获取session。这种方法的优势在于你不需要对session进行维护,会由Spring管理。你只需在需要session环境时,调用即可。
(2)编写HQL语句
语句中不能出现表名,列名,应该为实体类类名,实体类属性名,可以有占位参数。
(3)以HQL语句作为参数,调用Session的createQuery(hql)方法创建查询对象。
Query query = session.createQuery(hql);
(4)如果HQL语句中包含参数,调用query的setParameter()方法为占位参数赋值。
if (args != null && args.length > 0) { for (int i = 0; i < args.length; i++) { query.setParameter(i, args[i]); }}
(5)调用Query的list()或uniqueResult()方法返回查询结果列表(持久化实体集)
HQL语句查询
HQL查询的from子句
1. 查询整个映射对象所有字段
from关键字后面紧跟持久化类的类名。
String hql = "from Users"; Query query = session.createQuery(hql); Listusers = query.list(); for(Users user : users){ System.out.println(user.getName() + " : " + user.getPasswd() + " : " + user.getId()); }
2.查询字段
//查询其中几个字段
String hql = " select name,passwd from Users"; Query query = session.createQuery(hql); //默认查询出来的list里存放的是一个Object数组 List
3.修改默认查询结果(query.list())不以Object[]数组形式返回,以List形式返回
//查询其中几个字段,添加new list(),注意list里的l是小写的。也不需要导入包,这样通过query.list()出来的list里存放的不再是默认的Object数组了,而是List集合了
String hql = " select new list(name,passwd) from Users"; Query query = session.createQuery(hql); //默认查询出来的list里存放的是一个Object数组,但是在这里list里存放的不再是默认的Object数组了,而是List集合了 Listlist = query.list(); for(List user : list){ String name = (String)user.get(0); String passwd = (String)user.get(1); System.out.println(name + " : " + passwd); }
HQL查询的where子句
//条件查询,参数索引值从0开始,索引位置。通过setString,setParameter设置参数
String hql = "from Users where name=? and passwd=?"; Query query = session.createQuery(hql); //第1种方式 // query.setString(0, "name1"); // query.setString(1, "password1"); //第2种方式 query.setParameter(0, "name1"); query.setParameter(1, "password1"); Listlist = query.list(); for(Users users : list){ System.out.println(users.getId()); }
事务控制
事务是一步或者几步基本操作组成的逻辑执行单元,这些基本操作作为一个整体执行单元,要么全部执行,要么全部取消执行。
Session与事务
Hibernate的事务通过Session的beginTransaction()方法显式打开,Hibernate并不提供事务控制行为,Hibernate只是对底层事务进行了抽象,让应用程序可以直接面向Hibernate事务编程,从而将应用程序和JDBC连接或其他事务资源隔离开。从编程角度来看,Hibernate事务由Session对象开启;从底层实现来看,Hibernate事务由TransactionFactory的实例来产生。
Session对象是轻量级的,也是线程不安全的。对于单个业务进程、单个工作单元而言,Session只被使用一次。创建Session时,并不会立即打开与数据库之间的连接,只有需要进行数据库操作时,Session才会获取JDBC连接。因此,打开和关闭Session并不会对性能造成很大的影响。
二级缓存与查询缓存
缓存的目的是为了通过减少应用程序对物理数据源访问的次数来提高程序运行的效率。
在Hibernate中,缓存用来把从数据库中查询出来的和使用过的对象保存在内存中,以便在后期需要用到这个对象时可以直接从缓存中来获取这个对象。避免了因大量发送SQL语句到数据库查询导致的性能损耗。
在Hibernate中有一级缓存和二级缓存的概念,一级缓存由Session来管理,二级缓存由SessionFactory来管理。二级缓存可有可无,一级缓存必须存在。
一级缓存使用的场合如下:当使用Session查询数据时,首先会在该Session内部查找该对象是否存在,若存在,则直接返回;否则就到数据库中去查询,并将查询的结果缓存起来以便后期使用。
一级缓存的缺点是当使用Session来表示一次会话时,它的生命周期比较短,而是它是线程不安全的,不能被多个线程共享。
二级缓存用来为Hibernate配置一种全局的缓存,以便实现多个线程与事务共享。使用二级缓存之后,当查询数据时,首先在内部缓存中查询,如果不存在,接着在二级缓存中查找,最后才去数据库查询。二级缓存是独立于Hibernate软件部件,属于第三方产品。Hibernate3以后默认使用EhCache。
开启二级缓存(EhCache)
①在hibernate.cfg.xml文件中进行如下配置
true org.hibernate.cache.ehcache.EhCacheRegionFactory
②复制二级缓存的jar包。
③将缓存实现所需要的文件添加到系统的类加载路径中。ehcashe.xml