posts - 66,  comments - 11,  trackbacks - 0
package com.hibernate.higherApplication;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import junit.framework.TestCase;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Expression;

public class DurationOperator extends TestCase {
    
private SessionFactory sessionFactory = null;
    
private Session session = null;
    
/**
     * 初始化资源
     
*/
    
protected void setUp() throws Exception {
        
try {
            
//加载类路径下的hibernate.cfg.xml文件
            Configuration config = new Configuration().configure();
            
//创建sessionFactory对象
            sessionFactory = config.buildSessionFactory();
            
//创建session
            session = sessionFactory.openSession();
        } 
catch (HibernateException e) {
            e.printStackTrace();
        }
    }
    
/**
     * load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。
     * 区别在于:
     * 1、如果未发现符合条件的记录,get方法返回null,而load方法抛出一个ObjectNotFoundException
     * 2、load方法可以返回实体的代理类实例,而get方法永远直接返回实体类。
     * 3、load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如果
     * 没有发现数据,将越过二级缓存,直接调用SQL完成数据读取。
     *
     
*/
    
public void loadOrGetData(){
        TUser user 
= (TUser)session.load(TUser.class,new Integer(1));
    }
    
/**
     * 查询性能往往是一系统性能表现的一个重要方面。
     * query.list方法通过一条select SQL实现了查询操作,而iterate方法,则执行了3次selectSQL,第一次获取了所有符合条件的记录
     * 的id,之后,在根据各个id从库表中读取对应的哦记录,这是一个典型的N+1次查询问题。
     * 
     * 我们进行query.list数据查询时,即使缓存中已经有一些符合条件的实体对象存在,我们也无法保证这些数据就是库表中所有符合条件的数据。假设
     * 第一次查询条件是age>25,随即缓存中就包括了所有age>25的user数据;第二次查询条件为age>20,此时缓存中虽然包含了满足age>25d的
     * 数据,但这些并不是满足条件age>20的全部数据
     * 因此,query.list方法还是需要执行一次select sql以保证查询结果的完整性(iterate方法通过首先查询获取所有符合条件记录的id,以此保证
     * 查询结果的完整性)。
     * 因此,query.list方法实际上无法利用缓存,它对缓存只写不读。而iterate方法则可以充分发挥缓存带来的优势,如果目标数据只读或者读取相对
     * 较为频繁,通过这种机制可以大大减少性能上的损耗。
     
*/
    
public void queryForList(){
        String hql 
= "from TUser where age>?";
        Query query 
= session.createQuery(hql);
        query.setInteger(
1,1);
        
        List list 
= query.list();
        
        
for(int i=0;i<list.size();i++){
            TUser user 
= (TUser)list.get(i);
            System.out.println(
"User age:"+user.getAge());
        }
    }
    
public void queryForIterate(){
        String hql 
= "from TUser where age>?";
        Query query 
= session.createQuery(hql);
        query.setInteger(
1,1);
        
        Iterator it 
= query.iterate();
        
        
while(it.hasNext()){
            TUser user 
= (TUser)it.next();
            System.out.println(
"User age:"+user.getAge());
        }
    }
    
/**
     * 大数据量的批量读取(10W条)
     * 解决方案:结合iterate方法和evict方法逐条对记录进行处理,将内存消耗保持在可以接受的范围之内。
     * 在实际开发中,对于大批量数据处理,还是推荐采用SQL或存储过程实现,以获得较高的性能,并保证系统平滑运行。
     
*/
    
public void bigDataRead(){
        String hql 
= "from TUser where age>?";
        Query query 
= session.createQuery(hql);
        query.setInteger(
"age"1);
        Iterator it 
= query.iterate();
        
        
while(it.hasNext()){
            TUser user 
= (TUser)it.next();
            
//将对象从一级缓存中移除
            session.evict(user);
            
//二级缓存可以设定最大数据缓存数量,达到峰值时会自动对缓存中的较老数据进行废除,但是我们这里还是通过
            
//编码指定将对象从二级缓存中移除,这有助保持缓存的数据有效性。
            sessionFactory.evict(TUser.class,user.getId());
        }
    }
    
/**
     * Query Cache弥补了find方法的不足,QueryCache中缓存的SQL及其结果及并非永远存在,当Hibernate发现此SQL对应的库表发生变动,
     * 会自动将Query Cache中对应表的SQL缓存废除。因此Query Cache只在特定的情况下产生作用:
     * 1、完全相同的select SQL重复执行。
     * 2、在2次查询之间,此select SQL对应的库表没有发生过改变。
     
*/
    
public void queryForQueryCache(){
        String hql 
= "from TUser where age>?";
        Query query 
= session.createQuery(hql);
        query.setInteger(
11);
        
//除了在这里设置QueryCache外,还要在hibernate.cfg.xml中进行设置
        
//<property name="hibernate.cache.use_query_cache">true</property>
        query.setCacheable(true);
        List userList 
= query.list();
    }
    
/**
     * 所谓延迟加载,就是在需要数据的时候,才真正执行数据加载操作。
     * 延迟加载实现主要针对:
     * 1、实体对象:通过class的lazy属性,我们可以打开实体对象的延迟加载功能。
     * 2、集合
     
*/
    
public void queryForEntityLazy(){
        Criteria criteria 
= session.createCriteria(TUser.class);
        criteria.add(Expression.eq(
"name","Erica"));
        
        List userList 
= criteria.list();
        TUser user 
= (TUser)userList.get(0);
        
//虽然使用了延迟加载,但是我们可以通过hibernate的初始化方法进行强制加载,这样即使session关闭之后,关联的对象仍让可以使用
        Hibernate.initialize(user.getAddresses());
        
        System.out.println(
"User name=>"+user.getAge());
        
        Set hset 
=user.getAddresses();
        TAddresses addr 
= (TAddresses)hset.toArray()[0];
        System.out.println(addr.getAddress());
        
        session.close();
    }
    
/**
     * 关闭资源
     
*/
    
protected void tearDown() throws Exception {
        
try{
            session.close();
        }
catch(HibernateException e){
            e.printStackTrace();
        }
    }

}
posted @ 2010-01-02 15:27 王永庆 阅读(354) | 评论 (0)编辑 收藏
  基于Java的缓存实现,最简单的方式莫过于对集合类数据类型进行封装。Hibernate提供了基于Hashtable的缓存实现机制,不过,由于其性能和功能上的局限,仅供开发调试中使用。同时,Hibernate还提供了面向第三方缓存实现的接口,如:
HashTable--------------------------------net.sf.hibernate.cache.HashtableCacheProvider
1、JSC
2、EHCache->默认的二级Cache实现。--------net.sf.encache.hibernate.Provider
3、OSCache-------------------------------net.sf.hibernate.cache.OSCacheProvider
4、JBoss Cache->分布式缓存---------------net.sf.hibernate.cache.TreeCacheProvider
5、SwarmCache----------------------------net.sf.hibernate.cache.SwarmCacheProvider
相对于JSC而言,EHCache更加稳定,并具备更好的混存调度性能,其缺陷是目前还无法做到分布式缓存。
首先设置hibernate.cfg.xml然后设置ehcache.xml最后设置缓存策略。

  缓存同步策略决定了数据对象在缓存中的存取规则。为了使得缓存调度遵循正确的应用级事物隔离机制,我们必须为每个实体类指定相应的缓存同步策略。Hibernate提供4种内置的缓存同步策略:
1、read-only:只读。对于不会发生改变的数据,可使用只读型缓存。
2、nonstrict-read-write:如果程序对并发访问下的数据同步要求不是非常严格,且数据更新操作频率较低,可以采用本选项。
3、read-write:严格可读写缓存。
4、transactional:事务型缓存,必须运行在JTA事物环境中。

  JDBC事物由Connection管理,也就是说,事务管理实际上是在JDBC Connection中实现。事务周期限于Connection的生命周期之类。同样,对于基于JDBC Transaction的Hibernate事务管理机制而言,事物管理在Session所以托的JDBCConnection中实现,事务周期限于Session的生命周期。
  JTA事物管理则由JTA容器实现,JTA容器对当前加入事物的众多Connection进行调度,实现其事务性要求。JTA的事物周期可横跨多个JDBC Connectin生命周期。同样对于基于JTA事务的Hibernate而言,JTA事物横跨多个Session.

  Hibernate支持2种锁机制:即通常所说的悲观锁和乐观锁。
  悲观锁的实现,往往依靠数据库提供的锁机制。典型的悲观锁调用:
  select * from account where name=="Erica" for update
package com.hibernate.higherApplication;

import java.util.List;

import junit.framework.TestCase;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Expression;

public class LockOperator extends TestCase {
    
private Session session = null;
    
/**
     * 初始化资源
     
*/
    
protected void setUp() throws Exception {
        
try {
            
//加载类路径下的hibernate.cfg.xml文件
            Configuration config = new Configuration().configure();
            
//创建sessionFactory对象
            SessionFactory sessionFactory = config.buildSessionFactory();
            
//创建session
            session = sessionFactory.openSession();
        } 
catch (HibernateException e) {
            e.printStackTrace();
        }        
    }
    
/**
     * 悲观锁
     * Hibernate的加锁模式有:
     * 1、LockMode.NONE:无锁机制
     * 2、LockMode.WRITE:Hibernate在Insert和Update记录的时候会自动获取
     * 3、LockMode.READ:Hibernate在读取记录的时候会自动获取
     * 上述3种锁机制为了保证update过程中对象不会被外界修改,在目标对象上加锁,与数据库无关
     * 4、LockMode.UPGRADE:利用数据库的for update子句加锁
     * 5、LockMode.UPGRADE_NOWAIT:oracle的特定实现
     * 注意:只有在查询开始之前设定加锁,才会真正通过数据库的锁机制进行加锁处理。
     
*/
    
public void addPessimismLock(){
        String hqlStr 
= "from TUser as user where user.name='Erica'";
        Query query 
= session.createQuery(hqlStr);
        query.setLockMode(
"user",LockMode.UPGRADE);//多所有返回的user对象加锁
        List userList = query.list();//执行查询
    }
    
/**
     * 乐观锁
     * 数据版本:即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个version字段来实现。
     * 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1.此时,将提交数据的版本数据与数据库对应记录的当前版本信息
     * 进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
     * 
     * Hibernate在其数据访问引擎中内置了乐观锁实现。如果不考虑外部系统对数据库的更新操作,利用Hibernate提供的透明化乐观锁
     * 实现,将大大提升我们的生产力。见配置文件T_USER.hbm.xml
     * 乐观锁机制避免了长事务中的数据加锁开销,大大提升了大并发量下的系统整体性能表象。
     *
     
*/
    
public void addOptimismLock(){
        Criteria criteria 
= session.createCriteria(TUser.class);
        criteria.add(Expression.eq(
"name","Erica"));
        
        List userList 
= criteria.list();
        TUser user 
= (TUser)userList.get(0);
        
        Transaction tx 
= session.beginTransaction();
        user.setVersion(
1);
        tx.commit();
    }
    
/**
     * 关闭资源
     
*/
    
protected void tearDown() throws Exception {
        
try{
            session.close();
        }
catch(HibernateException e){
            e.printStackTrace();
        }
    }
    
}

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>
<hibernate-mapping>
    
<!-- 
        none:无乐观锁
        version:通过版本机制实现乐观锁
        dirty:通过检查发生变动过的属性实现乐观锁
        all通过检查所有属性实现乐观锁
     
-->
    
<class
        
name="org.hibernate.sample.TUSER"
        table
="t_user"
        dynamic-update
="true"
        dynamic-insert
="true"
        optimistic-lock
="version"
        lazy
="true"
        
>
        
<id
        
name="id"
        column
="id"
        type
="java.lang.Integer"
        
>
            
<generator class="native">
            
</generator>
        
</id>
        
<version name="version" column="version" type="java.lang.Integer">
        
</version>
        
<set name="addresses"
             table
="t_address"
             lazy
="true"
             inverse
="false"
             cascade
="all"
        
>
            
<key
                
column="user_id"
            
>
            
</key>
            
<one-to-many class=""/>
        
</set>
    
</class>
</hibernate-mapping>


posted @ 2010-01-02 15:25 王永庆 阅读(552) | 评论 (0)编辑 收藏
    实体对象,特指Hibernate O/R映射关系中的域对象。实体对象生命周期中的3种状态
    1、Transient(自由状态):所谓Transient,即实体对象在内存中的自由存在,它与数据库中的记录无关。
    2、Persistent(持久状态):即实体对象处于由Hibernate框架所管理的状态。
    3、Detached(游离状态):处于Persistent状态的对象,其对应的Session实例关闭之后,那么,此对象就处于"Detached"状态。
    Transient状态的user对象与库表的数据缺乏对应关系,而Detached状态的user对象,却在库表中存在对应的记录,只不过由于Detached对象脱离了session这个数据操作平台,其状态的变化无法更新到库表中的对应记录。
    处于Transient和Detached状态的对象统称为值对象(VO),而处于Persistent状态的对象称为持久对象(PO).这是站在实体对象是否被纳入Hibernate实体管理容器的立场加以区分的,非管理的实体对象统称为VO,而被管理的实体对象称为PO.
VO与PO的主要区别在于:
1、VO是相对独立的实体对象,处于非管理状态。
2、PO是由Hibernate纳入其实体管理容器的对象,它代表了与数据库中某条记录对应的Hibernate实体,PO的变化在事务提交时将反映到实际数据库中
3、如果一个PO与其对应的Session实例分离,那么此时,它又会变成一个VO。

    不覆盖equals/hashCode方法的情况下我们要面对的问题:实体对象的跨session识别。解决办法一个是实现所谓的值比对,即在equals/hashCode方法中,对实体类的所有属性值进行比对.除了值比对,还有另外一种基于业务逻辑的对象判定方式业务关键信息判定。

    tx.commint();方法中会调用session.flush()方法,在flush()方法中会执行2个主要任务
1、flushEverything();//刷新所有数据
2、execute(0);//执行数据库SQL完成持久化动作。

    数据缓存:在特定硬件基础上缓存往往是提升系统性能的关键因素。缓存是数据库数据在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库与数据访问层之间。ORM在进行数据读取时,会根据其缓存管理策略,首先在缓存中查询,如果在缓存中发现所需数据,则直接以此数据作为查询结果加以利用,从而避免了数据库调用的性能开销。
    相对内存操作而言,数据库调用是一个代价高昂的过程,对于典型企业及应用结构,数据库往往与应用服务器位于不同的物理服务器,这也就意味着每次数据库访问都是一次远程调用,Socket的创建与销毁,数据的打包拆包,数据库执行查询命令,网络传输上的延时,这些消耗都给系统整体性能造成了严重影响。
    ORM的数据缓存应包含如下几个层次:
1、事务级缓存:事务级缓存是基于Session生命周期实现的,每个Session会在内部维持一个数据缓存,此缓存随着Session的创建而存在,因此也成为Session Level Cache(内部缓存)
2、应用级/进程级缓存:在某个应用中,或者应用中某个独立数据访问子集中的共享缓存。此缓存可由多个事物共享。在Hibernate中,应用级缓存在SessinFactory层实现,所有由此SessionFactory创建的Session实例共享此缓存。多实例并发运行的环境要特别小心进程级缓存的调用。
3、分布式缓存:分布式缓存由多个应用级缓存实例组成集群,通过某种远程机制实现各个缓存实例间的数据同步,任何一个实例的数据修改操作,将导致整个集群间的数据状态同步。由于多个实例间的数据同步机制,每个缓存实例发生的变动都会复制到其余所有节点中,这样的远程同步开销不可忽视。

    Hibernate数据缓存分为2个层次,1、内部缓存2、二级缓存hibernate中,缓存将在以下情况中发挥作用:
1、通过ID加载数据时
这包括了根据id查询数据的Session.load方法,以及Session.ierate等批量查询方法
2、延迟加载

    Session在进行数据查询操作时,会首先在自身内部的一级缓存中进行查找,如果一级缓存未能命中,则将在二级缓存中查询,如果二级缓存命中,则以此数据作为结果返回。
    如果数据满足以下条件,则可将其纳入缓存管理
1、数据不会被第三方应用修改
2、数据大小在可接受的范围之内
3、数据更新频率较低
4、同一数据可能会被系统频繁引用
5、非关键数据(关键数据,如金融账户数据)
Hibernate本身并未提供二级缓存的产品化实现(只是提供了一个基于Hashtable的简单缓存以供调试),而是为众多的第三方缓存组件提供了接入接口,我们可以根据实际情况选择不同的缓存实现版本。

   
posted @ 2009-12-22 15:01 王永庆 阅读(209) | 评论 (0)编辑 收藏
    比较字符串是否相等使用equals方法
    使用"=="与equals到底有哪些不同?
    equals:可以比较内容,是2个字符串内容的比较。
    ==:数值比较,比较的是内存地址的值是否相等。

    一个字符串就是String类的匿名对象。
    String name1 = new String("wyq");->开辟了2个空间,其中一个是垃圾空间。
    String name2 = "wyq";->开辟了一个空间,所以应该选择它。

    String的另一个特殊之处:String使用了Java中的共享模式,它只要发现在内存中有这块数据,不会在内存中重新生成。
    String类中的内容一旦声明则不可改变。
    StringBuffer与String的本质区别,在于StringBuffer可以改变。

    this可以调用本类中的属性,也可以调用本类中的方法(含构造方法this())。
    注意:构造方法本身必须在首行被使用,为了给类中的属性初始化。
    this调用属性、本类方法、构造方法这三点是this的基本应用,也是最常用的,但是以上三点实际上可以综合成一点---表示当前对象。
    this表示当前对象主要应用在一点:用于进行对象的比较。
public boolean compare(Person p1){
    
boolean flag = false;
    Person p2 
= this;
    
if(p1.name.equals(p2.name)&&p1.age==p2.age)
    
{
       flag 
= true;
    }

    
return flag;
}

posted @ 2009-12-08 09:56 王永庆 阅读(182) | 评论 (0)编辑 收藏

    在软件中,要么全有要么全无的操作成为事务。事务允许你把几个操作组成一个单一的工作单元,这个工作单元要么全部发生要么全部不发生。如果每件事都顺利,那么这个事务是成功的。但是如果任何一件事情出错的话,那么已经发生的行为就被清除掉,就像什么事情都没发生一样。
    Spring对事务管理有丰富的支持,程序控制的和声明式的。
    原子性(Atomic):事务由一个或多个行为绑定在一起组成,好像是一个单独工作单元。原子性确保在十五中的所有操作要么都发生,要么都不发生。
    一致性(Consistent):一旦一个事务结束了(不管成功失败),系统所处的状态和它的业务规则是一致的。就是说数据应当不会被破坏。
    隔离性(Isolated):事务应该允许多个用户操作同一数据,一个用户的操作不会和其他用户的操作相混淆。因此,事务必须是互相隔离的,防止并发读写同一数据的情况发生。
    持久性(Durable):一旦事务完成,事务的结果应该持久化,这样不管什么样的系统崩溃,他们都将幸免于难。
 
    Spring对程序控制事务管理的支持和EJB的有很大不同。EJB的事务管理和JTA密不可分,和EJB不同的是,Spring使用了一种回调机制,把真实的事务实现从事务代码中抽象出来。选择程序控制事务管理还是声明式事务管理,很大程度上是在细粒度控制与简便操作之间做出决定。当你在代码中编写事务时,你能精确控制事务的边界,在你希望的地方精确的开始和结束。典型的情况下,你不需要程序控制事务所提供的细粒度控制,你会选择在上下文定义文件中声明你的事务。

    Spring对声明式事务管理的支持是通过它的AOP框架实现的。这样做是非常自然的,因为事务是系统级的,凌驾于应用的主要功能之上的。

    在Spring里,事务属性是对事务策略如何应用到方法的描述。这个描述包括:传播行为、隔离级别、只读提示、事务超时间隔
    传播行为:
    PROPAGATION_MANDATORY:表示该方法必须运行在一个事务中。如果当前事务不存在,将抛出一个异常。
    PROPAGATION_NESTED:表示如果当前已经存在一个事务,则该方法应当运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交或回滚。如果当前事务不存在,那么它看起来和PROPAGATION_REQUIRED没有两样。
    PROPAGATION_NEVER:表示当前的方法不应该运行在一个事务上下文中。如果当前存在一个事务,则会抛出一个异常。
    PROPAGATION_NOT_SUPPORTED:表示该方法不应在事务中运行。如果一个现有的事务正在运行中,它将在该方法的运行期间被挂起。
    PROPAGATION_REQUIRED:表示当前方法必须运行在一个事务中。如果一个现有的事务正在运行中,该方法将运行在这个事务中。否则的话,要开始一个新的事务。
    PROPAGATION_REQUIRES_NEW:表示当前方法必须运行在它自己的事务里。它将启动一个新的事务。如果有事务运行的话,将在这个方法运行期间被挂起。
    PROPAGATION_SUPPORTS:表示当前方法不需要事务处理环境,但如果有一个事务已经在运行的话,这个方法也可以在这个事务里运行。

   传播规则回答了一个问题:就是新的事务是否要被启动或是被挂起,或者方法是否要在事务环境中运行。

   隔离级别:在一个典型的应用中,多个事务并发运行,经常会操作同一个数据来完成它们的任务。并发,虽然是必须的,但会导致下面问题:
1、脏读:脏读发生在一个事务读取了被另一个事务改写但还未提交的数据时。如果这些改变在稍后被回滚,那么第一个事务读取的数据就是无效的。
2、不可重复读:不可重复读发生在一个事务执行相同的查询2次或2次以上,但每一次查询结果都不同时。这通常是由于另一个并发事务在2次查询之间更新了数据。
3、幻读:幻读和不可重复读相似。当一个事务读取几行纪录后,另一个并发事务插入一些记录,幻读就发生了。隔离级别有如下几个:
ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED:允许你读取还未提交的改变了的数据,可能导致脏读、幻读、不可重复读
ISOLATION_READ_COMMITTED:允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可能发生。
ISOLATION_REPEATABLE_READ:对相同字段的多次读取的结果是一致的,除非数据被事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
ISOLATION_SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻读。这在所有隔离级别中也是最慢的。

    只读:如果一个事务只对后端是据库执行读操作,数据库就可能利用事务只读的特性,使用某些优化措施。通过声明一个事务为只读,你就给了后端数据库一个机会,来应用那些它认为合适的优化措施。因为只读的优化措施是在事务启动时由后端数据库实施的,所以,只有将那些具有可能启动新事务的传播行为的方法的事务标记成只读才有意义(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED) TransactionProxyFactoryBean参照一个方法的事务属性,决定如何在那个方法上执行事务策略。

<?xml version="1.0" encoding="UTF-8"?>
<beans
    
xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        
<property name="jndiName">
            
<value>java:comp/env/jdbc/myDatasource</value>
        
</property>
    
</bean>
    
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        
<property name="dataSource">
            
<ref bean="dataSource"/>
        
</property>
    
</bean>
    
<!-- 这个对象有一个值为courseService的id.当应用从应用上下文里请求一个courseService时,它将得到一个被
    TransactionProxyFactoryBean包裹的实例。 
-->
    
<bean id="courseService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        
<!-- 代理所实现的接口 -->
        
<property name="proxyInterfaces">
            
<list>
                
<value>
                    com.springinaction.training.service.CourseService
                
</value>
            
</list>
        
</property>
        
<!-- 被代理的对象 -->
        
<property name="target">
            
<ref bean="courseServiceTarget"/>
        
</property>
        
<!-- 事务管理器 -->
        
<property name="transactionManager">
            
<ref bean="transactionManager"/>
        
</property>
        
<!-- 事务的属性源 -->
        
<property name="transactionAttributeSource">
            
<ref bean="transactionAttributeSource"/>
        
</property>
    
</bean>
    
<!-- 要知道尽管可以改变MatchAlwaysTransactionAttributeSource的事务属性参数,但它总是返回相同的事务属性,而
    不关心参与交易的哪一个方法。当你有一个相对简单的应用,把同样的事务策略应用到所有方法都没问题时,使用MatchAlwaysT
    ransactionAttributeSource就相当好。但是,在那些更为复杂的应用中,你很可能需要对不同的方法应用不同的事务策略。在那样
    情况下,你需要在应用何种策略的问题上做更多精确的控制。 
-->
    
<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
        
<property name="transactionAttribute">
            
<ref bean="myTransactionAttribute"/>
        
</property>
    
</bean>
    
<!-- 定义事务策略 -->
    
<bean id="myTransactionAttribute" class="org.springframework.transaction.interceptor.DefaultTransactionAttribute">
        
<!-- 传播行为 -->
        
<property name="propagationBehaviorName">
            
<value>PROPAGATION_REQUIRES_NEW</value>
        
</property>
        
<!-- 隔离级别 -->
        
<property name="isolationLevelName">
            
<value>ISOLATION_REPEATABLE_READ</value>
        
</property>
    
</bean>
</beans>

 

除了将transactionAttributeSource对象织入到TransactionProxyFactoryBean的transactionAttributeSource属性中外,还有一种简单的方法。发展到现在,TransactionProxyFactoryBean也有一个transactionAttributes属性为transactionProperties.

<?xml version="1.0" encoding="UTF-8"?>
<beans
    
xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        
<property name="jndiName">
            
<value>java:comp/env/jdbc/myDatasource</value>
        
</property>
    
</bean>
    
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        
<property name="dataSource">
            
<ref bean="dataSource"/>
        
</property>
    
</bean>
    
<!-- 这个对象有一个值为courseService的id.当应用从应用上下文里请求一个courseService时,它将得到一个被
    TransactionProxyFactoryBean包裹的实例。 
-->
    
<bean id="courseService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        
<!-- 代理所实现的接口 -->
        
<property name="proxyInterfaces">
            
<list>
                
<value>
                    com.springinaction.training.service.CourseService
                
</value>
            
</list>
        
</property>
        
<!-- 被代理的对象 -->
        
<property name="target">
            
<ref bean="courseServiceTarget"/>
        
</property>
        
<!-- 事务管理器 -->
        
<property name="transactionManager">
            
<ref bean="transactionManager"/>
        
</property>
        
<!-- 事务的属性源 -->
        
<property name="transactionAttributeSource">
            
<ref bean="transactionAttributeSource"/>
        
</property>
    
</bean>
    
<!-- NameMatchTransactionAttributeSource的properties属性把方法名映射到事务属性描述器上。注意CourseException
    用一个负号标记。异常可以用负号或正号标记,当负号异常抛出时,将触发回滚;相反的,正号异常表示事务仍可提交,即使这个异常抛出 
-->
    
<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
        
<property name="properties">
            
<props>
                
<prop key="enrollStudentInCourse">
                    PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ,readOnly,
                    -CourseException
                
</prop>
                
<!-- 还可以使用通配符 -->
                
<prop key="get*">
                    PROPAGATION_SUPPORTS
                
</prop>
            
</props>
        
</property>
    
</bean>
</beans>

 

 

posted @ 2009-11-22 11:43 王永庆 阅读(252) | 评论 (0)编辑 收藏

    HQL作为Hibernate的查询语言,提供了ANSI SQL面向对象的封装形式。
    与Criteria和HQL互为补充,Hibernate也提供了对原生SQL以及存储过程的支持,相对于JDBC的SQL操作,Hibernate提供了更为妥善的封装。代码如下:

package com.testproject.hibernate;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;

public class HibernateSqlQuery {
    Session session 
= null;
    
public void querySql(){
        String sql 
= "select {usr.*} from T_User usr";
        List list 
= session.createSQLQuery(sql).addEntity("usr", TUser.class).list();
        Iterator it 
= list.iterator();
        
while(it.hasNext()){
            TUser user 
= (TUser)it.next();
        }

    }

    
public void queryMappingSql(){
        Query query 
= session.getNamedQuery("queryUser");
        query.setParameter(
"name","Erica");
        Iterator it 
= query.list().iterator();
        
while(it.hasNext()){
            TUser user 
= (TUser)it.next();
        }

    }

}

 

<?xml version="1.0"?>
<!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.testproject.hibernate.TUser" table="T_USER">
        
<id name="id" column="id">
            
<generator class="native"></generator>
        
</id>
    
</class>
    
<sql-query name="queryUser">
        
<![CDATA[
            select {usr.*} from T_User usr where name=:name
        
]]>
        
<return alias = "usr" class="com.testproject.hibernate.TUser"></return>
    
</sql-query>
    
<!-- 基于存储过程查询 sql-query节点的callable属性设定为true,指明当前查询基于存储过程定义-->
    
<sql-query name="getUsersByAge" callable="true">
        
<return alias="user" class="com.testproject.hibernate.TUser">
            
<return-property name="id" column="ID"></return-property>
            
<return-property name="name" column="NAME"></return-property>
            
<return-property name="age" column="AGE"></return-property>
        
</return>
        {?=call getUsersByAge(?)}
    
</sql-query>
</hibernate-mapping>






    与HQL相同,Native SQL也可以在实体映射文件中进行配置:

posted @ 2009-11-21 21:37 王永庆 阅读(235) | 评论 (0)编辑 收藏
     摘要:     实体Bean包含BMP和CMP两种类型。对BMP实体Bean而言,开发者必须提供各自的数据访问逻辑。为了实现BMP,通常会使用操作数据库的API,比如JDBC.对于CMP实体Bean而言,EJB容器会自动实现数据访问逻辑。这就是CMP的优势所在。通常,只有在应用服务器提供的CMP和目标RDBMS不能满足性能要求时,才去考虑BMP.此时,开发者能够细粒度调整BM...  阅读全文
posted @ 2009-11-21 12:32 王永庆 阅读(208) | 评论 (0)编辑 收藏

    Criteria提供了符合面向对象编程风格的查询封装模式。不过HQL提供了更加丰富灵活的特性,它在涵盖了Criteria功能范围的前提下,提供了更为强大的查询能力。HQL基于SQL,同时提供了更加面向对象的封装。
    实体查询:HQL子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分。需要注意的是,Hibernate中,查询的目标实体存在着继承关系的判定,如"from TUser"将返回所有TUser以及TUser子类的记录,我们知道,Java中所有类的根类都是java.lang.Object,那么,如下HQL将返回数据库中所有库表的记录:"from java.lang.Object",在where子句中,我们可以通过比较操作符指定条件,=,<>,>,>,<=,>=,between,notbetween,in,not in,is,like等。与SQL相同,我们可以通过and,or等逻辑连接符组合各个逻辑表达式。

    属性查询:有时我们并不需要获取完整的实体对象,只需要现实部分列,通过HQL也可以做到这点,如:"select user.name,user.age form TUser user"表明我们需要读取name和age属性的内容,而此时,返回的list数据结构中,每个条目都是一个对象数组(Object[]),其中一次包含了我们所获取的属性数据。
    如果觉得返回数组的方式不够符合面向对象的风格,我们可以通过在HQL中动态构造对象实例的方法对这些平面化的数据进行封装。"select new TUser(user.name,user.age) from TUser user",我们通过HQL获取数据的部分属性值,与此同时,我们也可以在HQL的select字句中使用统计函数,甚至原生SQL函数,或者利用distinct关键字,剔除返回集中的重复记录。

    实体更新与删除:在Hibernate2中,HQL仅仅用于数据查询,而在Hibernate3中,HQL具备了更加强大的功能。实体更新与删除就是其中的主要特征之一。

    分组和排序:与SQL类似,HQL通过order by子句实现对查询结果的排序,order by子句可以指定多个排序条件:"from TUser user order by user.name ,user.age desc"通过Group by子句可进行分组统计。如:"select count(user),user.age from TUser user group by user.age",我们知道where子句可以对记录进行甄选。那么,对于Group by子句获得的结果集我们可以通过Having子句进行甄选。例如:"select count(user),user.age from TUser user gourp by user.age having count(user)>10".

     参数绑定:类似JDBC中的SQL操作,我们可以通过顺序占位符"?"对参数进行标识,并在之后对参数内容进行填充。建议使用Query接口"from TUser user where user.name=? and user.age>?",这里除了顺序占位符,我们还可以使用引用占位符,如:"from TUser where name=:name"参数绑定机制可以使得查询语法与具体参数数值相互独立。这样,对于参数不同,查询语法相同的查询操作,数据库即可实施性能优化策略。同时,参数绑定机制也杜绝了参数值对查询语法本身的影响。

     引用查询:SQL语句混杂在代码之间将破坏代码的可读性,并使得系统的可维护性降低。为了避免这样的情况出现,我们通常采取将SQL配置化的方式,也就是将SQL保存在配置文件中,需要调用的时候在进行读取。
<query name="queryByName">
 <![CDATA[
  from TUser user where user.name=:name
 ]]>
</query>
之后,我们可通过session.getNamedQuery方法从配置文件中调用引用的HQL.

     联合查询:inner join,left outer join,right outer join,full join
     子查询:如:"from TUser user where (select count(*) from user.addresses)>1"HQL中,子查询必须出现在where子句中,且必须以一对圆括号包围。
     数据加载方式:Hibernate支持以下几种数据加载方式:
1、即时加载:当实体加载完毕后,立即加载其关联数据。
2、延迟加载:实体加载时,其关联数据并非即刻获取,而是当关联数据第一次被访问时再进行读取。
3、预先加载:预先加载时,实体及其关联对象同时读取,这与即时加载类似。
4、批量加载:对于即时加载和延迟加载,可以采用批量加载方式进行性能上的优化。

posted @ 2009-11-15 22:25 王永庆 阅读(312) | 评论 (0)编辑 收藏

持久化实体Bean的两种方式:
    既然需要将实体Bean映射到存储源中,应用肯定需要提供操作RDBMS的代码。
    Bean管理持久化实体Bean,是手工完成持久化行为的EJB类型。换句话所,组件开发者必须开发代码,以将内存中的持久化域存储到底层存储源中。这种方式成为BMP。
    EJB规范还提供了BMP的替代组件类型:借助于EJB容器完成数据的持久化。这就是容器管理持久化(CMP)。此时,通常都要将持久化逻辑从CMP中剥离出来。然后借助于容器提供的工具完成数据的自动持久化。最后,EJB容器将生成访问数据库的代码。注意,CMP是独立于任何O/RMapping技术的数据对象,因此可以在各种企业环境中重用CMP组件。
    CMP极大减少了实体Bean的代码量,因为不用直接编写JDBC代码了。EJB容器将会处理所有的持久化操作,这是EJB势能应用的优势之一。
    在执行ejbCreate()方法期间,即在初始化内存中的实体Bean时,将会在底层RDBMS中插入新的记录,并将这些记录同实体Bean实例建立起映射关系。当调用BMP实体Bean的ejbCreate()时,它将负责生成RDBMS中的数据。类似的,当调用BMP实体Bean的ejbRemo()时,它将负责RDBMS中数据的删除。

    在EJB领域中,客户并没有直接调用EJB实例,它们仅仅调用了EJB对象代理。借助于Home对象能够生成EJB对象。因此,对于定义在EJB Bean类中的各个ejbCreate()方法,在Home接口中也将存在对象的create()方法。当客户调用Home对象的create()方法时,容器将把调用请求委派给ejbCreate()方法。
    开发者可以通过多种方式查找实体Bean.需要在实体Bean的Home接口中列举出这些查找方法。我们称这些方法为"finder"方法。除了暴露创建、销毁实体Bean实例的方法外,Home接口还需暴露finder方法。这是实体Bean的Home接口同其他EJB类型中的Home接口的最明显区别。

    实体上下文,所有的EJB组件都存在上下文对象供组件访问到容器环境使用。这些上下文对象含有EJB容器设置的环境信息。因此EJB组件能够访问到上下文,从而获取各种信息,比如事务,安全性信息。对于实体Bean而言,存在javax.ejb.EntityContext上下文接口。它继承自EJBContext
public interface javax.ejb.EntityContext extends javax.ejb.EJBContext{
 public javax.ejb.EJBLocalObject getEJBLocalObject();
 public javax.ejb.EJBObject getEJBObject();
 public java.lang.Object getPrimarykey();
}
    通过调用getEJBObject()方法,当前客户能够获得某实体Bean实例对应的EJB对象。客户调用的是EJB对象,而不是实体Bean实例本身。因此,客户能够在应用中引用返回的EJB对象。
    实体Bean实例对应的主键可以通过getPrimaryKey()方法获得。主键唯一标识某实体Bean实例。当实体Bean实例存储到存储源中时,可以使用主键获得单个实体Bean实例。由于在RDBMS中也存在主键,因此主键能够唯一标识某个实体Bean实例。

posted @ 2009-11-15 12:21 王永庆 阅读(189) | 评论 (0)编辑 收藏

    实体Bean是持久化对象,它能够存储到持久化存储源中。实体Bean是EJB编程模型中最为重要的利器之一。
    将对象映射到RDBMS的技术称之为对象-关系映射。它能够实现内存对象同关系数据的相互转换。O/R映射器能够将Java对象映射到任意RDBMS模式。比如简单的O/RMapping引擎能够将Java类映射成SQL表定义。Java语言提供的对象序列化功能比O/RMapping简单多了。O/RMapping是更加复杂、成熟的对象持久化机制。通过将Java对象分解成关系数据,应用便能够查找到所需的数据了。
    通过如下两种方式能够完成Java对象到关系数据的映射。其一,通过硬编码实现O/RMapping.其二,借助于O/RMapping产品,自动完成映射过程,比如:Hibernate.
    对于任何成熟的、基于OO多层部署的企业应用而言,总可以划分出2种截然不同的组件类型。1、应用逻辑组件,2、持久化数据组件。会话Bean和实体Bean的最大区别在于实体Bean是实体,客户是可以看的到的。因此实体Bean能够独立于客户应用的生命周期。对于实体Bean而言,通过比较它们各自含有的数据便能够区分不同的实体Bean.这意味着客户能够引用单个的实体Bean实例并将它传入到其他应用中,不同的客户可以共享同样的实体Bean实例,这对于会话Bean是办不到的。会话Bean建模过程或者工作流。实体Bean本身就是客户,它就是持久化状态对象。
    实体Bean实例存在几方面的含义:
1、持久化数据的Java表示,即它能够从持久化存储源装载数据到内存中。同时,实体Bean实例能够将装载到的数据存储到实例的成员变量中。
2、通过修改内存中的Java对象可以改变数据的取值。
3、还可以将修改后的数据保存到存储源汇中,从而更新RDBMS中的物理数据。
    实体Bean是持久化对象,它能够长期存在。即使出现了不可恢复的失败,比如应用服务器瘫痪、数据库瘫痪,实体Bean还是能够存活的。原因在于实体Bean只是对底层具有容错行为的持久化存储源中数据的映射,因此,即使极其瘫痪,内存中的实体Bean实例还可以重新构建。在极其重启后,实体Bean实例需要从底层存储源装载数据,并使用获得的数据对实体Bean实例中的各个域进行setter操作。实体Bean比客户会话的生命周期要长。可以认为,数据库中记录存活的时间决定了实体Bean实例的生命周期。
    相同数据往往存在多分物理拷贝,比如内存中的实体Bean实例、实体Bean数据本身,他们都是对RDBMS中数据的拷贝。因此,EJB容器需要提供某种机制实现数据在Java对象和RDBMS间的自动传输。实体Bean的Bean类为此提供了2个特殊方法:
ejbLoad():它能够从持久化存储源中读取数据,并存储到实体Bean实例的域中。
ejbStore():它能够将当前实体Bean实例的域值保存到底层RDBMS中。
那么何时需要完成内存中实体Bean实例和RDBMS中数据的传递和转换,开发者需要知道是谁调用了ejbLoad()和ejbStore(),答案是EJB容器。它们是回调方法,供EJB容器调用。EJB规范要求所有的实体Bean组件必须提供它们。至于读取或存储数据的时机,由EJB容器决定。依据实体Bean实例当前的事务状态,EJB容器会自动计算出需要调用实体Bean实例中的ejbLoad(),ejbStore()方法的时机,这也是使用实体Bean组件的优势之一:开发者不用考虑java对象同步底层RDBMS的问题。

    为了满足大量并发客户访问同一数据的要求,架构师需要借助于实体Bean设计出高性能的访问系统。如下给出一种解决方案:允许多个客户共享同一实体Bean实例。因此,实体Bean实例能够同时服务多个客户。尽管表面上看是可行的,但是对于EJB而言,这是行不通的。原因有亮点:其一,为实现实体Bean实例服务多个并发客户,必须保证实体Bean实例是线程安全的,开发线程安全的代码并不是一件容易的工作,而且经常会出现一堆错我。其二,底层事务系统几乎不可能控制多个线程的并发执行,事务往往同具体的线程绑定在一起。因此,基于上述理由,单个实体Bean实例只能够在单线程环境中运行。对于所有的EJB组件而言,包括会话Bean、消息驱动Bean、实体Bean,它们都是以单线程方式运行的。
    当然,强制要求各个实体Bean实例只能同时服务单个客户,将引入性能瓶颈。由于实例以单线程方式运行,客户需要排队等候实体Bean实例,从而获得对实体Bean实例的调用,这对于大型企业应用而言,是不允许出现的
    为了提供系统性能,EJB容器会实例化同一实体Bean的多个实例。这使得多个客户能够并发同不同实体Bean实例进行交互,而这些实体Bean实例代表了同一RDBMS数据。事实上,这就是EJB容器的运行行为。因此,客户再也不用排队等候实体Bean实例,因为存在多个实体Bean实例了。
    一旦多个实体Bean实例代表了同一RDBMS数据,则引入了另外一个问题:数据瘫痪。如果多个实体Bean实例代表的数据是通过缓存管理的,则需要在内存中拷贝多分缓存中的数据。显然,某些缓存中的数据将变得陈旧,因此会出现很多过期的数据。
    为了实现实体Bean实例的缓存一致性,各个实体Bean实例必须同底层存储元进行同步。EJB容器将通过调用ejbLoad(),ejbStore()方法同步这些实体Bean实例。
    至于实体Bean实例同底层RDBMS数据的同步频率,则取决于事务。事务将各个客户请求隔离起来。借助于事务实现数据同步。

    EJB容器提供的实例池是很有意义的。当然,并不是只有实体Bean才存在实例池。在将实体Bean实例重新分配给不同EJB对象时,会存在一些问题,并要求容器去解决。比如当实体Bean实例被指定给EJB对象时,它可能还持有资源(比如Socket连接)。如果将实体Bean实例放置在实例池中,Socket连接不在需要。因此为实现资源的获取和释放,实体Bean的Bean类需要实现如下2个回调方法:
1、ejbActivate().在将实体Bean实例从实例池中取出来时,EJB容器会自动调用它。该过程称之为激活。进而,EJB容器会将实体Bean实例分配给某EJB对象,并同时获得主键对象。在执行ejbActivate()方法期间,实例需要获得所需的资源,比如Socke,否则,在将实体Bean实例分配给某EJB对象时,无法对资源进行操作。
2、ejbPassivate().在将实体Bean实例放置到实例池中时,EJB容器会调用它。注意,它也是回调方法。这一过程称之为挂起。进而,EJB容器需要从某EJB对象中取回分配于它的实体Bean实例,并将实例的主键对象也收回。在执行ejbPassivate()方法期间,需要释放ejbActivate()执行期间获得的相关资源,比如:Socket.
    一旦实体Bean实例被挂起,不但要释放它持有的资源,还将实例的状态信息保存起来。因此,实体Bean实例最新的状态信息可以从RDBMS中找到了。为了保存实体Bean实例的域信息到RDBMS中,容器要在挂起实例前调用ejbStore()方法。类似的,一旦实体Bean被激活,不但要获得所需的资源,还要从RDBMS装载最新的数据,为了完成数据的读取,EJB容器将在激活实体Bean实例后调用ejbLoad()方法。


 

posted @ 2009-11-15 11:46 王永庆 阅读(280) | 评论 (0)编辑 收藏
仅列出标题  下一页
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(1)

随笔分类

随笔档案

关注blogs

搜索

  •  

最新评论

阅读排行榜

评论排行榜