• 1.说出orm的作用
• 即对象关系映射。它是一种用来完成对象模型到关系模型的映射技术。
• 就是把应用程序中的对象数据持久化到关系数据库的表的一种技术。
• ORM能利用面向对象的思想开放基于关系型数据库的应用程序,它的主要工作是将对象数据保存到关系数据库的表中,以及将关系数据库表中数据读入到对象中。
•
• 2简述使用Hibernate的基本流程。
• Hibernate的工作流程:
读取并解析配置文件
读取并解析映射信息,创建SessionFactory
打开Sesssion
创建事务Transation
持久化操作
提交事务
关闭Session
关闭SesstionFactory
• 3.叙述Hibernate应用的主要接口和类
• Configuration类
• 是Hibernate的入口,它负责配置并启动Hibernate。Hibernate通过Configuration的实例加载配置文件信息,然后读取指定对象关系映射文件的内容并创建SessionFactory实例。
• .SessionFactory接口
• 负责初始化Hibernate。一个SessionFactory实例对应一个数据库。应用程序从SessionFactory中获得Session实例。
• .Session接口
• Session被称为持久化管理器,负责管理与持久化相关的操作:存储、更新、删除和加载对象。
• .Transaction接口
• 是Hibernate框架的事务接口。它对底层的事务接口做了封装。包括:JDBC API和JTA。
•
• 4解释Hibernate全局配置文件中常用标记的使用(配置文件是什么及其作用)
<!DOCTYPEhibernate-configuration PUBLIC
"-//Hibernate/HibernateConfiguration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///user</property>
<property name="connection.username">root</property>
<property name="connection.password">1234</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="com/hbsi/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
全局配置文件中的各个标记的含义及其使用:
Hibernate.connection.url表示要链接的数据库地址
Hibernate.connection.driver_class表示要链接的数据库的驱动类
Hibernate.connection.username要连接的数据库的用户名
Hibernate.connection.password要连接的数据库的密码
Hibernate.dialect 表示要使用的数据库的类型
org.hibernate.dialect.MySQL5Dialectmysql数据库
org.hibernate.dialect.Oracle9Dialectoracle数据库
org.hibernate.dialect.SQLServerDialectSQLServer数据库
hibernate.hbm2ddl.auto
validate:加载hibernate时验证创建表结构
update:加载hibernate时自动更新数据库结构,如果表存在不用创建,如果不存在就创建。
create:每一次加载hibernate时都创建表结构
create-drop:加载hibernate时创建,退出时删除
•
• 5解释对象关系映射文件中常用标记的使用
•
• 6阐述常用对象标识符生成器的作用
increment 标识符生成器
i. increment 标识符生成器由 Hibernate以递增的方式为代理主键赋值
ii. Hibernate 会先读取NEWS 表中的主键的最大值, 而接下来向 NEWS 表中插入记录时, 就在max(id) 的基础上递增, 增量为 1.(带走+1)
iii. 适用范围:
• 由于increment 生存标识符机制不依赖于底层数据库系统, 因此它适合所有的数据库系统
• 适用于只有单个Hibernate 应用进程访问同一个数据库的场合
• OID 必须为long, int 或 short 类型, 如果把 OID 定义为byte 类型, 在运行时会抛出异长
b) identity 标识符生成器
i. identity 标识符生成器由底层数据库来负责生成标识符, 它要求底层数据库把主键定义为自动增长字段类型(加1带走)
ii. 适用范围:
• 由于identity 生成标识符的机制依赖于底层数据库系统, 因此, 要求底层数据库系统必须支持自动增长字段类型. 支持自动增长字段类型的数据库包括:DB2, Mysql, MSSQLServer, Sybase 等
• OID 必须为long, int 或 short 类型, 如果把 OID 定义为byte 类型, 在运行时会抛出异常
i. sequence 标识符生成器利用底层数据库提供的序列来生成标识符.
ii.Hibernate 在持久化一个 News 对象时, 先从底层数据库的news_seq 序列中获得一个唯一的标识号, 再把它作为主键值
• 由于sequence 生成标识符的机制依赖于底层数据库系统的序列, 因此, 要求底层数据库系统必须支持序列. 支持序列的数据库包括:DB2 Oracle 等
• OID 必须为long, int 或 short 类型, 如果把 OID 定义为byte 类型, 在运行时会抛出异常
i. native 标识符生成器依据底层数据库对自动生成标识符的支持能力, 来选择使用 identity, sequence 或 hilo 标识符生成器.
• 由于native 能根据底层数据库系统的类型, 自动选择合适的标识符生成器, 因此很适合于跨数据库平台开发
• OID 必须为long, int 或 short 类型, 如果把 OID 定义为byte 类型, 在运行时会抛出异常
i. hibernate和底层数据库都不帮助你生成主键,也就是说得自己在程序中手动的设置主键的值。
主键有一定的含义,需要根据业务产生的情况。
f) Uuid标识符生成器
i. Hibernate采用128位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符,这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间
ii. 使用范围:
主键是字符串,而且必须是唯一
•
• 7解释Hibernate数据类型在Java数据类型与SQL数据类型之间的桥接作用
•
• 8叙述Hibernate持久化生命周期及状态转换(Hibernate对象的三种状态是什么?)
•
• 瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
• 持久(persistent):数据库中可能有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
• 脱管(detached):数据库中可能有数据与之对应,但当前没有session与之关联;托管对象状态发生改变,hibernate不能检测到。
•
• 9编写一个CRUD示例来运用Session中的常用方法
• 10说说Hibernate中的update()和saveOrUpdate()的区别。
• public void update(Object object):重附脱管对象,并把它的状态更新到数据库表中。
•
• public void saveOrUpdate(Objectobject):同时具有save()和update()的功能。
•
• 11分类叙述出Set、Bag、List、Map的映射方式
• <set>元素:可以映射java.util.Set接口的属性,元素没有顺序且不允许重复。
•
• <list>元素:可以映射java.util.List接口的属性,有顺序,需要在集合属性对应的表中用一个额外的索引保存每个元素的位置。
•
• <bag> <idbag>元素:可以映射java.util.Collection接口的属性,元素可重复,但不保存顺序。
•
• <map>元素:可以映射java.util.Map接口的属性,元素以键/值对的形式保存,也是无序的。
•
• <primitive-array><array>:可以映射数组元素。
•
• 12编写订单和客户的单向多对一映射示例
订单:
Private int id;
Private String name;
Private Customer cus;
<hibernate-mapping package="com.hbsi.many2one">
<class name="Order"table="order">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
<many-to-one name="cus"class="Customer" column="cus_id"></many-to-one>
</class>
</hibernate-mapping>
客户:
Privateint id;
Private String name;
<hibernate-mapping package="com.hbsi.many2one">
<class name=" Customer "table=" customer ">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
</class>
• </hibernate-mapping>
• 13编写客户与订单的单向一对多映射示例
• 客户:
Private int id;
Private String name;
Private Set<Order> orders;
<class name=”Customer” talbe=”customer”>
<id name=”id” column=”id”>
<generator class=”native”>
</id>
<property name="name"column="name" type="string"/>
<set name=”orders” table=”orders” cascade=”save-update”>
<key column=”cus_id”/>
<one-to-manyclass=”Orders”/>
</set>
</class>
订单:
Private int id;
Private String name;
<hibernate-mapping package="com.hbsi.one2many">
<class name="Orders"table="orders">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
</class>
</hibernate-mapping>
• 14编写客户与订单的双向一对多映射示例
• 客户:
privateintid;
private String name;
• private Set<Order> orders;
<hibernate-mapping package="com.hbsi.one2manyboth">
<class name="Customer"table="customer">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
<set name="orders" inverse="true"cascade="save-update">
<key column="customer_id"/> <!-- 这里是顾客的外键,也是主键 就是外键的灵活应用 -->
<one-to-many class="Order"/>
</set>
</class>
订单;
privateintid;
private String no;
private Customer customer;
<hibernate-mapping package="com.hbsi.one2manyboth">
<class name="Order"table="orders">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="no"column="no" type="string"/>
<many-to-one name="customer"column="customer_id" cascade="save-update"/>
</class>
</hibernate-mapping>
• 15叙述inverse属性的作用
hibernate配置文件中有这么一个属性inverse,它是用来指定关联的控制方的。
inverse属性默认是false,若为false,则关联由自己控制,若为true,则关联
由对方控制
• 16编写客人与房间的单向一对一基于主键的映射示例
• Private int id;
• Private String name;
• Private room room;
<hibernate-mapping package="com.hbsi.one2one_pk">
<class name="Customer"table="customer_pk">
<id name="id"column="id">
<generator class="foreign">
<param name="property">idcard</param>
</generator>
</id>
<property name="name"column="name" type="string"/>
<one-to-one name="room"constrained="true" cascade="all"/>
</class>
• </hibernate-mapping>
• 房间:
Private int id;
Private Stringno;
<hibernate-mapping package="com.hbsi.one2one_pk">
<class name="IdCard"table="idcard_pk">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="no"column="no" type="string"/>
</class>
</hibernate-mapping>
• 17编写客人与房间的单向一对一基于外键的映射示例
• 客人:
• Private int id;
• Private String name;
• Private room room;
<hibernate-mapping package="com.hbsi.one2one">
<class name="Customer"table="customer">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
<many-to-one name="room"column="room_card" unique="true"cascade="save-update"/>
</class>
</hibernate-mapping>
房间:
privateintid;
private String no;
<hibernate-mapping package="com.hbsi.one2one">
<class name="IdCard"table="idcard">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="no"column="no" type="string"/>
</class>
</hibernate-mapping>
• 18编写客人与房间的双向一对一映射示例
• 客人:
• Private int id;
• Private String name;
• Private room room;
<hibernate-mapping package="com.hbsi.one2one">
<class name="Customer"table="customer">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
<many-to-one name="room"column="room_card" unique="true"cascade="save-update"/>
</class>
</hibernate-mapping>
• 房间:
Privateint id;
PrivateString no;
Private Customer customer;
<hibernate-mapping package="com.hbsi.one2oneboth_fk">
<class name="IdCard"table="idcard">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="no"column="no" type="string"/>
<one-to-one name="customer"property-ref="idcard"/>
</class>
</hibernate-mapping>
• 19编写用户和角色的单向多对多映射示例
• 用户:
• Private int id;
• Private String name;
• Private Set<Role> roles;
<hibernate-mapping package="com.hbsi.many2many">
<class name="Customer"table="customer">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
<set name="roles"table="customer_roles">
<key column="c_id"/>
<many-to-many class="Rolescolumn="r_id"/>
</set>
• </class>
角色:
Private intid;
PrivateString name;
<hibernate-mapping package="com.hbsi.many2many">
<class name="Roles"table="roles">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" type="string"/>
</class>
</hibernate-mapping>
• 20编写用户和角色的双向多对多映射示例
•
• 21叙述三种常用继承映射的优缺点
方式一:整个的继承体系就用一张表、
建立关系模型原则:描述一个继承关系只用一张表,也就是说子类所使用的表与父类相同
优缺点:首先表中引入的区分子类的字段,也就是包括了描述其他字段的字段。其次,如果某个子类的某个属性不能为空,那么在数据库一级不能设置该字段not null(非空),维护起来方便,只需要修改一个表,灵活性差,表中冗余字段会随着子类的增多而越来越多,在任何情况下,都只需处理一个表,对于单个对象的持久话操作只需要处理一个表
方式二:每个子类一张表,存放子类所特有的属性
建立关系模型原则:每个子类使用一张表,但这些子类所对应的表都关联到基类所对应的表中
优缺点:这种设计方式完全符合关系模型的设计原则,且不存在冗余,
维护起来比较方便,对每个类的修改只需要修改其所对应的表,灵活性很好,完全是参照对象继承的方式进行配置,对于父类的查询需要使用左外链接,对于子类查询需要使用内链接,对于子类的持久话至少要处理两个表
方式三:每个具体类一张表(union-subclass) ,保存是子类完整信息
建立关系模型原则:每个具体类对应一张表,有多少具体类就需要建立多少个独立的表
优缺点:这种设计方式符合关系模型的设计原则,但有表中存在重复字段的问题。如果需要对基类进行修改,则需要对基类以及该类的子类所对应的所有表都进行修改,映射的灵活性很大,子类可以包括基类属性在内的每一个属性进行单独配置,对于子类的查询只需要访问单独的表,对父类查询怎需要检索所有的表,对于单个对象持久话操作只需要处理一个表
•
22 Hibernate有哪几种查询数据的方式
• HQL查询、
支持动态绑定参数
支持投影查询、分页查询、连接查询、分组查询,子查询
内置了一些聚集函数
•
• Criteria查询、标准化条件查询,是比HQL更面向对象的查询语句。称为QBC
示例查询(QBE)根据一个给定的实例类实例来构建一个条件查询的方式。
先创建一个对象样板,然后检索出所有和这个样板相同的对象。
在查询表单中填写的项,可以封装成一个对象,这就是对象样板。
Native SQL Queries
原生SQL查询,就是指直接使用标准SQL语句或特定数据库的SQL进行查询。对原生SQL查询执行的控制是通过SQLQuery接口进行的,通过Session上调用createSQLQuery()来获取这个接口。
•
• 23阐述左连接查询和左连接抓取查询的异同
•
• 24叙述Criteria的使用步骤
•
• 25阐述事务管理的步骤
• 、JDBC事务
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口(java.sql.Connection )提供了两种事务模式:自动提交和手工提交。
java.sql.Connection 提供了以下控制事务的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。
JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
2、JTA(Java Transaction API)事务
JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。
JTA允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
如果计划用 JTA 界定事务,那么就需要有一个实现javax.sql.XADataSource 、 javax.sql.XAConnection 和javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。XAConnection s 是参与 JTA 事务的 JDBC 连接。
您将需要用应用服务器的管理工具设置 XADataSource 。从应用服务器和 JDBC 驱动程序的文档中可以了解到相关的指导。
J2EE 应用程序用 JNDI 查询数据源。一旦应用程序找到了数据源对象,它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的连接。
XA 连接与非 XA 连接不同。一定要记住 XA 连接参与了JTA 事务。这意味着 XA 连接不支持 JDBC 的自动提交功能。同时,应用程序一定不要对 XA 连接调用 java.sql.Connection.commit() 或者java.sql.Connection.rollback() 。相反,应用程序应该使用 UserTransaction.begin()、UserTransaction.commit() 和 serTransaction.rollback() 。
3、容器事务
容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。相对编码实现JTA 事务管理,我们可以通过EJB容器提供的容器事务管理机制(CMT)完成同一个功能,这项功能由J2EE应用服务器提供。这使得我们可以简单的指定将哪个方法加入事务,一旦指定,容器将负责事务管理任务。这是我们土建的解决方式,因为通过这种方式我们可以将事务代码排除在逻辑编码之外,同时将所有困难交给 J2EE容器去解决。使用EJB CMT的另外一个好处就是程序员无需关心JTA API的编码,不过,理论上我们必须使用EJB。
三种事务差异:
1、JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。
2、JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
3、容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
• 26解释并发引起的各种问题
• 丢失更新 脏读 不可重复读 幻读
•
• 27编写程序来演示乐观锁的作用
• 乐观锁是假定当前事务操作数据库资源时,不会有其他事务同时访问,因此不做数据库层次上的锁定。为了维护正确数据,hibernate用version和timestamp来实现。
使用版本号进行版本控制
在持久化类中定义一个version属性。类型只能是整型的。
在映射文件中添加<version>标签,注意一定要放在<id>元素后面
public void testTx1() {
Session session = HibernateSessionFactory.getSession();
Transaction tx = session.beginTransaction();
Student stu = null;
try {
stu = (Student)session.get(Student.class, 1);
System.out.println(stu.getName());
System.out.println("version=" + stu.getVersion());
stu.setName("在第一个事务中修改");
session.getTransaction().commit();
}
catch (HibernateException he) {
tx.rollback();
he.printStackTrace();
}
session.close();
// 操作完毕
System.out.println("tx1操作完成后------");
System.out.println(stu.getName());
System.out.println("version=" + stu.getVersion());
}
• 28叙述悲观锁的实现原理及使用步骤
• 悲观锁是假定当前事务操作数据资源时,一定有其他事务同时访问该数据资源,所以先锁定资源。
• 一般实现方式是由数据库来实现,采用独占锁来锁定资源。使用get(),load(),是可以显示指定锁定模式:LockMode.UPGRADE
•
• Session.get(Student.class,1,LockMode.UPGRADE);
•
•
• 29分析各种延迟加载策略的异同点
• 延迟加载
• 延迟加载(load)是Hibernate为提高程序执行效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
对象的延迟加载
Hibernate延迟加载之实体对象的延迟加载:
如果想对实体对象使用延迟加载,必须要在实体的映射配置文件中进行相应的配置,如下所示:
1. <Hibernate-mapping><class name=”com.neusoft.entity.User”
2. table=”user” lazy=”true”>
3. ……</class></Hibernate-mapping>
通过将class的lazy属性设置为true,来开启实体的延迟加载特性。如果我们运行下面的代码:
1. Useruser=(User)session.load(User.class,”1”);(1)
2. System.out.println(user.getName());(2)
实体的延迟加载是用通过中间代理类完成的,所以只有session.load()方法才会利用实体延迟加载,因为只有session.load()方法才会返回实体类的代理类对象。
对象里的属性延迟加载
1. <Hibernate-mapping>
2. <class name=”com.neusoft.entity.User” table=”user”>……
3. <property name=”resume” type=”java.sql.Clob”
4. column=”resume” lazy=”true”/>
5. </class></Hibernate-mapping>
通过对<property>元素的lazy属性设置true来开启属性的延迟加载,在Hibernate3中为了实现属性的延迟加载,使用了类增强器来对实体类的Class文件进行强化处理,通过增强器的增强,
1. String sql=”fromUseruserwhereuser.name=’zx’ ”;
2. Query query=session.createQuery(sql); (1)
3. List list=query.list();
4. for(int i=0;i<list.size();i++)
5. {Useruser=(User)list.get(i);
6. System.out.println(user.getName());
7. System.out.println(user.getResume()); (2)
8. }
当执行到(1)处时,会生成类似如下的SQL语句:
1. Select id,age,namefromuserwherename=’zx’;
这时Hibernate会检索User实体中所有非延迟加载属性对应的字段数据,当执行到(2)处时,会生成类似如下的SQL语句:
1. Select resume fromuserwhere id=’1’;
这时会发起对resume字段数据真正的读取操作。
集合延迟加载
Hibernate延迟加载之集合类型的延迟加载:
1. <Hibernate-mapping>
2. <class name=”com.neusoft.entity.User” table=”user”>…..
3. <setname=”addresses” table=”address” lazy=”true”
4. inverse=”true”> cache usage=”read-only”/><<keycolumn=”user_id”/>
5. <one-to-many class=”com.neusoft.entity.Arrderss”/></set>
6. </class></Hibernate-mapping>
通过将<set>元素的lazy属性设置为true来开启集合类型的延迟加载特性。我们看下面的代码:
1. Useruser=(User)session.load(User.class,”1”);
2. Collection addset=user.getAddresses(); (1)
3. Iterator it=addset.iterator(); (2)
4. while(it.hasNext()){Address address=(Address)it.next();
5. System.out.println(address.getAddress());}
这里我们应用了<cache usage=”read-only”/>配置,如果采用这种策略来配置集合类型,Hibernate将只会对数据索引进行缓存,而不会对集合中的实体对象进行缓存。
• 30鉴别各种抓取策略的异同点
• 通过配置“抓取策略”来直接影响session的get()和load()方法的查询效果。
• 1.单端关联<many-to-one><one-to_one>上的抓取策略。
• 可以给单端关联的映射元素添加fetch属性。fetch属性有2个可选值
• select:作为默认值,它的策略是当需要使用到关联对象的数据时,另外单独发送一条select语句抓取当前对象的关联对象的数据。即延迟加载。
• join:它的策略是在同一条select语句使用连接来获得对象的数据和它关联对象的数据,此时关联对象的延迟加载失效。
• 集合属性上的抓取策略
• 在集合属性的映射元素上可以添加fetch属性,它有3个可选值。
• select:作为默认值,它的策略是当需要使用所关联集合的数据时,另外单独发送一条select语句抓取当前对象的关联集合,即延迟加载。
• join:在同一条select语句使用连接来获得对方的关联集合。此时关联集合上的lazy会失效。
• subselect:另外发送一条查询语句(或子查询语句)抓取在前面查询到的所有实体对象的关联集合。这个策略对HQL的查询也起作用。
关联级别检索策略
•
• 31简述hibernate的缓存机制
• 缓存的作用 就是降低应用程序直接读写永久性数据存储源的频率,从而增强应用的运行性能。
• 缓存的实现不仅需要作为物理介质的硬件(内存),同时还需要用于管理缓存并发访问和过期等策略的软件。
• 缓存的范围分为以下三类:
• 1)事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。
• 2)进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。
• 3)集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据一致性。对于大多数应用来说,应该慎用集群范围的缓存,因为访问的速度并不一定比直接访问数据库数据的速度快很多。
• 缓存的并发访问策略:
• 1)事务型(Transactional)策略
• 2)读写型(Read-Write)策略
• 3)非严格读写型(Nonstrict-read-write)策略
• 4)只读型(Read-only)策略
•
• 32归纳一级缓存的管理方式
Session的CRUD方法及调用查询接口的list(),iterate()等方法时
,不存在加入到缓存中,存在就直接使用,不去加载数据库里了,当Hibernate清理缓存时默认的是提交事务的时候,关闭session时清除所有的对象。
一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用
evict(Object obj):从缓存中清除指定的持久化对象。
clear():清空缓存中所有持久化对象。
flush(): 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步执行一些列sql语句,但不提交事务。
commit():先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。
当做批量插入或批量更新时,必须通过经常调用Session的flush()以及稍后调用clear()来控制一级缓存的大小,这样内存才能保证足够的空间。
• 33session的load()和get()的区别。
1. load()方法从来就是假定数据在数据库中是存在的,在使用时如果不存在则会抛出ObjectNotFoundException;而get()方法不会假定数据在数据库中存在,如果不存在则返回null
2. load()方法返回的是实体类的代理类,因此load()可以使用延迟加载策略来加载对象
get()方法返回的不一定是实体类,可能是代理类,因为get()方法如果在session缓存(一级缓存)中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象。
3. load()方法查询数据时会先从session缓存(一级缓存)中查找,如果没有找到则会创建代理类,该代理类仅仅初始化了OID属性,当第一次访问其他属性值时,则会依次从二级缓存-->数据库查找,直到找到数据,最后将所有属性值赋给代理类。而get()方法则会直接按照一级缓存-->二级缓存-->数据库的顺序查找。
4.也许别人把数据库中的数据修改了,load如何在缓存中找到了数据,则不会再访问数据库,而get则会返回最新数据
• 34说明Hibernate应用优化的手段
一、批量修改和删除
在Hibernate 2中,如果需要对任何数据进行修改和删除操作,都需要先执行查询操作,在得到要修改或者删除的数据后,再对该数据进行相应的操作处理。在数据量少的情况下采用这种处理方式没有问题,但需要处理大量数据的时候就可能存在以下的问题:
占用大量的内存。
需要多次执行update/delete语句,而每次执行只能处理一条数据。
以上两个问题的出现会严重影响系统的性能。因此,在Hibernate 3中引入了用于批量更新或者删除数据的HQL语句。这样,开发人员就可以一次更新或者删除多条记录,而不用每次都一个一个地修改或者删除记录了。
如果要删除所有的User对象(也就是User对象所对应表中的记录),则可以直接使用下面的HQL语句:
delete User
而在执行这个HQL语句时,需要调用Query对象的executeUpdate()方法,具体的实例如下所示:
StringHQL="delete User";
Queryquery=session.createQuery(HQL);
intsize=query.executeUpdate();
采用这种方式进行数据的修改和删除时与直接使用JDBC的方式在性能上相差无几,是推荐使用的正确方法。
如果不能采用HQL语句进行大量数据的修改,也就是说只能使用取出再修改的方式时,也会遇到批量插入时的内存溢出问题,所以也要采用上面所提供的处理方法来进行类似的处理。
二、使用SQL执行批量操作
在进行批量插入、修改和删除操作时,直接使用JDBC来执行原生态的SQL语句无疑会获得最佳的性能,这是因为在处理的过程中省略或者简化了以下处理内容:
● HQL语句到SQL语句的转换。
● Java对象的初始化。
● Java对象的缓存处理。
但是在直接使用JDBC执行SQL语句时,有一个最重要的问题就是要处理缓存中的Java对象。因为通过这种底层方式对数据的修改将不能通知缓存去进行相应的更新操作,以保证缓存中的对象与数据库中的数据是一致的。
三、提升数据库查询的性能
数据库查询性能的提升也是涉及到开发中的各个阶段,在开发中选用正确的查询方法无疑是最基础也最简单的。
1 、SQL语句的优化
使用正确的SQL语句可以在很大程度上提高系统的查询性能。获得同样数据而采用不同方式的SQL语句在性能上的差距可能是十分巨大的。
由于Hibernate是对JDBC的封装,SQL语句的产生都是动态由Hibernate自动完成的。Hibernate产生SQL语句的方式有两种:一种是通过开发人员编写的HQL语句来生成,另一种是依据开发人员对关联对象的访问来自动生成相应的SQL语句。
至于使用什么样的SQL语句可以获得更好的性能要依据数据库的结构以及所要获取数据的具体情况来进行处理。在确定了所要执行的SQL语句后,可以通过以下三个方面来影响Hibernate所生成的SQL语句:
HQL语句的书写方法。
查询时所使用的查询方法。
对象关联时所使用的抓取策略。
2 、使用正确的查询方法
在前面已经介绍过,执行数据查询功能的基本方法有两种:一种是得到单个持久化对象的get()方法和load()方法,另一种是Query对象的list()方法和iterator()方法。在开发中应该依据不同的情况选用正确的方法。
get()方法和load()方法的区别在于对二级缓存的使用上。load()方法会使用二级缓存,而get()方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。在使用中,对使用了二级缓存的对象进行查询时最好使用load()方法,以充分利用二级缓存来提高检索的效率。
list()方法和iterator()方法之间的区别可以从以下几个方面来进行比较。
执行的查询不同
list()方法在执行时,是直接运行查询结果所需要的查询语句,而iterator()方法则是先执行得到对象ID的查询,然后再根据每个 ID值去取得所要查询的对象。因此,对于list()方式的查询通常只会执行一个SQL语句,而对于iterator()方法的查询则可能需要执行N+1 条SQL语句(N为结果集中的记录数)。
iterator()方法只是可能执行N+1条数据,具体执行SQL语句的数量取决于缓存的情况以及对结果集的访问情况。
缓存的使用
list()方法只能使用二级缓存中的查询缓存,而无法使用二级缓存对单个对象的缓存(但是会把查询出的对象放入二级缓存中)。所以,除非重复执行相同的查询操作,否则无法利用缓存的机制来提高查询的效率。
iterator()方法则可以充分利用二级缓存,在根据ID检索对象的时候会首先到缓存中查找,只有在找不到的情况下才会执行相应的查询语句。所以,缓存中对象的存在与否会影响到SQL语句的执行数量。
对于结果集的处理方法不同
list()方法会一次获得所有的结果集对象,而且它会依据查询的结果初始化所有的结果集对象。这在结果集非常大的时候必然会占据非常多的内存,甚至会造成内存溢出情况的发生。
iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。因此在访问中可以控制缓存中对象的数量,以避免占用过多缓存,导致内存溢出情况的发生。使用iterator()方法的另外一个好处是,如果只需要结果集中的部分记录,那么没有被用到的结果对象根本不会被初始化。所以,对结果集的访问情况也是调用iterator()方法时执行数据库SQL语句多少的一个因素。
所以,在使用Query对象执行数据查询时应该从以上几个方面去考虑使用何种方法来执行数据库的查询操作。
四、使用正确的抓取策略
所谓抓取策略(fetching strategy)是指当应用程序需要利用关联关系进行对象获取的时候,Hibernate获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL或条件查询中声明。
Hibernate 3定义了以下几种抓取策略。
连接抓取(Join fetching)
连接抓取是指Hibernate在获得关联对象时会在SELECT语句中使用外连接的方式来获得关联对象。
查询抓取(Select fetching)
查询抓取是指Hibernate通过另外一条SELECT语句来抓取当前对象的关联对象的方式。这也是通过外键的方式来执行数据库的查询。与连接抓取的区别在于,通常情况下这个SELECT语句不是立即执行的,而是在访问到关联对象的时候才会执行。
子查询抓取(Subselectfetching)
子查询抓取也是指Hibernate通过另外一条SELECT语句来抓取当前对象的关联对象的方式。与查询抓取的区别在于它所采用的SELECT语句的方式为子查询,而不是通过外连接。
批量抓取(Batch fetching)
批量抓取是对查询抓取的优化,它会依据主键或者外键的列表来通过单条SELECT语句实现管理对象的批量抓取。
以上介绍的是Hibernate 3所提供的抓取策略,也就是抓取关联对象的手段。为了提升系统的性能,在抓取关联对象的时机上,还有以下一些选择。