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自动生成的.所以实现的功能是比较有限的