Hibernate 概述 笔记

hibernate三种查询:

标准化查询
hql
原始sql查询

from user user where user.name like 'j%'

可以运用对象的导航技术,少了连接查询的麻烦

user.group.name=?

关键字不区分大小写

select name from student实体是需要区分大小写的.
简单属性的查询,返回的类型取决于属性的类型.
List a;
for(Iterator it = a.iterator();it.hasNext()){


查询多个属性,集合元素是对象数组
长度取决于属性个数.

select new Student(id,name) from Student
这里需要注意在实体里加入响应的构造函数.这里返回的是Student对象实体.可以使用别名.可以有as关键字.

from Student s

使用select查询实体对象必须采用as

select s from Student as s
select * from Student(实体)
这是不对的.但是可以使用count(*)

n+1问题损耗性能.(query.iterator)
1是指查询ID的select的语句
之后是根据ID发出select语句,迭代接口会利用缓存,即缓存存在时候不会发出select语句.

list只会往缓存放数据,不会使用缓存.所以多次查询都会重新发送sql,除非配置了查询缓存.

外置命名查询,这样sql是可配置的.具体放在vo的映射文件里头.
<query name="">
<![CDATA[from User]]>可以放到任意的映射文件里头
</query>


还可以在映射文件里加入过滤器查询条件
可以通过字符串的拼接方式,也可以使用占位符的方式.
方法连的查询是直接通过导航的形式.
也可以使用冒号的形式传递参数 like:myname
in(:aa)
setParamList("aa",new Object[]{1,3,3})


hibernate不太适合批量的操作,主要是缓存的数据和数据库的不一致.

分布式缓存比较麻烦.需要通过一定的方式同步数据
load和get会使用缓存
迭代查询普通属性,一级缓存不起作用,因为是缓存实体对象的.

load得到的是代理,使用时才发出sql语句.
session间不能共享缓存的数据,因为缓存伴随session的生命周期而存在.
save时会使用缓存.

session.clear()或者evict()方法(可以指定驱逐特定的对象,clear清楚所有缓存)
一级缓存无法实现自动分配.必须通过以上2种方法.

数组可以在声明时指定长度,但是内存中没有分配.当使用静态初始化时,即声明时立刻赋值,就不能在声明的内部指定长度.多维数组可以作同样的考虑.

session缓存也叫事务级缓存.

一对多和多对一映射是一致的,都是在多的一端加入字段.主要的差别是加载时查询级联数据有些不同.

hihernate一对多关联映射(单向Classes----->Student)

一对多关联映射利用了多对一关联映射原理

多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一
一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多

也就是说一对多和多对一的映射策略是一样的,只是站的角度不同

在一一端维护关系的缺点:
 * 如果将t_student表里的classesid字段设置为非空,则无法保存
 * 因为不是在student这一端维护关系,所以student不知道是哪个班的,
   所以需要发出多余的update语句来更新关系

<hibernate-mapping package="com.bjsxt.hibernate">
 <class name="Classes" table="t_classes">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <set name="students">
   <key column="classesid"/>
   <one-to-many class="Student"/>
  </set>
 </class>
</hibernate-mapping>

执行了save之后数据库是否有记录,与对象的ID的生成策略有关.uuid的方式,执行了save之后只是将user纳入了session的管理.显式调用flush后才能发出sql语句.commit之后才能真正提交数据.commit方法会自动调用flush方法.当生成策略是native时,save后数据库有记录,sql也发出.uuid的生成策略是hibernate执行的.native是数据库生成主键的方式.


脏读:一个事务读取的另一个事务的数据可能是错误的,原因往往是另一个数据在操作记录时发生了异常,回滚了事务.

不可重复读:一个事务两次或者多次读取一条记录时,结果不一致,原因往往是另一个事务正在修改该记录.一般可在特定条件的记录上加上悲观锁可以放在.这样的话并发性不是很好,但是提高了数据的准确性.

幻读:一个事务读取时,另一个事务正在往表里添加数据.再次读取时记录数增加.对整表加上悲观锁可以防止此类问题,但是所有涉及此表操作的事务都必须排队等待,影响了性能.

数据库的隔离级别:(以A和B两个事务为例)

未提交读.A往表插入数据,但是还未提交,B已经可以看到.当A进行事务回滚时,B原先看到的数据就是错误的,也就是所谓的脏数据.

提交读.事务提交时才能读取数据.不存在脏读,存在不可重复读(重复读的时数据与前面一次不一致),加入悲观锁可以避免,一般是加在了记录上.

可重复读.

序列化度.采用的是顺序队列的形式操作表.对整个表加了悲观锁.其他事务无法加入数据.

session.evict()是从缓存中将数据清除. 临时集合不同于缓存.flush清除临时集合的数据,同时更新缓存里一个实体的是否存在记录的属性更新为true.commit时需要在临时集合中查找数据.

hibernate对sql的批量执行是有一定的顺序的,先是insert,然后是update,再是delete.可以通过flush可以改变默认的执行顺序,满足业务上的特殊需求.

identity 是mysql主键的生成方式,sequence是oracle的.所以配置时要注意底层的数据库.identity的生成策略要求字段是数字型的,比如long,或者int等.


一对多的关联映射必须在其中的一端持有另外一端的引用.并且通过映射配置实现.多对一的关联映射,左边是多的关系.比如多个学生属于一个班级,那么多的一端就是学生.其实,一对多和多对一只是考虑的角度不一致.实际上没有本质的区别.

在多的一端加上映射标签
<many-to-one name="" column>
和properties并列出现.这个字段在导出表时会在多的一端加上该字段,并且作为外键参照一的一端的主键.
实际存储数据初始化对象时需要先初始化一的一端数据,即班级数据.当然,如果在多对一的映射标签里加上cascade属性,值设为all的话会在(删除修改保存)时级联保存相关的数据.删除操作时需要注意,因为不能因为删除了一个学生,就将班级删除.

级联的含义是对象的连锁操作.

加载数据(load或者get时)可以不配置级联关系.因为多对一的关联映射标签已经维持了这种关系.

持久对象不能引用瞬时对象.所以必须先save班级数据.

数据库本身的生成策略需要访问额外的表,需要加锁,并发性不怎么好.

实体类的设计原则:
需要一个无参数的构造方法
提供标识(可选)
使用非final,因为代理类的实现需要使用继承.
为持久化字段声明访问器

每次导出时表都被删除,然后重新创建.
如果需要表字段发生变化时才重新创建,需要在hibernate.cfg.xml文件里配置属性hibernate.hbm2dll.auto的值为update.

更改后以前的字段保留,存取数据时该字段为空.

一对一单双向映射.单向,访问时单方向的,双向式彼此都能互相访问.
主键关联,是一个对象的主键以外键的形式参照另一方的主键.

一对一的生成策略不再是uuid或者native,应该是foreign,同时应继续加入一个子标签<param name="propertites">属性值</param>

<one-to-one constrain="trued">默认根据主键加载.不会像多对一一样在多的一端的映射表里加入了字段.默认了cascade的属性.

hibernate一对一主键关联映射(单向关联Person---->IdCard)

一对一主键关联映射:让两个实体对象的id保持相同,这样可以避免多余的字段被创建

具体映射:

 <id name="id">
  <!-- person的主键来源idCard,也就是共享idCard的主键 -->
  <generator class="foreign">
   <param name="property">idCard</param>
  </generator>
 </id>
 <property name="name"/>
 <!-- one-to-one标签的含义,指示hibernate怎么加载它的关联对象,默认根据主键加载,
 constrained="true", 表明当前主键上存在一个约束,person的主键作为外键参照了idCard 
  -->
 <one-to-one name="idCard" constrained="true"/>


唯一外键关联时需要在一端多加入一个字段.他是多对一的一个特例,只需在多的一端的多余字段的唯一性设置为true就可以.

hibernate一对一唯一外键关联映射(单向关联Person---->IdCard)

一对唯一外键关联映射是多对一关联映射的特例

可以采用<many-to-one>标签,指定多的一端的unique=true,这样就限制了多的一端的多重性为一
通过这种手段映射一对一唯一外键关联

hibernate一对一唯一外键关联映射(双向关联Person<---->IdCard)

一对一唯一外键关联双向,需要在另一端(idcard),添加<one-to-one>标签,指示hibernate如何加载
其关联对象,默认根据主键加载person,外键关联映射中,因为两个实体采用的是person的外键维护的关系,
所以不能指定主键加载person,而要根据person的外键加载,所以采用如下映射方式:
<one-to-one name="person" property-ref="idCard"/>


领域模型,即对象模型
sessionFactory是线程安全的.session是非线程安全的.一个session的生命周期对应一个请求的业务操作.
只对一个数据库操作,称之为本地事务.
对于跨数据库的访问,需要用到JTA.

get查询对象不存在时返回null.不支持懒加载.
load调用时生成的是代理,真正使用时才发出sql语句.这就是延迟加载.其原理是代理方式.

load方法,当数据库不存在相应的数据时,查询的操作就会出现异常.

理解对象的状态,需要抓住三点,session的边界,对象,数据库是否存在记录

update时没有赋值的字段成为了null.所以做好先load上来.

测试实体对象的生命周期

junit简介:
 * 编写测试类xxxTest,需要继承TestCase
 * 编写单元测试方法,测试方法必须以test开头,测试方法不能含有参数和返回值,如:
   public void testHello1() {}
 * 最好单元测试的代码单独建立一个目录
 
了解Hibernate中CRUD操作

了解get和load的区别?
 * get不支持lazy,load支持lazy
 * 采用get加载数据,如果没有匹配的数据,返回null,而load则抛出异常
 
transient状态的特征?
 * 在数据库中没有与之匹配的数据
 * 没有纳入session的管理
 
persistent状态的特征?
 * persistent状态的对象在数据库中有与之匹配的数据
 * 纳入了session的管理
 * 在清理缓存(脏数据检查)的时候,会和数据库同步
 
detached状态的特征?
 * 在数据库中有与之匹配的数据
 * 没有纳入session的管理    
 
 orm 的m是map包括映射机制和缓存机制 ,maping只是一个简单的映射,范围比较小.

轻量级框架测试比较简单.EJB是重量级的,测试的时候要继承一些框架的接口,需要专门的部署,需要注册到JNDI对象树上,使用时还需要查找,很是麻烦.

可以建立jar库,此后不用每次进行相同的加入jar的操作.右键工程--properties--java--buildbath--user liberaris 

hibernate项目配置顺序

1、新建java项目

2、创建User Library,加入如下jar
 * HIBERNATE_HOME/hibernate3.jar
 * HIBERNATE_HOME/lib/*.jar
 * MySql jdbc驱动
 
3、创建hibernate配置文件hibernate.cfg.xml,为了便于调试最好加入log4j配置文件.里面是连接数据所需要的属性配置.

<hibernate-configuration>
<session-factory>
<properties name="">
</hibernate-configuration>
</session-factory>

其中还可以配置一个数据库语言适配器,通过hibernate的api能够自动翻译成合数据库相关的语句

4、定义实体类

5、定义User类的映射文件User.hbm.xml,一般放在一起.
主键需要配置生成策略.实体的属性与数据库字段有一定的映射机制.

6、将User.hbml.xml文件加入到hibernate.cfg.xml文件中
需要放到<propertis>属性映射的后面

7、编写hbm2ddl工具类,将实体类生成数据库表

package com.bjsxt.hibernate;

import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class ExportDB {

 public static void main(String[] args) {
  
  //读取hibernate.cfg.xml文件
  Configuration cfg = new Configuration().configure();//不调用configure方法时默认就调用properties文件.
  
  SchemaExport export = new SchemaExport(cfg);
  
  export.create(true, true);//
第一个true表示打印ddl语句.第二个true表示将脚本导入到数据库里.
 }
}
一个数据库对应一个sessionfactory;

Configuration cfg = new Configuration().configure();
  
  //创建SessionFactory
  SessionFactory factory = cfg.buildSessionFactory();

8、开发客户端
 
为了方便跟踪sql执行,在hibernate.cfg.xml文件中加入<property name="hibernate.show_sql">true</property>

hibernate的事务自动提交是false
session.beginTransaction();//手动开启事务
   
   User user = new User();
   user.setName("张三");
   user.setPassword("123");
   user.setCreateTime(new Date());
   user.setExpireTime(new Date());
   
   //保存数据
   session.save(user);
   
   //提交事务,需要得到事务的上下文
   session.getTransaction().commit();


session.getTransaction().rollback();//出现问题时回滚.

项目的分层:表示层-struts,
业务层-spring
持久层应该是透明的-hibernate(01年就出现了)
数据库

各层之间的依赖一般都是通过抽象进行的,抽象是一种规范,所以变化比较小.

数据若只是保存到磁盘文件上,一些事物操作就不存在,并发的问题也就无从解决.

没有h,很多jdbc语句都要自己写,而且很多都是重复.开发效率不高.

对象的继承关系,关系数据库里没有与之对应的概念.这叫做阻抗不匹配.

和h差不多的框架有:apache OJB,sun提出的一套标准--JDO,oracle提出的TOPLink,ibatis(只是对sql进行了轻量级的包装,严格意义上不属于orm框架)

h的优点:sql大部分不用写,直接操作对象.提高生产力,开发更为对象化,移植性很好(只需要更改配置),实现了透明持久化,无须继承h的任何类和任何接口,保存的对象是一个POJO对象,即没有任何的侵入性,属于轻量级的第三方框架.

适用情况:
简单对象的查询和修改.
批量的修改则不适合,主要是和缓存有关.

对象之间的关系复杂时不适合,

当要求使用数据库的特殊功能时也不适合. 因为sql是h自动生成的.所以实现的功能是比较有限的

猜你喜欢

转载自zengshaotao.iteye.com/blog/1734699