`
q272156430
  • 浏览: 270242 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

hibernate 笔记

阅读更多

Hibenrate同时支持XML格式的配置文件和传统的properties文件配置方式.

<hibernate-configuration>
     <session-factory>
          <property name="hibernate.connection.url">jdbc:mysql://localhost/sample</property>
   <property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>
   <property name="hibernate.connection.username">User</property>
   <property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
   <property name="hibernate.show_sql">True</property>
   <property name="hibernate.user_outer_join">True</property>
   <property name="hibernate.transaction.factory_class">
  net.sf.hibernate.transaction.JDBCTransactionFactory
   </property>
   <mapping resource="com/redsaga/quickstart/TUser.hbm.xml"/>
     </session-factory>

</hibernate-configuration>


Configuration config=new Configuration();//用配置文件来创建并初始化一个Configuration
config.addClass(TUser.class);
config.addFile("TUser.hbm.xml");//得到一个包含了所有Hibernate 运行期参数的Configuration实例.
SessionFactory sessionFactory=config.buildSessionFactory();
Session session=sessionFactory.openSession();
Transaction tran=session.beginTransaction();

Hibernate Session与Hibernate,相当于JDBC Connection 之与JDBC

session.flush()方法把缓存中的数据刷新到数据库.

Configuration类负责管理Hibernate的配置信息.Hibernate 运行时需要获取一些底层实现的基本信息.一般只在获取SessionFactory时需要涉及,当SessionFactory实例创建之后,由于配置信息已经由Hibernate绑定在返回的SessionFacoty之中,因此一般情况下无需再对其进行操作.

SessionFactory一旦构造完毕,即被赋予特定的配置信息.之后config的任何变动将不会影响到已经创建的SessionFactory
SessionFactory中保存了当前数据库配置的所有映射关系.同时也负责维护当前的二级数据缓存和StatementPool SessionFacoty采取了纯程安全的设计,可由多个线程并发调用,大多数情况下,一个应用中针对一个数据库共享一个SessionFactory即可.

Hibernate3 Session接口的定义中已经取消了find方法.Session是非线程安全的,一个Session实例同时可由一个线程使用.,同一个Session实例的多线程并发调用将导致难以预知的错误.

Save:
session.save(user);
Get:
session.get(TUser.class,new Integer(1));
delete:
session.delete(user);
session.delete("from TUser where id=1");
session.createQuery("delete TUser where id=1").executeUpdate();
#########################################################
Query:

String hql="from TUser user where user.name like ?";
Query query=session.createQuery(hql);
query.setParameter(0,"Cartier");
List list=query.list();
###################################################
Criteria:

Criteria criteria=sesison.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Cartier"));
List list=criteria.list();

Query和Criteria都是Hibernate数据查询的接口,提供了对查询条件的封装机制.
#############################################################################
在Hibernate中,可以设置两种数据库访问策略:JDBC,由Hibernate来完成连接管理;JNDI完成数据库连接获取.
/////JDBC 可以为其指定数据库连接池(默认,C3P0,DBCP,Proxool)
hibenrate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql://localhost/sample
hibernate.connection.username root
hibernate.connection.password
////JNDI
hibernate.connection.datasource jdbc/test
hibernate.connection.username user
hibernate.connection.password pwd
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
###########################################################
为了使用Hibernate和Transaction API,必须通过hibernate.transaction.factory_class属性指定一个Transaction实例工厂.Transaction API隐藏了底层的事务机制,允许Hibernate代码在受管制和非受管制的环境下都可以运行.
/////JDBC事务处理机制
hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
/////JTA
hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory

hibernate.dialect     数据库适配器,用于对特定数据库提供支持,其中包含特定数据库特性的实现.
hibernate.default_schema  在生成的SQL中,schema/tablespace的全限定名
hibernate.session_factory_name 把sessionFactory绑定到JNDI中的名称
hibernate.max_fetch_depth 对单要联合(一对一,多对一),设置外连接的最大深度,如果是0将关闭默认的外连接抓取
hibernate.jdbc.fetch_size 非零值,用来设置JDBC获取的记录条数
hibernate.jdbc.batch_size 非零值,指定了Hibernate进行每次批量提交阈值
hibernate.jdbc.use_scrollable_resultset 设置是否允许Hibernate使用JDBC2提供的可滚动结果集.只有在使用用户自行
提供的JDBC连接时,这个数才是必需的,否则Hibernate会根据连接的元数据自行判定.
hibernate.jdbc.use_get_generated_keys 是否允许使用JDBC3的PreparedStatement.getGeneratedKeys()在插入后获取数据库自身生成的key
hibernate.cglib.use_reflection_optimizer 是否使用CGLIB来代替运行时反射操作
hibernate.jndi.<propertyName> 把propertyName这个属性传递给JNDI InitialContextFactory去.
hibernate.connection.isolation 事务隔离级别
hibernate.connection.<propertyName> 把propertyName这个JDBC属性传递给DriverManager.getConnection();
hibernate.connection.provider_class 指定一个自定义的ConnectionProvider类名
hibernate.cache.provider_class 指定一个自定义的CacheProvider缓存提供者的类名
hibernate.cache.use_minimal_puts 是否优化第二级缓存操作,最小化缓存写入操作(适用于集群缓存)
hibernate.cache.use_query_cache 是否打开查询缓存(依然需要针对每个查询设置cacheable属性)
hibernate.cache.regin_prefix 指定一个自定义的TransactionFactory类名,Hibernate Transaction API将会使用(默认是JDBCTransactionFactory)
jta.UserTransaction JTATransactionFactory用来从应用服务器获取JTA UserTransaction的JNDI名
hibernate.transaction.manager_lookup_class TransactionManagerLookup的类名---当在JTA环境中启用JVM级缓存时使用
hibernate.query.substitutions 把Hibernate查询中的一些短语替换为SQL短语(函数/字符)
hibernate.show_sql 是否把执行的SQL语句输出到控制台
hibernate.hbm2dll.auto在SessionFactory创建后,自动输出schema创建语句到数据库,和create-drop同时使用的话,数据库schema会在SessionFactory显式关闭后被drop掉
###########################################################
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd>
<hibernate-mapping>
     <class name="com.redsaga.hibernate.db.entity.TUser" table ="T_USER">
          <id name="id" column="id" type="java.lang.Integer">
        <generator class="native"/>
   </id>
   <property name="name" column="name" type="java.lang.String"/>
   <property name="age" type="java.lang.Integer" column="age"/>
     </class>
</hibernate-mapping>

主键生成类型:
Assigned:
主键由应用逻辑产生,数据交由Hibernate保存时,主键值已经设置完毕,无需Hibernate干预;
hilo:
通过hi/lo算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态.
seqhile:
通过hi/lo算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适合于支持Sequence为数据库,如Oracle
increment:
主键按数值顺序递增,此方式的实现机制为在当前应用实例中维持一个变量,以保存首当前的最大值,之后每次需要生成主
键的时候将此值加1作为主键.
identity:
采用数据库提供的主键生成机制,如SQL Server,MySQL中的自增主键生成机制;
sequence:
采用数据库提供的sequence机制生成主键,如Oracle Sequence.
native:
由Hibernate根据数据库适配器中的定义,自动采用identity,hilo,sequence的其中一种作为主键生成方式
uuid.hex:
由Hibernate基于128位惟一值产生算法,根据当前设置IP,时间,JVM启动时间,内部自增量等4个参数生成16进制数作为主键
uuid.string:
与uuid.hex类似,只是生成的主键未进行编码.在某些数据库中可能出现问题
foreign:
使用外部关键字段作为主键
select:
Hibernate 3中新引入的主键获取机制,主要针对遗留系统的改造工程;
######################################################################
动态模型:即通过通用数据容器(如Map)对库表记录进行表达.
<class entity-name="DynamicUserMap" table="T_User"/>
session=sessionFactory.openSession().getSession(EntityMode.MAP);//Session操作的是Map类型数据
Map userMap=new HashMap();
userMap.put("name","Ken");
userMap.put("age",new Integer(25));
Transaction tx=sesison.beginTransaction();
session.save("DynamicUserMap",userMap);
tx.commit();
##########################################自定义数据类型
UserType和CompositeUserType是Hibernate中提供的类型定义接口.根据这个接口,我们可以实现自定义的数据类型.
public class TUser implements Serializable{
 private List email;
 ...
}
<property name=email" column="email" type="com.redsaga.hibernate.db.entity.EMailList"/>
List list=user.getEmail();
list.add("sss@126.com");
list.add("aas@126.com");
session.save(user);        
#################################复合主键
通过composite-id节点对复合主键进行定义.
<composite-id>
    <key-property name="lastname" column="lastname" type="string"/>
    <key-property name="firstname" column="firstname" type="string"/>
</composite-id>
Hibernate要求复合主键类实现equals和hashcode方法,以作为不同数据之间识别的标志.
TUser包含了复合主键数据,本身也扮演着"主键类"的角色.
TUser user=new TUser();
user.setFirstname("Kevin");
user.setLastname("Shark");
user=(TUser)session.load(TUser.class,user);

主键类:
public class TUserPK implements Serializable{
    private String firstname;
    private String lastname;
    .....
}
<composite-id name="userpk" class="TUserPK">
 <key-property name="lastname" column="lastname" type="string"/>
 <key-property name="firstname" column="firstname" type="string"/>
</composite-id>

完整的composite-id节点包含如下内容:
<composite-id name="propertyName" class="ClassName" unsaved-value="any|none">
 <key-property name="propertyName" type="typename" column="column_name"/>
 <key-many-to-one name="propertyName" class="ClassName" column="column_name"/>
</composite-id>

Blob采用单字节存储,适合保存二进制数据、Clob采用多字节存储,适合保存大型文本数据。
<property name="image" column="image" type="java.sql.Blob"/>
<property name="resume" column="resume" type="java.sql.Clob"/>

private Blob image;
private Clob resume;
////Blob, Clob创建并保存
TUser user=new TUser();
user.setAge(new Integer(20));
user.setName("Shark");
FileInputStream imgis=new FileInputStream("C:\\inimage.jpg");
Blob img=Hibernate.createBlob(imgis);
user.setImage(img);
Clob resume=Hibernate.createClob("This is Clob");
user.setResume(resume);
Transaction tx=session.beginTransaction();
session.save(user);
tx.commit();
通过Hibernate.createBlob和Hibernate.createClob创建了对应的Blob和Clob对象.
////Blob,Clob读取
TUser user=(TUser)session.load(TUser.class,new Integer(3));
System.out.println("User resume=>"+resume.getSubString(1,(int)resume.length()));
Blob img=user.getImage();
InputStream is=img.getBinaryStream();
FileOutputStream fos=new FileOutputStream("C:\\outimage.jpg");
byte[] buf=new byte[102400];
int len;
while((len=is.read(buf))!=-1){
 fos.write(buf,0,len);
}
fos.close();
is.close();
////Oracle Blob/Clob字段本身拥胡一个游标(cursor),,JDBC必须通过游标对Blob/Clob字段进行操作,在BLOB/Clob字段被创建之前,我们无法获取游标句柄,这也
就意味着,我们必须首先创建一个空Blob/Clob字段,再从这个空Blob/Cob字段获取游标,写入我们所期望保存的数据.
TUser user=new TUser();
user.setAge(new Integer(20));
user.setName("Shark");
user.setImage(Hibernate.createBlob(new byte[1]));
user.setResume(Hibernate.createClob(" "));
Transaction tx=session.beginTransaction();
session.save(user);
session.flush();
session.refresh(user,LockMode.UPGRADE);
Blob blob=user.getImage();
OutputStream out=blob.getBinaryOutputStream();
FileInputStream imgis=new FileInputStream("C:\\inimage.jpg");
byte[] buf=new byte[102400];
int len;
while((len=imgis.read(buf))>0){
 out.write(buf,0,len);
}
imgis.close();
out.close();

Clob clob=user.getResume();
Writer writer=clob.getCharacterOutputStream();
writer.writer("this is my resume");
writer.close();
session.save(user);
tx.commit();
////运用自定义数据类型,我们可以对Oracle的Blob/Clob字段进行抽象,并以其作为所有Oracle Blob/Clob字段的映射类型:
对于单表的对象细分,在Hibernate中可借助Component节点的定义完成.
Component与实体对象的根本差别就在于Component没有标识,它作为一个逻辑组成,完全从属于实体对象.
<component name="name" class="com.redsaga.hibernate.db.entity.Name">
 <property name="firstname" type="string" column="firstname"/>
 <property name="lastname" type="string" column="lastname"/>
</component>
面向性能的粒度细分:
public class TUserInfo implements Serializable{
 private Integer id;
 private String name;
 private Integer age;
 .....
}
public class TUserProfile extends TUserInfo implements Serializable{
 private Blob image;
 private Clob resume;
 .....
}
对于无需要resume/image数据的应用逻辑而言,我们可以通过TUserInfo对数据进行加载,而对于需要的我们可以通过TUsrProfile进行处理.
<class name="com.redsaga.hibernate.db.entity.TUserProfile" table="T_USER" polymorphism="explicit">
 <id name="id" column="id" type="java.lang.Integer">
  <generator class="native">
 </id>
 <property name="name" type="java.lang.String" column="name"/>
 <property name="age" type="java.lang.Integer" column="age"/>
 <property name="image" column="image" type="java.sql.Blob"/>
 <property name="resume" column="resume" type="java.sql.Clob"/>
</class>
通过polymorphism="explicit"声明一个显式多态关系,声明为显式多态的类,只有在明确指定类名的时候才会返回此类实例.
////继承
<class name="com.redsaga.hibernate.db.entity.TItem" table="T_ITEM">
 <joined-subclass name="com.redsaga.hibernate.db.entity.TDVD" table="T_DVD">
  <key column="id"/>
  <property name="regionCode" column="regioncode"/>
 </joined-subclass>
</class>
/////discriminator
<discriminator column="category" type="string"/>
<subclass name="com.redsaga.hibernate.db.entity.TBook" discriminator-value="1">
 <property name="pageCount" column="pageCount"/>
</subclass>
####################################################
One-to-One
////主键关联:
TUser:
<one-to-one name="passport" class="com.redsaga.hibernate.db.entity.TPassport" cascade="all" outer-join="true"/>
TPassport:
<id name="id" column="id">
 <generator class="foreign">
  <param name="property">user</param>
 </generator>
</id>
<one-to-one name="user" class="com.redsaga.hibernate.db.entity.TUser" constrained="true"/>//约束
cascade(级联关系设置)
级联指的是当主控方执行操作时,关联对象是否同步执行同一操作.如对主控对象调用save-update或delete方法时,是否同时对关联对象进行save-update或delete
##############唯一外键关联:
<many-to-one name="group" class="com.redsaga.hibernate.db.entity.TGroup" column="GROUP_ID" unique="true"/>  单向一对一
/////双向一对一
<one-to-one name="user" class=com.redsaga.hibernate.db.entity.TUser" property-ref="group"/>
############################一对多
单向一对多关系只需要在一方进行配置,双向一对多关系需要在关联双方均加以配置
TUser:
<set name="addresses" table="t_address" cascade="all" order-by="zipcode asc">
 <key column="user_id">
 <one-to-many class="com.redsaga.hibernate.db.entity.TAddress"/>
</set>
双向一对多关联:
在主控方配置单向一对多关系,在被控方配置与其对应的多对一关系:
<class name="com.redsaga.hibernate.db.entity.TUser" tabe="t_user" dynamic-update="true" dynamic-insert="true">
 <set name="addresses" table="t_address" lazy="false" inverse="true" cascade="all" sort="unsorted" order-by="zipcode asc">
  <key column="user_id"/>
  <one-to-many class="com.redsaga.hibernate.db.entity.TAddress"/>
 </set>
</class>
inverse被设为true,这意味着TUser不再作为主控方,而是将关联关系的维护工作交给关联对象TAddress来完成.
////TAddress
<class name="com.redsaga.hibernate.db.entity.TAddress" table="t_address" dynamic-update="false" dynamic-insert="false">
...
<many-to_one name="user" class="com.redsaga.hibernate.db.entity.TUser" cascade="none" outer-join="auto" update="true" insert="true" access="property" column="user_id" not-null="true"/>
###################################
<set name="roles" table="t_group_role" lazy="false" inverse="false" cascade="save-update">
 <key column="group_id"/>
 <many-to-many class="com.redsaga.hibernate.db.entity.TRole" column="role_id"/>
</set>
多对多----一般情况下,cascade应该设置为"save-update",对于多对多逻辑,很少出现删除一方需要级联删除所有关系.数据的情况.
<set name="groups" table="t_group_role" lazy="false" inverse="true" cascade="save-update" sort="unsorted">
 <key column="role_id"/>
 <many-to-many class="com.redsaga.hibernate.db.entity.TGroup" column="group_id" outer-join="auto"/>
</set>
##############################################数据检索Criteria Query
Expression.sql提供了原生SQL语法的支持/ Expression.sql("lower({alias}.name) like lower(?)","Erica%",Hibernate.STRING);
在Hibernate 3中,引入了Restrictions类作为Expression的替代
////示例查询
criteria.add(Example.create(exampleUser));
Hibernate会过滤掉示例对象的Null值属性,可以通过调用Example.excludeNode/excludeZeroes方法来进行调整,或者通过Example.excludeProperty方法将某个属性排除在外
Criteria criteria=sesion.createCriteria(TUser.class);
Criteria addrCriteria=criteria.createCriteria("addresses");
addrCriteria.add(Expression.like("address","%Shanghai%"));
#####################################DetachedCriteria
Criteria生命周期位于其宿主Session生命周期之内,也就是说,由某个Session创建的Criteria实例,一旦Session销毁,那么此Criteria实例也随之失效.
DetachedCriteria可以脱离Session实例独立存在.这样我们就可以将某些通用的Criteria查询条件进行抽离,每次使用时再与当前Session实例绑定
DetachedCriteria deCriteria=DetachedCriteria.forClass(TUser.class);
Criteria criteria=deCriteria.getExecutableCriteria(session);

DetachedCriteria avgAge=DetachedCriteria.forClass(TUser.class);
avgAge.setProjection(Projections.avg("age"));
Criteria criteria=session.createCriteria(TUser.class);
criteria.add(Subqueries.propertyGt("age",avgAge);

criteria.setFirstResult(100);
criteria.setMaxResults(20);

criteria.addOrder(Order.asc("name"));    criteria.addOrder(Order.desc("groupId"));
criteria.setProjection(Projections.groupProperty("age"));
Projections.avg()、rowCount()、count()、max()、min()、countDistinct()
############################################HQL
String hql="from TUser";
Query query=session.createQuery(hql);//返回TUser以及TUser子类的记录
List userList=query.list();
我们也可以在where子句中使用算术表达式:
from TUser user where (user.age % 2 =1)

List list=session.createQuery("select user.name,user.age from TUser user").list();//返回数组
如果觉得返回数组的方式不够符合面向对象的风格,我们也可以通过在HQL中动态构造对象实例的方法对这些平面化的数据进行封装
List list=session.createQuery("select new TUser(user.name,user.age) from TUser as user").list();

delete/update子句极有可能导致缓存同步上的障碍/
session.find("from TUser user where user.name=?","Erica",Hibernate.STRING);

Object[] args=new Object[]{"Erica",new Integer(20)};
Type[] types=new Type[]{Hibernate.STRING,Hibernate.INTEGER};
session.find("from TUser user where user.name=? and user.age>?",args,types);

Query query=session.createQuery("from TUser user where user.name=? and user.age>?");
query.setString(0,"Erica");
query.setString(1,20);

String hql="from TUser where name=:name";
Query query=session.createQuery(hql);
query.setParameter("name","Erica");

String hql="from TUser where name:=name and age=:age";
Query query=session.createQuery(hql);
UserQuery uq=new UserQuery();
uq.setName("Erica");
uq.setAge("new Integer(20));
query.setProperties(uq);

#########################SQL配置化
<query name="queryByName">
 <![CDATA[from TUser user where user.name=:name]]>
</query>
Query query=session.getNamedQuery("queryByName");
UserQuery uq=new UserQuery();
uq.setName("Erica");
uq.setAge(new Integer(21));
query.setProperties(uq);
########################################联合查询
"fetch"关键字表明TAddress对象读出后立即填充到对应的TUser对象(addresses集合属性中).如果忽略fetch关键字,我们得到的结果集中,每个条目都是一个
Object数组,其中包括了一个TUser对象以及其对应的TAddress对象。
from TUser user left join fetch user.addresses

String hql="from TUser user right join user.addresses";
List list=session.createQuery(hql).list();
Iterator it=list.iterator();
while(it.hasNext()){
 Object[] results=(Object[])it.next();
 TUser user;
 TAddress addr;
 if((results[0])!=null){
  user=(TUser)results[0];
  System.out.println(user.getName());
 }else{
  System.out.println("No corresponding user");
 }
 addr=(TAddress)results[1];
 System.out.println(addr.getAddress());
}
##########################################子查询
from TUser user where (select count(*) from user.addresses)>1
子查询必须出现在where子句中,且必须以一对圆括号包围。
#########################################数据加载方式
1、即时加载 
2、延迟加载:lazy=true
3、预先加载:与即时加载类似,不过实体及其关联数据是通过一条SQL语句同时读取。通过outer-join完成关联数据的加载
4、批量加载:通过批量提交多个限定条件,一次完成多个数据读取(batch-size=5参数打开批量加载机制)
############################################SQL查询
String sql="select {usr.*} from T_User usr";
List list=session.createSQLQuery(sql,"user",TUser.class).list();
while(it.hasNext()){
 TUser user=(TUser)it.next();
}

select u.id as {usr.id} 其中u是SQL中T_User表的别名,而usr是我们指定的实体对象别名。

String sql="select {usr.*},{addr.*} from T_User usr inner join T_Address addr on usr.id=addr.user_id";
List list=session.createSQLQuery(sql,new String[]{"usr","addr"},new Class[]{TUser.class,TAddress.class}).list();
Iterator it=list.iterator();
while(it.hasNext()){
 Object[] result=(Object[])it.next();
 TUser user=(TUser)result[0];
 TAddress addr=(TAddress)result[1];
}
<sql-query name="queryUser">
 <![CDATA[select {usr.*} from T_User usr where name=:name]]>
 <result alias="usr" class="com.redsaga.hibernate.db.entity.TUser"/>
</sql-query>

<sql-insert/>  <sql-update/>  <sql-delete/>
属性复制可以通过Apache Jakarta Commons Beanutils组件提供的属性批量复制功能
BeanUtils.copyProperties(anotherUser,user);
对于Hibernate而言,这个规则也成立。net.sf.hibernate.engine.Key类封装了Hibernate用于区分两个实体对象的识别信息。
Collection在判断两个对象是否相等的时候,会首先调用对象的hashCode方法,如果hashCode相同的话,随即调用其equals方法,如果两次判断均为真,则认为对比的两个对象相等。
public void flush()throws HibernateException{
 ...
 flushEverything();//刷新所有数据
 execute();//执行数据库SQL完成持久化动作
 ...
}
flushEverything会首先完成一些预处理工作(如调用对应的interceptor,协同级联关系等);之后,即调用flushEntities方法对当前Session中的实体对象进行刷新,而这个过程也是脏数据判定的关键。
static final class EntityEntry implements Seriaizable{
 LockMode lockMode;//当前加销模式
 Status status;//当前状态
 Serializable id;
 Object[] loadedState;//实体最近一次与数据库的同步版本
 Object[] deletedState;//实体最近一次删除时的版本
 boolean existsInDatabase;
 Object version;//版本号[用于乐观锁]
 //针对实体类的持久化封装,通过它我们可以获得实体类属性对应的数据库字段类型等信息,或者执行持久化操作
 transient ClassPersister persister;
 boolean isBeingReplicated;
}

在Session中,保存了所有与当前Session实例相关联的实体对象的当前实例和原始状态信息,这两者以"key-value"的形式,保存在SessionImpl.entityEntries数据结构中。
Session.flushEntities方法,就是遍历entityEntries,并将其中的实体对象与其原始版本进行比对,判断对象状态是否更改。
unsaved-value,数据保存时,Hibernate将根据这个值来判断对象是否需要保存。
##################################数据缓存策略
1、事务级缓存
2、应用级/进程级缓存
3、分布式缓存

事务级缓存:
对于Hibernate而言,事务级缓存是基于Session生命周期的实现的,每个Session会在内部维持一个数据缓存,此缓存随着Session的创建(销毁)而存在(消亡),
应用级缓存:
此缓存可由多个事务共享.事务之间的缓存共享策略与应用的事务隔离机制密切相关.在Hibernate中,应用级缓存在SessionFactory层实现,所有由此SessionFactory创建的Session实例共享此缓存.
分布式缓存:
分布式缓存由多个应用级缓存实例组成集群,通过某种远程机制实现各个缓存实例间的数据同步,任何一个实例的数据修改操作,将导致整个集群间的数据状态同步

内部缓存(一级缓存:session)正常情况下由Hibernate自动维护,如果需要手动干预,我们可以通过以下方法完成:
1、Session.evict  将某个特定对象从内部缓存中清除
2、Session.clear  清空内部缓存
二级缓存:(SessionFactory)
在引入二级缓存时,我们首先必须考虑以下问题:
1、数据库是否与其他应用共享
2、应用是否需要部署在集群环境中
对于第一种情况,就意味着我们不得不放弃二级缓存的使用
如果数据满足以下条件,则可将其纳入缓存管理:
1、数据不会被第三方应用修改
2、数据大小在可接受的范围之内
3、数据更新频率较低
4、同一数据可能会被系统频繁引用
5、非关键数据
第三方缓存实现的接口:JCS、EHCache、OSCache、JBossCache、SwarmCache
Hibernate中启用二级缓存,需要在hibernate.cfg.xml中配置以下参数:
<property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.Provider</property>
另外还需要针对Cache实现本身进行配置:
<ehcache>
 <diskStore path="java.io.tmpdir"/>
 <defaultCache
  maxElementsInMemory="10000"//Cache中最大允许保存的数据对象数量
  eternal="false" //Cache中数据是否为常量
  timeToIdleSeconds="120" //缓存数据钝化时间
  timeToLiveSeconds="120" //缓存数据的生存时间
  overflowToDisk="true" //内存不足时,是否启用磁盘缓存
 />
</ehcache>
之后,需要在我们的映射文件中指定各个映射实体的缓存同步策略
<class name="org.hibernate.sample.TUser"...>
 <cache usage="read-write"/>
 ...
 <set name="addresses"..>
  <cache usage="read-only"/>
  ...
 </set>
</class>
缓存同步策略可应用于实体类和集合属性。缓存同步策略决定了数据对象在缓存中的存取规则。
为了使得缓存调试遵循正确的应用级事务隔离机制,我们必须为每个实体类指定相应的缓存同步策略。
read-only:
只读。对于不会发生改变的数据,只使用只读型缓存。
nonstrict-read-write:
如果程序对并发访问下的数据同步要求不是非常严格,且数据更新操作频率较低(几个小时或更长时间)
read-write:
严格可读写缓存,基于时间戳判定机制,实现了“read committed”事务隔离等级。可用于数据同步要求严格的情况,但不支持分布式缓存。这也是实际应用中使用最多的同步策略。
transactiona:
事务型缓存,必须运行在JTA事务环境中。
在事务型缓存中,缓存的相关操作也被添加到事务之中,如果由于某种原因导致事务失败,我们可以连同缓冲池中的数据一同回滚到事务开始之前的状态。
事务型缓存实现了“Repeatable read”事务隔离等级,有效保障了数据的合法性,适用于对关键数据的缓存。
###############################################事务管理
事务隔离指的是,数据库通过某种机制,在并行的多个事务之间进行分隔,使每个事务在其执行过程中保持独立。
Hibernate本身并不具备事务管理能力。在事务管理层,Hibernate将其委托给底层的JDBC或者JTA,以实现事务的管理和调度。
<session-factory>
...JTA作为事务管理
 <property name="hibernate.transaction.factory_class">
  net.sf.hibernate.transaction.JTATransactionFactory
 </property>
 ...
</session-factory>
#######基于JDBC的事务管理
session=sessionFactory.openSession();//AutoCommit=false;
Transaction tx=session.beginTransaction();
...
tx.commit();

JTA提供了跨Session的事务管理能力。
JDBC事务管理实际上是在JDBC Connection中实现。事务周期限于Connection的生命周期这类。
JTA事务管理则是由JTA容器实现,JTA容器对当前加入事务的众多Connection进行调度,实现其事务性要求。JTA的事务周期可横跨多个JDBC Connection生命周期。同样,对于基于JTA事务的Hibernate而言,JTA事务横跨多个Session
Session.beginTransaction同样也执行了InitialContext.lookup方法获取UserTransaction实例,Transaction.commit方法同样也调用了UserTransaction.commit方法。

在EJB中使用JTA Transaction,将save方法配置为JTA事务支持即可。
/**
  *@ejb.interface-method view-type="remote"
  *@ejb.transaction type="Required"
  */
public void save(){
 classA.save(user);
}
###############################悲观锁/乐观锁
悲观锁:
指数据被外界修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。往往依靠数据库的锁机制
select * from account where name="Erica" for update  //锁定指定数据
Hibernate实现悲观锁,也是基于数据库的锁机制实现:
String hqlStr="from TUser as user where user.name='Erica'";
Query query=session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE);//加锁
List userList=query.list();
Hibernate的加锁模式有:
1、LockMode.NONE:无锁机制
2、LockMode.WRITE:Hibernate在Insert和Update记录的时候会自动获取
3、LockMode.READ:Hibernate在读取记录的时候会自动获取     
///////////上面三种都是Hibernate内部对数据的锁定机制,与数据库无关
4、LockMode.UPGRADE:利用数据库的for update子句加锁
5、LockMode.UPGRADE_NOWAIT:Oracle的特定实现,利用Oracle 的for update nowait子句实现加锁
Criteria.setLockMode     Query.setLockMode        Session.lock  //在查询之前开始加锁,否则无效
########乐观锁
乐观锁大多是基于数据版本记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库增加一个"version"字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。
乐观锁机制往往基于系统中的数据存储逻辑,因些也具备一定的局限性,如,来自外部系统的用户余额更新操作不受我们系统的控制,因此可能会造成非法数据更新到数据库中。(将乐观锁在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据库表直接对外公开)
Hibernate中可以通过class描述符的optimistic-lock属性结合version描述符指定。
<class name="org.hibrenate.sample.TUser"
 table="t_user"
 dynamic-update="true"
 dynamic-insert="true"
 optimistic-lock="version"
 >
 <id.../>
 <version column="version" name="version" type="java.lang.Integer"/>//必须出现在ID之后
</class>
optimistic-lock属性有如下取值:
none:无乐观锁
version:通过版本机制实现乐观锁;脱离Session发生修改的情况下依然有效
dirty:通过检查发生变动过的属性实现乐观锁
all:通过检查所有属性实现乐观锁
######################################持久层操作
数据加载:
Session.load/get方法均可以根据指定的实体类和ID从数据库读取记录,并返回与之对应的实体对象.
其区别在于:
1、如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException
2、Load方法可返回实体的代理类实例,而get方法永远直接返回实体类,
3、load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如果没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。
session.createQuery().list();
session.createQuery().iterate();
find方法通过一条select SQL实现了查询操作,而iterator方法,则执行了3次select SQL,第一次获取了所符合条件记录的ID,之后再根据各个ID从库表中读取对应的记录。
那为何还要用iterator方法,是在缓存的情况下,当find与iterator联用查询条件一样的时候,iterator也只执行一次。
find数据查询时,即使缓存中已经有一些符合条件的实体对象存在,我们也无法保证这些数据就是库表中所有符合条件的数据。
session.evict(user);//将对象从一级缓存中移除
sessionFactory.evict(TUser.class,user.getId());//二级缓存
Query Cache中保存了之前查询操作执行过的Select SQL,以及由此查询产生的查询结果集
为了启用Query Cache,我们必须在Hibernate配置文件中打开hibernate.cache.use_query_cache选项:
<property name="hibernate.cache.use_query_cache">true</property>
之后我们必须将Query的查询执行之前,将Query.Cacheable设为true
Query query=session.createQuery(hql).setInteger(0,20);
query.setCacheable(true)
##################################延时加载
如果我们采用了延迟加载机制,但希望在一些情况下,实现非延迟加载时的功能.Hibernte,initialize方法可以强制Hibernate立即加载关联对象集:
Hibernate.initialize(user.getAddresses());
session.close();
Set hset=user.getAddresses();
TAddress addr=(TAddress)hset.toArray()[0];
<set name="addresses" table="t_address" lazy="true" inverse="true" cascade="all" sort="unsorted">
 <cache usage="read-only"/>//使得Hibernate对数据索引进行缓存.而并不包括这个集合中的各个实体元素
 <key column="user_id"/>
 <one-to-many class="com.redsaga.hibernate.db.entity.TAddress"/>
</set>

<cache usage="read-write"/>//Hibernate才会对集合中的实现也进行缓存

Hibernate3属性延迟加载机制在配置之外,还需要借助类增强器对二进制Class文件进行强化处理
Session.save()方法中包含了以下几个主要步骤:
1、在Session内部缓存中寻找待保存对象,内部缓存命中,则认为此数据已经保存,实体对象已尼处于Persistent状态,直接返回。
2、如果实体类实现了lifecycle接口,则调用待保存对象的onSave()方法
3、如果实体类实现了Validatable接口,则调用其validate()方法。
4、调用对应拦截器的interceptor.onSave方法
5、构造insert SQL,并加以执行
6、记录插入成功,user.id属性被设定为insert操作返回的新记录ID值
7、将USER对象放入内部缓存(不纳入二级缓存,以防同步问题的代价)
8、最后,如果存在级联关系,对级联关系进行递归处理
可以通过设置Hibernate.jdbc.batch_size参数来指定Hibernate每次提交SQL的数据
批量删除(游标):
Transaction tx=session.beginTransaction();
String hql="from TUser";
Query query=session.createQuery(hql);
ScrollableResults scRes=query.scroll();
while(scRes.next()){}
#######################################################Collection:
////////Set
Hibernate Set类型的实现位于net.sf.hibernate.collection.Set。通过对java.util.HashSet的封装,它实现了java.util.Set接口并进行了扩充。
<set name="addresses" lazy="true" table="t_address">
 <key column="user_id"/>
 <element type="string" column="address"/>
</set>
直接将t_address表的address字段与TUser相关联。TUser对象的addresses集合来自t_address表对应记录的address字段填充。
////////////Bag
它是Hibernate自定义的集合类型,实现了一个允许包含重复元素的“Set”。借助了List实现,但却屏蔽了List的有序特性
<bag name="addresses" lazy="true" table="t_address">
 <key column="user_id"/>
 <element type="string" column="address"/>
</bag>
idbag配置比Bag多出一项“collection-id”,用于配置id字段,根据些ID字段,hibernate就可以准确定位库表记录,从而实现高效的数据操作。
<idbag name="addresses" lazy="true" table="t_address">
 <collection-id type="int" column="id">
  <generator class="identity"/>
 </collection-id>
 <key column="user_id"/>
 <element type="string" column="address"/>
</idbag>
/////////////////Map
<map name="addresses" lazy="true" table="t_address">
 <key column="user_id"/>
 <index type="string" column="type"/>
 <element type="string" column="address"/>
</map>
添加了一个index配置,用于指定用作key的字段名;此字段要求在数据集中取值惟一。
TUser user=(TUser) session.load(TUser.class,new Integer(1));
user.getAddresses().get("Home"));
/////////////////List
List实现了集合内元素顺序的持久化,
<list name="addresses" lazy="true" tabl="t_address">
 <key column="user_id"/>
 <index type="integer" column="idx"/>
 <element type="string" column="address"/>
</list>
index节点中,我们指定以idx字段保存次序状态
######################################集合排序
Sort操作是在JVM中完成的,而order-by是由数据库完成的
<set name="addresses" lazy="true" table="t_address" sort="natural">
 <key column="user_id"/>
 <element type="string" column="address"/>
</set>
可排序Set在Hibernate中对应的实现类为net.sf.hibernate.collection.SortedSet,它实现了java.util.SortedSet接口
sort="natural"指定采用Java默认排序机制,它会调用相应数据类型的compareTo方法进行排序中的值比对。
sort值可以为实现了Comparator接口的类。
Bag和List由于实现原理的不同,并不支持sort排序方式。Set,Map,Bag均支持order-by排序,有序集List例外。
####################################################lifecyle与Validatable
Hibernate通过lifecycle、Validatable接口制定了实体对象(CRUD)过程中的回调(CallBack)方式。实体类通过实现Lifecycle接口,即可在特定的持久化阶段,触发特定的处理过程。
public class TUser implements Serializable,Lifecycle{
 public boolean onSave(Session s)throws CallbackException{
  return false;//insert操作正常执行
 }
 public boolean onUpdate(Session s)throws CallbackException{
  ...
  if(...) return true;//update操作将被中止
 }
 public boolean onDelete(Session s)throws CallbackException{
  ...
  return false;//delete操作正常执行
 }
 public void onLoad(Session s,Serializable id){
  ...
 }
}
onSave、onUpdate、onDelete方法,如果返回true则意味着需要中止执行对应的操作过程。如果代码运行期间抛出了CallbackException ,对应的操作也会被中止

Validatable.validate方法将在实体持久化之前得到调用以对数据进行验证
#####################################################Interceptor
Interceptor接口定义了Hibernate中的通用拦截机制。Session创建时即可以指定加载相应的Interceptor,之后,此Session的持久化操作动作都将首先经由此拦截器捕获处理。
通过编码加载Interceptor:
SessionFactory sessionFactory=config.buildSessionFactory();
Interceptor it=new MyInterceptor();
session=sessionFactory.openSession(it);
例:
public class MyInterceptor implements Interceptor{
 public boolean onSave(Object entity,Serializable id,Object[] state,
  String[] propertyNames,Type[] types)throws CallbackException{
  if(entity instanceof TUser){
   System.out.println("User to be saved=>"+((TUser)entity).getName());
  } 
  return false;
 }
}
//////////////////Interceptor典型应用
数据稽核:即针对关键信息及其变更历史进行审查,作为业务跟踪的基础依据。
SessionFactory sessionFactory=config.buildSessionFactory();
MyInterceptor it=new MyInterceptor();
session=sessionFactory.openSession(it);
it.setSession(session);
it.setUserId("CurrentUser");
TUser user=new TUser();
user.setName("Erica");
Transaction tx=session.beginTransaction();
session.save(user);
tx.commit();
session.close();
session.connection()利用了当前Session的JDBC Connection引用

public static void doLog(String action,String userId,Set moditySet,Connection connection){
//依托当前Session的JDBC Connection,我们创建了一个临时Session实例用于保存操作记录。
 Session tempSession=HibernateUtil.getSessionFactory().openSession(connection);
 try{
  Iterator it=modifySet.iterator();
  while(it.hasNext()){
   TUser user=(TUser)it.next();
   TAuditLog auditLog=new TAuditLog();
   ...
   tempSession.save(auditLog);
  }
  tempSession.flush();
 }catch(Exception ex){
  throw new CallbackExcption(ex);
 }finally{
  try{
   tempSession.close();
  }catch(HibernateException ex){
   throw new CallbackException(ex);
  }
 }
}
Hibernate 3中,提供了基于时间监听Listener机制的拦截器实现,这种方式可以提供比Interceptor更加清晰合理的实现模型。
##########################################Hibernate实用技术
////////Hibernate分页
通过Criteria.setFirstResult和Criteria.setFetchSize方法设定分页范围。如:
Criteria criteria=sesion.createCriteria(TUser.class);
criteria.add(Expression.eq("age","20");
criteria.setFirstResult(100);
criteria.setFetchSize(20);
###########################################Session管理
Sesion中包含了数据库操作相关的状态信息,如对JDBC Connection的维护,数据实体的状态维持等。
Session管理的目标聚集于通过合理的设计,避免Session的频繁创建和销毁,从而避免大量的内存开销和频繁的JVM垃圾回收,保证系统高效平稳运行。
ThreadLocal是Java中一种较为特殊的线程绑定机制。通过TreadLocal存取的数据,总是与当前线程相关。也就是说,JVM为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程常出现的并发访问问题提供了一种隔离机制。
SessionFactory是线程安全的,多个并发线程可以同时访问一个SessionFactory并从中获取Session实例。Session并非线程安全的,也就是说,如果多个线程同时使用一个Session实例进行数据存取,则将会导致Session数据存取逻辑混乱,
public class TestServlet extends Httpservlet{
 private Session session;
 public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
  session=getSession();
  doSomething();
  session.flush();
 }
 public void doSomething(){
  .....//基于session的存取操作
 }
}
Servlet运行是多线程的,应用服务器并不会为每个线程都创建一个Servlet实例,也就是说应用服务器中只有一个Servlet实例。
public class TestServlet extends HttpServlet{
 private ThreadLocal localSession=new ThreadLocal();
 public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
  localSession.get(getSession());
  doSomething();
  session.flush();
 }
 public void doSomething(){
  Session sesion=(Session)localSession.get();
  ...//基于session的存取操作
 }
}
//通过Set方法将获取的session 实例保存,而在doSomething方法中,通过get方法取出session实例。
ThreadLocal会为每个线程维护一个私有的变量空间。实际上,其实现原理是在JVM中维护一个Map,这个Map的Key就是当前的线程对象,而value则是线程通过ThreadLocal.set方法保存的对象实例。
通过应用ThreadLocal机制,线程A的Session实例只能为线程A所用,同样,其他线程的session实例也各自从属于自己的线程,这样,我们就实现了线程安全的session共享机制。
Filter的生命周期覆盖了Servlet及其底层对象。Filter在Servlet被调用之前执行,在Servlet调用结束之后结束。因此,在Filter中管理Session对于Web程序而言就显得水到渠成。
public class PersistenceFilter implements Filter{
 protected static ThreadLocal hibernateHolder=new ThreadLocal();
 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)throws IOException,ServletException{
  hibernateHolder.set(getSession());
  try{
   chain.doFilter(request,response);
  }finally{
   Session sess=(Session)hibernateHolder.get();
   if(sess!=null){
    hibernateHolder.set(null);
   }
   try{
    sess.close();
   }catch(HibernateException ex){
    throws new ServletException (ex);
   }
  }
 }
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics