Hibernate总结

hibernate(20170731)

1.导包:hibernate-distribution-3.5.6-Final/hibernate3.jar
hibernate-distribution-3.5.6-Final\lib\required里面所有的包必须的包
再导入log4j-1.2.16, slf4j-log4j12-1.6.1, mysql驱动的包
2.在做了国际化的说明文档中找到hibernate.cfg.xml配置文件的相关内容,然后在src文件夹下面建立
hibernate.cfg.xml的总配置文件,然后复制相关内容并更改相关配置
3.创建实体类,并创建实体类与表的映射文件,并设置dtd约束
4.将映射文件配置到总配置文件(注意里面用的是斜杠)
5,如有需要创建hibernateUtil类,来获得session工厂

对象的三种状态瞬时态,持久态,托管态
对象被new出来,还没有和session建立关系的时候,那么此时就是瞬时态,和session建立关系此时就是持久态,和session没有建立关系,但是数据库有相应的记录,就是托管态

getCurrentSession()得到同一个session对象
openSession();每次都会创建一个session对象

load和get的区别;
load方式,类似懒加载,不会立即发送sql去查询,只有查询除了id以外的数据,才会发送sql语句,而get是全部查询,
/get方式去取不存在的数据,会返回一个null
//load方式去取不存在的数据,会报错

以下操作都要在事务中进行
添加操作
session.save(user); 返回的是刚添加进去的ID

删除操作
User user=new User(); 需要先new对象再进行删除
user.setId(5);
session.delete(user);

更新操作
User user=(User)session.get(User.class, 1); //获得对象再进行修改
user.setName("强哥");
session.update(user);

User user=new User(); //new对象进行修改
user.setId(2);
user.setName("xiaohon");
session.update(user);

查询操作:
单个查询get或者load
查询多个
Query query = session.createQuery("from User"); //HQL语句
List<User> list = query.list();

一对多(两张表情况):
[图片上传中。。。(1)]

[图片上传中。。。(2)]

配置过程:
1,从一开始,用set标签,name为set对象的引用,里面加上key标签,里面column属性为外键,one-to-many的class为多的那一方类名
2。从多开始,直接配置many-to-one ,name为本类中一的一方的引用,里面的column标签name属性为外键

这样存储先把多放进一的set集合里面,无法直接将订单依赖着客户保存进入数据库,那么这些对象都需要用session保存一次变为持久态,才能让订单依赖客户保存进入数据库,并且是这样的执行流程,通过session先保存订单,再保存客户,保存完客户再更新订单的id,这样的话sql语句较多,影响效率

inverse 管理关系由一的一方交给多的一方,不写默认false由一的一方管理,由多的一方来管理后,就不需要打印后面的那两句更新ID的语句了,效率提高了
那么此时的关系由多的一方来维护,那么此时要注意inverse加到哪里和测试类联系起来

查询的时候不会级联查询主要是为了资源和性能的考虑,采用的是懒加载,用到的时候才会去加载

save-update:一的一方保存,同时保存多的一方
delete:删除一的一方,同时删除多的一方
delete-orphan:孤儿删除,解除一和多的关系,同时将多的一方删除
如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">
all : save-update 和 delete 整合
all-delete-orphan : 三个整合

如果我们在删除的时候,删除一的一方时,由于级联删除的关系,会将多的一方也删除,但是我们只想让一的一方删除,不想将多的一方删除,那么此时可以通过一来查询与之相关的多,然后将他们的外键设置为null,再将级联删除配置删除才能进行删除操作。

commit(); 提交事务;关闭session clear();清空session

多对多的表关系:

多对多不用在实体类表中添加外键字段,而是创建中间表来生成外键字段,并且多对多的配置两边都是相同的,用配置文件自动创建中间表的时候,可能会无法添加进入数据,要用原始的save方式,添加一次数据后才会能够添加进入数据
[图片上传中。。。(3)]

抓取策略:
get和load的区别,都是从数据库获得数据后封装成的对象
但是load是懒加载,get不是,load是要用到除了id以外的数据才会主动去加载,这样有利于提高性能
抓取策略(要么加在类上面,要么加在set上面,默认true,关闭懒加载将其置为false即可)只能影响load,不能影响get ;
lazy="extra",极其懒惰(延迟),先查询客户select, 如果只需要拥有的订单数,使用聚合函数(不查询详情)

[图片上传中。。。(4)]

[图片上传中。。。(5)]

[图片上传中。。。(6)]

HQL查询语句:
from entityname 查询全部

只有jdbc比较特殊是从1开始的,其他的都是从0开始的

from entityname where id =? 查询单个 uniqueResult();
绑定参数query.setinteger(0,5);从0开始
from entityname where id =:id;
绑定参数query.setinteger(“id”,5);从0开始

select c.name from entityanme c 查询部分字段 返回字符串
select c.id,c.name from entityname c 查询两个字段,返回object数组
select new entity(c.name,c.id) from entityname c 和上面的一样,要有对应得构造方法

from entityname c order by c.id; 排序
from entityname c order by c.id desc; 降序 排序

分页
[图片上传中。。。(7)]

聚合函数:
总条数:select count(*) from entityname;

连接查询:
from entityname1,entityname2; 笛卡尔积,用途不大
隐式内连接:
from Custom c,OrderInfo o where o.custom=c; 外键关联
hibernate会参照映射文件查找数据库
显示内连接
from Customer c inner join c.orderSet

迫切内连接
from Customer c inner join fetch c.orderSet,返回对象不是数组

distinct c 去重复

左连接

QBC: 标准查询

QBC运算符

含义

=

Restrictions.eq()

<>

Restrictions.ne()

Restrictions.gt()

=

Restrictions.ge()

<

Restrictions.lt()

<=

Restrictions.le()

is null

Restrictions.isnull()

is not null

Restrictions.isNotNull()

like

Restrictions.like()

and

Restrictions.and()

and

Restrictions.conjunction()

or

Restrictions.or()

or

Restrictions.disjunction()

not

Restrictions.not()

in(列表)

Restrictions.in()

[图片上传中。。。(8)]
@Test
public void qbc1() {
SessionFactory sessionFactory= HibernateUtil.getSessionFactory();
Session session=sessionFactory.getCurrentSession();
session.beginTransaction();

Criteria criteria=session.createCriteria(OrderInfo.class);
criteria.setFirstResult(0);
criteria.setMaxResults(2);
List<Customer> list=criteria.list();
System.out.println(list);

session.getTransaction().commit();
}

集合查询:
Criteria criteria=session.createCriteria(Customer.class);
List<Customer> list=criteria.list();
System.out.println(list);

分页查询
Criteria criteria=session.createCriteria(OrderInfo.class);
criteria.setFirstResult(0);
criteria.setMaxResults(2);
List<Customer> list=criteria.list();

排序查询
Criteria criteria=session.createCriteria(Customer.class);
criteria.addOrder(Order.desc("id")); asc也行
List<Customer> list=criteria.list();

条件/多条件组合查询
Criteria criteria=session.createCriteria(OrderInfo.class);
//下面的条件可以组合起来求得交集的集合
criteria.add(Restrictions.eq("address", "南山"));//准确查询
criteria.add(Restrictions.like("address", "%山"));//模糊查询,%可以加在任何位置
criteria.add(Restrictions.gt("id",2)); //数字查询
List<Customer> list=criteria.list();

采用集合的方式进行多条件组合查询
Criteria criteria=session.createCriteria(OrderInfo.class);
//下面的条件可以组合起来求得交集的集合
HashMap<String,String> hashmap=new HashMap<>();
hashmap.put("detail", "订单2");
hashmap.put("address","南山");
criteria=criteria.add(Restrictions.allEq(hashmap));
List<OrderInfo> list=criteria.list();

模糊查询的一种模式:
Criteria criteria=session.createCriteria(OrderInfo.class);
criteria.add(Restrictions.ge("id",16));
criteria.add(Restrictions.like("address", "宝安", MatchMode.EXACT));
List<OrderInfo> list=criteria.list();

MatchMode.START:字符串在最前面的位置.相当于"like 'key%'"
MatchMode.END:字符串在最后面的位置.相当于"like '%key'"
MatchMode.ANYWHERE:字符串在中间匹配.相当于"like '%key%'"
MatchMode.EXACT:字符串精确匹配.相当于"like 'key'"

in的范围查询:
Criteria criteria=session.createCriteria(OrderInfo.class);
criteria.add(Restrictions.in("address",new String[]{"宝安","南山"}));
List<OrderInfo> list=criteria.list();

and查询:
Criteria criteria=session.createCriteria(OrderInfo.class);
Criterion c1=Restrictions.like("address", "%山", MatchMode.END);
Criterion c2=Restrictions.gt("id", 16);
criteria.add(Restrictions.and(c1,c2));
List<OrderInfo> list=criteria.list();

or查询:
Criteria criteria=session.createCriteria(OrderInfo.class);
Criterion c1=Restrictions.like("address", "安", MatchMode.ANYWHERE);
Criterion c2=Restrictions.eq("id", 15);
criteria.add(Restrictions.or(c1,c2));
List<OrderInfo> list=criteria.list();

聚合函数求总条数
Criteria criteria=session.createCriteria(OrderInfo.class);
ProjectionList project=Projections.projectionList();
project.add(Projections.rowCount());
criteria.setProjection(project);
Long a=(Long)criteria.uniqueResult();
System.out.println(a);

分组查询:
Criteria criteria=session.createCriteria(OrderInfo.class);
ProjectionList project=Projections.projectionList();
project.add(Projections.groupProperty("address"));
criteria.setProjection(project);
List<OrderInfo> list=criteria.list();

去重查询:
Criteria criteria=session.createCriteria(Customer.class);
criteria.createAlias("hashSet", "h");
criteria.setResultTransformer(criteria.DISTINCT_ROOT_ENTITY);
List<OrderInfo> list=criteria.list();
System.out.println(list);

左外连接
Criteria criteria=session.createCriteria(Customer.class);
criteria.createAlias("hashSet", "h", CriteriaSpecification.LEFT_JOIN);
criteria.setResultTransformer(criteria.DISTINCT_ROOT_ENTITY);
List<OrderInfo> list=criteria.list();

返回一个对象数组
Criteria criteria=session.createCriteria(Customer.class);
criteria.createAlias("hashSet", "h");
criteria.setResultTransformer(CriteriaSpecification.PROJECTION);
List<Object[]> list=criteria.list();

离线查询:

离线查询

  • 就是在session还没创建的时候,就先组装好查询的相关条件
  • 然后再传递给dao层去执行
    public void qbc10() {
    DetachedCriteria detachedCriteria=DetachedCriteria.forClass(Customer.class);
    detachedCriteria.createAlias("hashSet", "h");
    detachedCriteria.setResultTransformer(CriteriaSpecification.PROJECTION);
    SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
    Session session=sessionFactory.getCurrentSession();
    session.beginTransaction();
    Criteria criteria=detachedCriteria.getExecutableCriteria(session);
    List<Object[]> list=criteria.list();
    for (Object[] objects : list) {
    System.out.println(objects[0]+":"+objects[1]);
    }
    session.getTransaction().commit();
    }

二级缓存:
a) 一级缓存:session级别缓存,在一次请求中共享数据。
b) 二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。

  1.   导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar
    
  2.   开启二级缓存
    
  3.   确定二级缓存提供商
    
  4.   确定需要缓存内容
    

1> 配置需要缓存的类
2> 配置需要缓存的集合

  1.   配置ehcache自定义配置文件
    

[图片上传中。。。(9)]

注意在设置需要缓存的内容和权限时要放在映射文件的下面这样的顺序

查询二级缓存:
先配置<property name="hibernate.cache.use_query_cache">true</property>
public void query() {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();

Session session=sessionFactory.getCurrentSession();
session.beginTransaction();
Query query=session.createQuery("from Customer");

query.setCacheable(true); 每次使用时先开启
List<Customer> l=query.list();
System.out.println(l);
session.getTransaction().commit();

Session session1=sessionFactory.getCurrentSession();
session1.beginTransaction();
Query query1=session1.createQuery("from Customer");
query1.setCacheable(true); 每次使用时先开启
List<Customer> l1=query1.list();
System.out.println(l1);
session1.getTransaction().commit();
sessionFactory.close();
}

hihernate的批处理:
采用这样的配置
[图片上传中。。。(10)]
那么程序就会采用in的方式进行这种范围的查询
但是hibernate还是建议我们使用jdbc api进行查询,这样速度比较快

采用实现work();接口来进行批处理,比hibernate自带的批处理快
session.doWork(new Work() {

@Override
public void execute(Connection connection) throws SQLException {
PreparedStatement ps=connection.prepareStatement("insert into user(name)values(?)");
for(int i=0;i<10000;i++){
ps.setString(1, "小明"+i);
ps.addBatch();
if(i%500==0){
ps.executeBatch();
}}
}
});
session.getTransaction().commit();
long end=System.currentTimeMillis();
System.out.println(end-start);
}

hibernate采用c3p0连接池
1.导包C:\Users\Administrator\Desktop\hibernate-distribution-3.5.6-Final\lib\optional\c3p0
2.配置配置文件
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.max_size">3</property>
<property name="hibernate.c3p0.mix_size">3</property>
<property name="hibernate.c3p0.timeout ">4000</property>

设置事务的隔离级别:
<property name="hibernate.connection.isolation">4</property>

事务的特性:
⑴ 原子性(Atomicity)
  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
⑵ 一致性(Consistency)
  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
  拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
⑶ 隔离性(Isolation)
  隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
  关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
⑷ 持久性(Durability)
  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
  例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

以上介绍完事务的四大特性(简称ACID),现在重点来说明下事务的隔离性,当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
1,脏读
  脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
  当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下
update account set money=money+100 where name=’B’; (此时A通知B) update account set money=money - 100 where name=’A’;
  当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。
2,不可重复读
  不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
  例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
  不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
  在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……
3,虚读(幻读)
  幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
  幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

现在来看看MySQL数据库为我们提供的四种隔离级别:
  ① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
  ② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
  ③ Read committed (读已提交):可避免脏读的发生。
  ④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
  在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
  在MySQL数据库中查看当前事务的隔离级别:
select @@tx_isolation;
  在MySQL数据库中设置事务的隔离 级别:
set [glogal | session] transaction isolation level 隔离级别名称; set tx_isolation=’隔离级别名称;’
例1:查看当前事务的隔离级别:
  
[图片上传中。。。(11)]
例2:将事务的隔离级别设置为Read uncommitted级别:
  
[图片上传中。。。(12)]
或:
  
[图片上传中。。。(13)]
记住:设置数据库的隔离级别一定要是在开启事务之前!
  如果是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)方法之前。调用Connection对象的setTransactionIsolation(level)即可设置当前链接的隔离级别,至于参数level,可以使用Connection对象的字段:
  
[图片上传中。。。(14)]
在JDBC中设置隔离级别的部分代码:
  
[图片上传中。。。(15)]
  后记:隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。

乐观锁和悲观锁
a) 悲观锁(数据库提供实现) . 悲观的认为认为一定发生别人要修改我使用的数据. 那我就可以为我读取的数据加锁.
采用这样的方式对正在操作的数据上锁,在本线程提交事务之前,其他线程要想操作这个数据必须要等到当前线程操作完了并且提交事务时候才能操作那个数据
User user=(User) session.get(User.class, 20, LockOptions.UPGRADE); 给id为20的user的数据上锁
b)乐观锁,乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
做法:在类里面添加一个Integer类型的version属性,然后在配置文件里面添加一个version配置,对应的表也要添加一个version字段<version name="version" />,这个标签必须加在id之后
它提交的时候主要是对比version的值,如果这个值和当时取出来的不一致就会提交失败,如果能够提交成功那么它就会将那个version的值加1。

在使用新版的hibernate的时候在获得sessionfactory的时候推荐使用这个工具类,新版的没有hibernate3.jar这个jar包,把必须的包导进去就行了
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();

private static SessionFactory buildSessionFactory() {
try {
Configuration cfg = new Configuration().configure();
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
SessionFactory sessionFactory = cfg.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}

注解实现映射关系:

[图片上传中。。。(16)]

[图片上传中。。。(17)]

[图片上传中。。。(18)]

[图片上传中。。。(19)]

[图片上传中。。。(20)]
使用注解的时候需要更换获得sessionFactory的方式,采用注解的方式

package com.qianfeng.forum.common;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibnateUtils {
private static final SessionFactory sessionFactory = buildSessionFactory();

private static SessionFactory buildSessionFactory() {

try {

//return new Configuration().configure().buildSessionFactory();
return new AnnotationConfiguration().configure().buildSessionFactory();

}

catch (Throwable ex) {

System.err.println("Initial SessionFactory creation failed." + ex);

throw new ExceptionInInitializerError(ex);

}

}

public static SessionFactory getSessionFactory() {

return sessionFactory;

}

}

采用拦截器
作用:控制session的关闭时机,等页面加载完数据之后再关闭session
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SessionFactory factory = HibnateUtils.getSessionFactory();
Session session = factory.getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 放行
chain.doFilter(request, response);// action--index.jsp
//
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction != null) {
transaction.rollback();
}
}

}

推荐阅读更多精彩内容