加载hibernate配置文件 Configuration cfg = new Configuration().configure("hibernate.cfg.xml"); --也可以用无参的configure()方法,不过此时hibernate配置文件必须放在src下。
批量更新 缓存同步 hibernate使用效率很低
一、代理:
JDK动态代理:只能对实现了接口的类进行代理。
cglib:对类进行代理。
二、lazy可用的地方:
1、class标签上(影响普通属性)
2、property标签上(影响当前属性)
3、集合上(影响集合)
4、<one-to-one>、<many-to-one>等标签上(影响关联对象)
lazy概念:只有真正使用该对象时,才会创建该对象,对于Hibernate而言,真正使用的时候才会发出sql
当我们查找集合时(默认为延迟加载)不会发sql,只有使用集合中的对象或集合中的对象的属性时才发sql
三、悲观锁和乐观锁:
悲观锁的实现:通常依赖于数据库机制,在整个过程中将数据锁定,其它任何用户都不能读取或修改
使用悲观锁时:load的lazy就会失效,使用方法为在代码中 load(EntryName.class,id,LockMode.UPGRADE)
使用乐观所时:要在对象bean中加一个int类型的versions属性(自定义属性),并且要在影射文件中开启对乐观锁的支持,即在<class>标签中加入<class optimistic-lock="version">一告诉Hibernate该对象要使用乐观锁,并且必须要在<id>标签后使用<version>标签配置版本号
即:<id name="id">
<generator class="native" />
</id>
<version name="versions" />
大多数基于数据版本记录机制(version)实现,一般是在数据库表中加入一个version字段
读取数据时将版本一同读出,之后更新数据时版本号加一,如果提交数据时版本号小于或等于数据表中的版本号,则认为数据是过期的,否则给予更新。
数据库隔离级别中的“可重复读”就是使用了“悲观锁”。
四、HQL查询
1、以对象为基础的查询语言,查询的是对象(类),不是某个表。
2、在Hql中关键字不区分大小写,但是属性和类名区分大小写。
3、查询语句:
简单属性查询:
List students = session.createQuery("select name from Student").list(); list是一个string类型的name属性集合
List students = session.createQuery("select id,name from Student").list(); List是一个Object[]类型的对象数组集合
List students = session.createQuery("select new Student(id,name) from Student").list(); List是一个Student类型的对象集合
List students = session.createQuery("select s.id,s.name from Student as s").list(); List是一个Object[]类型的对象数组集合,使用别名(as可用可不用)
实体对象查询:
List students = session.createQuery("from Student as s").list(); List是一个Student类型的集合(as可用可不用)
List students = session.createQuery("select s from Student as s").list(); List是一个Student类型的集合(如果用select查询实体时,一定要加别名)
使用query中的list()接口时,发出一条语句,查出对象,返回一个List,list会向缓存中放入数据,而不利用缓存中的数据。
使用query中的iterator()接口时,发出N+1条语句,第一条语句查出所有记录的id,后根据N个id查出N个对象,返回一个Iterator,iterator接口默认情况下会利用缓存数据,但如果缓存中不存在数据有可能出现N+1问题。
五、条件查询
1、拼字符串查询
List students = session.createQuery("select s.id,s.name from Student s where s.name like '%李%'").list(); 别名可有可无
2、使用占位符查询
Query query = session.createQuery("select s.id,s.name from Student s where s.name like ?");
query.setParameter(0,"%李%"); 参数索引从0开始
List students = query.list();
或使用基于方法连的编程(也是使用了占位符查询)
List students = session.createQuery("select s.id,s.name from Student s where s.name like ?")
.setParameter(0,"%李%")
.list();
3、使用参数命名方式查询
List studnets = session.createQuery("select s.id,s.name from Student s where s.name like :userName")
.setParameter("userName","%李%")
.list();
List studnets = session.createQuery("select s.id,s.name from Student s where s.name like :userName and s.id=:userId")
.setParameter("userName","%李%")
.setParameter("userId",12)
.list();
List studnets = session.createQuery("select s.id,s.name from Student s where s.id in(?,?,?)")
.setParameter(0,100)
.setParameter(1,101)
.setParameter(2,102)
.list();
List studnets = session.createQuery("select s.id,s.name from Student s where s.id in(:userIds)")
.setParameterList("userIds",new Object[]{1,2,3,5})
.list();
List studnets = session.createQuery("select s.id,s.name from Student s where date_format(s.birthday,'%Y-%m')") 在hql中可以使用数据库的函数,如:date_format
.setParameter(0,"2008-08")
.list();
六、使用原生SQL查询
List student = session.createSQLQuery("select * from t_student").list(); 使用createSQLQuery直接查询表
七、外置命名查询(将HQL语句放在配置文件中,使得与代码的耦合度很低)
在.xml影射文件中<class>标签以外使用<query>标签来写sql语句:(<query>可以写在任何一个映射文件中,只要name值不重复就可以了)
<query name="searchStudents"> 由于Hql语句中可能有"<"或">"符号与xml冲突,所以使用转移字符或将语句直接写入CDATA中
<![CDATA[
select s from Student s where s.id < ?
]]>
</query>
在程序中引入:
List students = session.getNameQuery("searchStudents") getNameQuery()从映射文件中得到查询串
.setParameter(0,10)
.list();
八、查询过滤器(在当前session中起作用,filter是hibernate的filter)
在.xml影射文件中<class>标签以外加入以下配置内容
<filter-def name="filtertest">
< filter-param name="myId" type="integer" />
</filter-def>
并且在需要使用此条件的对象影射文件<class>标签中加入如下内容:
<filter name="filtertest" condition="id < :myId" />
所以在我们查询“用户”对象时,就会在hql语句后自动加上 id < ?
如:(查询id小于10的用户)
在代码中:
session.enableFilter("filtertest")
.setParamter("myId",10);
hibernate生成的sql语句:select * from t_user where userId < ?
九、对象导航查询(使用.导航查询)
List students = session.createQuery("select s.name from Student s where s.classes.name like '%李%'").list();
十、连接查询
内连接
外连接(左连接、右连接)
List students = session.createQuery("select c.name,s.name from Student s inner join s.classes c").list(); //不用写on...条件了,因为条件在影射文件中已经有了,我们已经配置了对象之间的关系,inner可以省略。
List students = session.createQuery("select c.name,s.name from Classes c left join c.students s").list(); //查出所有的班级,即以班级为准查询
List students = session.createQuery("select c.name,s.name from Classes c right join c.students s").list(); //查出所有的学生,即以学生为准查询
十一、统计查询
Long count (Long)session.createQuery("select count(*) from Student").uniqueResult(); //uniqueResult接口返回单一值
List students = session.createQuery("select c.name,count(s) from Student s join s.classes c group by c.name order by c.id").list(); //以班级分组,统计每个班级内的人数
十二、DML风格的操作(尽量少用,因为和缓存不同步)
session.createQuery("update Student s set s.name=? where s.id < ?")
.setParameter(0,"李四")
.setParameter(1,5)
.executeUpdate();
十三、抓取(fetch)
抓取:是抓取他的关联对象。
连接抓取(Join fetching):Hibernate通过在select语句使用outer join(外连接)来获得对象的关联实例或者关联集合。
查询抓取(Select fetching):另外发送一条select语句抓取当前的关联实体或集合。除非你显式的指定lazy="false"禁止延迟抓取,否则只有当你真正访问关联关系的时候,才会执行第二条select语句。
<many-to-one>中有一个fetch属性,默认为select
fetch="select",另外发送一条select语句抓取与当前对象关联的实体或集合。
fetch="join",hibernate会通过select语句使用外连接来加载其关联的实体或集合,此时lazy失效。
集合上的抓取:(取值:select、join、subselect)
fetch的默认值为select。
子查询抓取(subselect fetching)另外发送一条selct语句抓取在前面查询到(或抓取到的)的所有实体对象的关联集合(实质上就是一个子查询)。除非你显式的指定lazy="false"禁止延迟抓取,否则只有当你真正访问关联关系的时候,才会执行第二条select语句,会影响hql语句。
fetch="select":与上相同
fetch="join":与上相同
fetch="subselect":与fetch="select"结果相同。但是会影响hql,他形成的sql是一个子查询,比如
List classesList = session.createQuery(select c from Classes c where c.id in(1,2,3)).list();
行程的sql为:
select u.userId,u.userName t_user as u where u.id in (select classId from t_classes where classId in(1,2,3));
<class>标签内有一个batch-size属性,表示一条sql查多少对象。也可以用在集合标签上<set>
batch-size属性:可以批量加载实体类,以达到减少sql的目的
hibernate.jdbc fetch size:每次取多少数据,需要JDBC和底层数据库的支持。不会一次性把全部数据读入内存,而是按照一定的数量来批量读取相应的数据。建议值50
hibernate.jdbc batch size:批量更新,建议值30
十四、hibernate缓存
一级缓存很短和session的生命周期一致,一级缓存也叫 session 级的缓存或事务级缓存。
一级缓存无法取消,但可以管理,如:
session.clear()(清除缓存中的所有对象),session.evict(Object o)(清除缓存中的某个对象)。
session 间不能共享一级缓存。
session.flush(),flush()作用:执行sql语句,强制session将数据持久化。
session.clear(),clear()作用:清理缓存中的数据。
避免一次性大量的实体数据入库导致内存溢出:
* 先 flush,再 clear
二级缓存和一级缓存一样,只缓存对象,不缓存结果集对象。
二级缓存可以被所有的 session 共享。
十五、二级缓存的配置和使用:
1、导入EHchech 的 jar 包
2、在hibernate发布包的 etc 目录下将 ehcache.xml 文件 拷贝到 src 下,classpath 能搜索到的地方。
3、ehcache.xml 文件中的默认配置解释:
<defaultCache
maxElementsInMemory="10000" //缓存中可以存放10000个对象。
eternal="false" //表示可以失效。
timeToIdleSeconds="120" //空闲时间,即:距第一次访问,间隔多长时间没有再访问就失效了。
timeToLiveSeconds="120" //别缓存的对象在内存中存活的时间,单位是s(秒)。
overflowToDisk="true" //当发生溢出时,是否保存到磁盘上,磁盘目录为:
/>
<diskStore path="java.io.tempdir" /> //这个磁盘目录是一个操作我们开发的系统的临时目录,这个目录可以自定义。
4、在hibernate.cfg.xml中配置缓存
<property name="hibernate.cache.use_second_level_cache">true</property> //开启二级缓存,其实默认就是开启的,这里我们显式的开启。
5、指定缓存产品提供商
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
6.1、指定那个对象使用二级缓存(第一种方式)
在映像文件中的<id>标签前面使用<cache>标签
<cache usage="read-only" /> //只读缓存(即:当数据库中的数据发生变化时,不会更新缓存中的东西) read-write:表示当数据库中的数据发生变化时,同时更新缓存中的东西(效率低,因为更新时要使用到锁)。
6.2指定那个对象使用二级缓存(第二种方式)(推荐)
在hibernate.cfg.xml中指定
<class-cache class="com.lixinmin.test.User" usage="read-only" />
查询的时候,先查看一级缓存中是否有信息,后查看二级缓存。
使用二级缓存时在代码中使用一下方式(在查询之前设置session): //此项可选可不选
session.setCacheMode(CacheMode.GET); //只从二级缓存中读数据,而不写入
CacheMode.PUT //只向二级缓存中写入数据,而不从二级缓存中读数据
CacheMOde.NORMAL //从二级缓存中可读可写数据(默认值)
十六、查询缓存
缓存普通属性结果集,和实体ID
1、启用查询缓存
在hibernate.cfg.xml中开启,如下:
<property name="hibernate.cache.use_query_cache">true</property> //默认是关闭的
2、在配置文件中开启后还必须在程序中开启,如下:
Query query = session.createQuery("select s.name from Student s");
query.setCacheable(true);
List names = query.list();
十七、hibernate数据更新
托管状态的对象,只要我们发update它就又可以变成持久状态,是因为数据库中存在有和这个对象主键值相同的记录,因为主键所以才能联系起来。
我们更新一条记录时建议先将其加载上来再更新,这样只有我们更新的的字段才会改变,没有更新的字段查出来什么值发update时还是把原值给保存回去,
如果我们自己构造一个托管状态的对象(即:new一个对象,再把对象的主键值改成和我们要修改的记录的主键值相同的值),此时改变的字段会保存到数据库中
没有改变的字段,因为我们构造的托管状态的对象的属性默认都是空,所以发update时那些没有改变的字段值都会变成null
十八、hibernate表结构修改
若将hibernate.hbm2ddl.auto属性的值改为update,这时如果我们将映射文件中的某些属性的property的name值改变成另外一个名字,那么在数据库中就会
新增加一些和改后名字相同的字段,而之前的字段保留,但不再用了,插入数据时都将其值赋为null