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 王永庆 阅读(350) | 评论 (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 王永庆 阅读(548) | 评论 (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 王永庆 阅读(206) | 评论 (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 王永庆 阅读(178) | 评论 (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 王永庆 阅读(248) | 评论 (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 王永庆 阅读(231) | 评论 (0)编辑 收藏
     摘要:     实体Bean包含BMP和CMP两种类型。对BMP实体Bean而言,开发者必须提供各自的数据访问逻辑。为了实现BMP,通常会使用操作数据库的API,比如JDBC.对于CMP实体Bean而言,EJB容器会自动实现数据访问逻辑。这就是CMP的优势所在。通常,只有在应用服务器提供的CMP和目标RDBMS不能满足性能要求时,才去考虑BMP.此时,开发者能够细粒度调整BM...  阅读全文
posted @ 2009-11-21 12:32 王永庆 阅读(205) | 评论 (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 王永庆 阅读(309) | 评论 (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 王永庆 阅读(185) | 评论 (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 王永庆 阅读(277) | 评论 (0)编辑 收藏
Spring提供了对Sun的标准的持久化API-JDO的整合,以及其他开放源码的ORM框架,如Hibernate、ApacheOJB和iBATIS SQL Maps.Spring对这些技术的支持没有像它对JDBC的支持那么广泛。
<?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/trainingDatasource</value>
        
</property>
    
</bean>
    
<!-- 管理Hibernate资源,在应用的整个生命周期里,你只要保存一个SessionFactory实例就可以了。-->
    
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        
<!-- 先要知道连接哪个数据源 -->
        
<property name="dataSource">
            
<ref bean="dataSource"/>
        
</property>
        
<!-- Hibernate本身有数十个属性,通过这些属性你就可以控制它的行为。当在Spring之外使用Hibernage的时候,
        Hibernate在应用的class path下的某个地方寻找一个名叫hibernate.properties的文件,并用它来进行配置。
        然而,用Spring就不需要在一个独立的属性文件里管理这些配置。 
-->
        
<property name="hibernateProperties">
            
<props>
                
<prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
            
</props>
        
</property>
        
<!-- 同样,你也要告诉Spring从哪里读取Hibernate.hbm.xml映射文件 -->
        
<property name="mappingResources">
            
<list>
                
<value>Student.hbm.xml</value>
            
</list>
        
</property>
        
<!-- 还有一种简单的方法设置映射文件资源,你可以用你应用的class path下的一个子路径来配置
        mappingDirectoryLocation属性,spring将找到这个路径下的每个*.hbm.xml文件,来配置SessionFactory 
-->
        
<property name="mappingDirectoryLocations">
            
<list>
                
<value>classpath:/com/springinaction/training/model</value>
            
</list>
        
</property>
    
</bean>
    
<!-- 如果把这个模版对象织入到一个个DAOBean中显得很麻烦的时候,可以使用Spring自动连接功能来将模版对象隐士的织入到
    DAObean. 
-->
    
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        
<property name="sessionFactory">
            
<ref bean="sessionFactory"/>
        
</property>
    
</bean>
    
</beans>
posted @ 2009-11-14 15:59 王永庆 阅读(248) | 评论 (0)编辑 收藏

调用存储过程:
Spring通过实现CallableStatementCallback来支持存储过程。假定有个存储过程的名字是ARCHIVE_STUDENTS,执行代码如下:

package com.testproject.spring.datasource;

import java.sql.CallableStatement;
import java.sql.SQLException;

import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.JdbcTemplate;
/*
 * 为了让JdbcTemplate工作,它所需要的,只是一个DataSource实例。
 
*/

public class StudentDaoImpl implements StudentDao {
    
private JdbcTemplate jdbcTemplate;
    
    
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        
this.jdbcTemplate = jdbcTemplate;
    }

    
/**
     * 调用存储过程,通过CallableStatementCallback来实现
     
*/

    
public void archiveStudentData(){
        CallableStatementCallback cb 
= new CallableStatementCallback(){
            
public Object doInCallableStatement(CallableStatement cs)throws SQLException{
                cs.execute();
                
return null;
            }

        }
;
        jdbcTemplate.execute(
"{ARCHIVE_STUDENTS}",cb);
    }

}


把操作创建成对象:

插入:

package com.testproject.spring.datasource;

import java.sql.Types;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
/**
 * Spring提供了一种真正把数据库操作建模成对象的方法,这样就在的代码和直接JDBC之间又加了一个绝缘层。
 * 首先,这些数据库操作对象是线程安全的,意味着对于每个数据库操作,你只需创建一个实例。
 * 其次,任何数据库操作对象必须在运行前先编译一下,这样就让对象知道什么时候可以预备statement,以便在稍后能执行它们。
 * 使用:
 * private InsertPerson insertPerson;
 * public int insertPerson(Person person){
 *     return insertPerson.insert(person);
 * }
 *
 
*/

public class InsertPerson extends SqlUpdate {
    
public InsertPerson(DataSource ds){
        
//首先要给sqlUpdate提供一个DataSource,用来创建JdbcTemplate
        setDataSource(ds);
        setSql(
"insert into person(id,firstName,lastName) values(?,?,?)");
        
//其次,我们需要为statement中的每个参数调用这个方法,顺序也是很重要的
        declareParameter(new SqlParameter(Types.NUMERIC));
        declareParameter(
new SqlParameter(Types.VARCHAR));
        declareParameter(
new SqlParameter(Types.VARCHAR));
        
//最后编译它,每个数据库操作对象必须在它被使用之前编译好。
        compile();
    }

    
public int insert(Person person){
        Object[] params 
= new Object[]{
                person.getId(),
                person.getFirstName(),
                person.getLastName()
        }
;
        
return update(params);
    }

}

查询:

package com.testproject.spring.datasource;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.MappingSqlQuery;

/**
 * 使用:
 * private PersonByIdQuery personByIdQuery;
 * public person getPerson(Integer id){
 *     Object[] params = new Object[]{id};
 *  return (Person)personByIdQuery.execute(params).get(0);
 * }
 *
 
*/

public class PersonByIdQuery extends MappingSqlQuery {
    
    
public PersonByIdQuery(DataSource ds){
        
super(ds,"select id,first_name,last_name from person where id=?");
        declareParameter(
new SqlParameter("id",Types.INTEGER));
        compile();
    }

    
    
protected Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
        Person person 
= new Person();
        person.setId((Integer)rs.getObject(
"id"));
        person.setFirstName(rs.getString(
"first_name"));
        person.setLastName(rs.getString(
"last_name"));
        
return person;
    }


}

 

posted @ 2009-11-14 15:21 王永庆 阅读(267) | 评论 (0)编辑 收藏
实现WEB服务客户
web服务是跨平台的、跨语言的,所以WEB服务的客户端使用的语言和平台和他的服务端没有必然的关系。比如我们可以在.Net下开发、部署web服务,然后在J2EE平台下调用它,或者使用在J2EE平台下开发、部署web服务,然后使用VB来调用它。
在J2EE平台中,WEB服务客户同EJB客户类似。如下两种方式能够访问到WEB服务:
1、不借助于JNDI查找,即使用单独的JAX-RPC客户访问。
2、借助于JNDI上下文访问WEB服务的J2EE客户
第一种方法称之为静态存根,此时需要在客户端预先生成SOAP客户存根,这同RMI存根类似。第二种方法称之为动态代理。它将在运行时获得WSDL描述,然后创建动态代理。无论采用哪种方式访问WEB服务,客户都需要获得服务Endpoint地址URL,否则单凭JNDI名是很难访问到WEB服务的。
package com.testproject.ejb.web;

import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;

/**
 * 单独JAX-RPC客户代码实例。
 * 它使用了动态代理方法,以获得对远程WEB服务的引用。
 *
 
*/

public class HelloClient {
    
static String host = "localhost";
    
//web服务的名字
    static String serviceURL = "HelloBean";
    
//名称空间
    static String nameSpaceUri = "urn:examples";
    
static String serviceName = "HelloWorldWS";
    
//web服务调用的地址
    static String serviceEndpointAddress = "http://"+host+":8000"+serviceURL;
    
public static void main(String[] args)throws Exception{
        HelloInterface hello 
= null;
        
//动态代理方法
        
//1、指定WSDL文件的位置
        URL url = new URL(serviceEndpointAddress+"?WSDL");
        
//2、创建服务工厂实例
        ServiceFactory serviceFactory = ServiceFactory.newInstance();
        
//3、创建服务对象,以作为代理工厂
        Service HelloService = serviceFactory.createService(url,new QName(nameSpaceUri,serviceName));
        
//4、获得port引用
        hello = (HelloInterface)HelloService.getPort(HelloInterface.class);
        
//调用hello()方法
        System.out.println("Dynamic Proxy:"+hello.hello());
    }

}

posted @ 2009-11-13 16:45 王永庆 阅读(130) | 评论 (0)编辑 收藏
EJB2.1和web服务
1、下载安装服务器后,需要设置环境变量。
Set J2EE_HOME=J2EE安装目录
Set CLASSPATH 
=%CLASSPATH%;%J2EE_HOME%\\lib\\j2ee.jar;.
Set Path =%Path%;%J2EE_HOME%\\bin;.

2、创建初始目录
+HelloBeanService
    +META-INF(application.xml;sun-j2ee-ri.xml )
    +ejb
       +META-INF(ejb-jar.xml; webservices.xml;mapping.xml)
       -*.java文件(EJB相关的类)
       -config.xml
3、定义web服务接口
package com.testproject.ejb.web;

/*
 * Hello服务Endpoint接口
 * 需要注意的是,这里使用的是EJB2.1无状态会话Bean,它作为WEB服务端点时可以不提供Home接口和Remote
 * Locale接口,它提供的是web服务端点接口,这个接口扩展了Remote接口。
 
*/

public interface HelloInterface extends java.rmi.Remote {
    
public String hello()throws java.rmi.RemoteException;
}


4、实现服务端点接口
package com.testproject.ejb.web;

import java.rmi.RemoteException;

import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

/*
 * 定义了服务端点接口,接下来的任务就是开发无状态会话Bean。无状态会话Bean同样需要实现SessionBean接口,
 * 服务端点接口定义的方法在会话Bean中实现。
 
*/

public class HelloBean implements SessionBean {
    
    
public void ejbCreate(){
        
/*
         * 这里不能接收任何参数
         
*/

    }


    
public void ejbActivate() throws EJBException, RemoteException {
    }


    
public void ejbPassivate() throws EJBException, RemoteException {
    }


    
public void ejbRemove() throws EJBException, RemoteException {
    }


    
public void setSessionContext(SessionContext arg0) throws EJBException,
            RemoteException 
{
    }

    
/*
     * 业务方法:输出hello
     
*/

    
public String hello(){
        
return "Hello wyq";
    }


}


5、创建EJB描述
用于部署成Web服务的无状态会话Bean的描述符和普通的无状态会话Bean不同ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1"
xmlns
="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
    
<display-name>HelloBean</display-name>
    
<enterprise-beans>
        
<session>
            
<display-name>HelloBean</display-name>
            
<ejb-name>HelloBean</ejb-name>
            
<service-endpoint>com.testproject.ejb.web.HelloInterface</service-endpoint>
            
<ejb-class>com.testproject.ejb.web.HelloBean</ejb-class>
            
<session-type>Stateless</session-type>
            
<transaction-type>Container</transaction-type>
        
</session>
    
</enterprise-beans>
</ejb-jar>

在这个新的部署描述符中,使用<service-endpoint>指定了服务端点,同时,必须指定EJB为无状态会话Bean
6、生成WEB服务描述

下面的任务就是生成一个Web服务描述,我们通常使用工具来生成这个描述符。在这里使用J2EE提供的wscompile工具来生成。在使用wscompile工具生成web服务描述前,首先手工编写一个简单的XML描述config.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
    
<service 
        
name="MyHelloBeanService" 
        targetNamespace
="urn:HelloBean" 
        typeNamespace
="urn:HelloBean"
        packageName
="helloBeanService">
        
<interface name="com.testproject.ejb.web.HelloInterface"/>
    
</service>
</configuration>

在这个描述中,指定了目标的名称空间、包的名字和Web服务端点接口:HelloInterface。如下命令就可以生成一个web服务描述

c:\\ HelloBeanService\\ejb \\>wscompile -define -d . -nd . -classpath . config.xml
自动生成MyHelloBeanService.wsdl文件。
7、编写一个web服务映射文件:
在ejb\\META-INF目录下新建一个mapping.xml文件,然后编辑这个描述符
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE java-wsdl-mapping PUBLIC
    "-//IBM Corporation,Inc.//DTD J2EE JAX-RPC mapping 1.0//EN"
    "http://www.ibm.com/standards/xml/webservices/j2ee/j2ee_jaxrpc_mapping_1_0.dtd"
>
<java-wsdl-mapping>
    
<package-mapping>
        
<package-type>HelloBeanService</package-type>
        
<namespaceURI>urn:HelloBean</namespaceURI>
    
</package-mapping>
</java-wsdl-mapping>
8、编写webservices.xml文件
另外,还需要提供webservices.xml文件,并存放到Ejb-jar存档的META-INF目录中。各个J2EE产品可能提供相应的部署工具来创建这个文件。
9、EJB打包
另外,web服务部署描述符在EJB-JAR文件中的位置是META-INF/webservices.xml.把生成的HelloBeanService.wsdl拷贝到ejb\\META-INF目录下
c:\\HelloBeanService\\ejb\\jar cvf ejb.jar com META-INF
10、Application打包
HelloBeanService\\MEAT-INF目录下创建2个文件:application.xml和sun-j2ee-ri.xml
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC '-//Sun Microsystems,
    Inc.//DTD J2EE Application 1.3//EN'
    'http://java.sun.com/dtd/application_1_3.dtd'
>
<application>
    
<description>Application description</description>
    
<display-name>HelloBeanServiceApp</display-name>
    
<module>
        
<ejb>ejb.jar</ejb>
    
</module>
</application>

最后归档
c:\\ HelloBeanService\\ejb\\>copy ejb.jar ../
c:\\ HelloBeanService\\ejb\\>cd..
c:\\ HelloBeanService\\\\>jar cvf encryptservice.ear ejb.jar META-INF

 


 

posted @ 2009-11-13 16:13 王永庆 阅读(149) | 评论 (0)编辑 收藏
     摘要:     Spring的一个目标就是让你遵循对接口编程的面向对象原则。DAO的存在提供了读写数据库中数据的一种方 法。只要把这个功能通过接口暴露,应用的其他部分就可以通过这些接口访问数据库了。     在Spring的DAO框架里,Connection对象是通过DataSource获得的。     从JN...  阅读全文
posted @ 2009-11-12 19:46 王永庆 阅读(177) | 评论 (0)编辑 收藏
    这次的辞职和以往有些不同,以前的辞职都是怀着迫不及待的心情,而且离职之后心情特别舒畅。但是这次辞职之后总是感觉心里空荡荡的。由于从金钱、从发展、从技术、还有从管理的经验上,我都觉得这里没有我发展的空间,感觉自己有能力带出一个出色的团队,所以离职是必须的。但是这种空荡的源头我还没有找到,是想念以前的那些好兄弟、好姐妹。还是想念那个不知道我在想什么的她。
    感觉自己心里的牵挂太多,不是说她。自己做了10多个场景,有复杂有简单的,离开公司之后,还想着那些接我的人能不能把我的场景接好。就在昨天晚上,还有个新来的小伙问了我些问题,看来接手的不是太顺利。没有办法,公司的开发模式就是这样,有利有弊,接手的人只要能干活就可以了,不需要你思考。我讨厌这种模式,而且他也没有重要的对待我。只有离开是我的选择。担心我们的团队,担心我的主管,担心我的队友。现在团队非常混乱,有很多人提出离职,他们太盲目了,没有目的性,就像我和我好朋友说的,我是谋定而后动,他则是先动而后谋,他的下分工作目标可能是工作轻松点,钱拿的多点,我们公司确实太累了。而我的目标就是项目经理,在这个公司的期间,我每次看到团队遇到困难的时候,我都能看到事情的本质,所以看问题还是很准的,自己也想去尝试一下。
     你的羽翼已经丰满了,就是缺少在蓝天下翱翔的经验,自己慢慢积累吧,在这个项目里我不能在给你什么了,我剩下的只有自己的经验和技术,自己的经验属于自己的。前几天还在哭鼻子的你,坚强点吧,你已经长大了,遇到问题想办法去解决它,不要去回避。不知道你想不想在这个程序的世界里自由翱翔,不知道你的心有没有那么大,我看到的你是不属于程序的这片天空的,所以还在担心走在这条路上的你。多余了,因为飞在路上的你根本就没想到你在成长路上的我。
    希望你们一切都好,我的大家,我也会在自己的道路上努力,我相信我自己,我也相信我们的友谊。
posted @ 2009-11-11 11:35 王永庆 阅读(183) | 评论 (0)编辑 收藏
年终奖"除12"税收优惠

按照《国家税务总局关于调整个人取得全年一次性奖金等计算征收个人所得税方法问题的通知》规定,对个人某个月取得的全年一次性奖金,可以先除以12个月,再按其商数确定适用税率和速算扣除数。

具 体的解释是,行政机关、企事业单位向其雇员发放的一次性奖金(包括:年终加薪、实行年薪制和绩效工资办法的单位,根据考核情况兑现的年薪和绩效工资)可单 独作为一个月工资、薪金所得计算纳税,并按以下计税办法:先将雇员当月内取得的全年一次性奖金,除以12个月,按其商数确定适用税率和速算扣除数。如果雇 员当月工资薪金所得高于(或等于)税法规定的费用扣除额(个税起征点)的,则将一次性奖金全额作为应税所得额,计算应纳税额。适用公式为:应纳税额=雇员 当月取得全年一次性奖金×商数对应的适用税率-速算扣除数。

而在这个规定之前,个人所取得的年终奖金是全额作为应纳税所得额来寻找适用税率及速算扣除数的。具体公式为:应纳税额=雇员当月取得的全年一次性奖金*奖金全额对应的适用税率-对应的速算扣除数。

可别小看这"12",如此这般将年终奖先除以12,所得税率可就大大降低了,能为员工减轻不少税负呢。

比 如,某私营企业主王老板要给自己的得力干将张先生准备年终奖。年终奖为12000元,按原有规定,12000元作为单月收入,对应的适用税率为20%,速 算扣除数为375元,则张先生这笔年终奖应纳个人所得税款为2025元(12000×20%-375)。但若按新办法,张先生12000元年终奖先除以 12,得到商数为1000元,1000元对应的适用税率为10%,速算扣除数为25。因此,新办法下,张先生只需交纳个人所得税1175元 (12000×10%-25)。也就是说,如果采用新办法,张先生可少交850元的个人所得税。换而言之,张先生实际拿到手的税后年终奖可以多850元。

月薪较低者还有"特别"优惠

对于日常每月工资较低的个人,年终奖还会有另一层税收优惠。为此,平常对员工比较"抠门"的老板,可要发发善心,在过年前给员工多些奖金哦。

按 照前述国家税务总局的《通知》规定,如果在发放一次性奖金的当月,雇员工资薪金所得低于税法规定的费用扣除额(即个税起征点,2006年1月1日后全国统 一为1600元),应将全年一次性奖金减去"雇员当月工资薪金所得与费用扣除额的差额"后的余额作为应纳税额,再按前述办法确定全年一次性奖金的适用税率 和速算扣除数。

具体如下:如果雇员当月工资薪金所得低于税法规定的费用扣除额的,适用公式为:应纳税额=(雇员当月取得全年一次性奖金-雇员当月工资薪金所得与费用扣除额的差额)×适用税率-速算扣除数。

比 如,王老板手下有个普通工人蒋某,虽然月薪只有1200元,但平常工作非常卖力,尤其最近大半年来表现特别突出,长进很快。新年到了,王老板准备在年轻工 人中间树立一个学习典型,蒋某正是一个不错的选择。那么,王老板完全可以通过给蒋某多发放一些年终奖,并拉开蒋某所获年终奖与其他同级工人年终奖所得距离 的方式,暗示大家公司是"奖优惩劣"的,由此激励年轻人要奋发图强,多为企业做绩效。若王老板此次给蒋某发放6000元的年终奖,按照原来的老办 法,6000元奖金对应的所得税率为20%,速算扣除数为375元,蒋某需要被缴纳的个人所得税额为825元(6000×20%-375),实际拿到手的 税后奖金为5175元。

而按照新办法,则蒋某这笔年终奖可以先除以12,得到商数为500,对应的所得税率为5%,速算扣除数为0。 同时,由于蒋某当月的工资收入只有1200元,比国家规定的1600元费用扣除额还低400元,因此蒋某这笔年终奖实际的应纳税额为5600元 (6000-400),其应该被缴纳的个人所得税额为108元(5400×5%-0),实际拿到手的税后奖金有5892元。

分次奖金不如归总发放

但是,老板们还必须明确一个概念,国家税务总局对于年终奖这样一次性发放的奖金税收优惠政策,每个员工每年只能享受一次。

因为前述《通知》规定,在一个纳税年度里,对每一个纳税人,该计算办法只允许采用一次。同时,雇员取得除全年一次性奖金以外的其他各种名目奖金,如半年奖、季度奖、加班奖、先进奖、考勤奖等,仍旧是一律与当月工资、薪金合并,按税法规定缴纳个人所得税。

正 是基于这一点,我们在此提出一个发放奖金的窍门,就是如果老板们想为员工尽量减轻税负,应尽量减少季度奖、半年奖等名目繁多的奖金发放次数,而是把这些本 欲发放的奖金先在平时"记账",然后和年终奖一起,一次性发给员工。这样一来,员工全年得到的税后奖金收入,并不会减少,反而会增加。

比 如,作为王老板得力助手的张先生,除了12000元的年终奖,王老板每个季度还会发2000元的季度奖金给他。由于张先生的月工资为6000元,平常每月 的应纳税所得额6000-1600=4400元,对应的适用税率为15%,速算扣除数为125元,应纳税额为535元。但每次发放季度奖时,都必须与其当 月的工资收入合并缴税,所以每次发放季度奖的那个月,也是他最不愿意看到工资明细单的那个月--那个月的所得税率和所得税额令他非常"郁闷"。在那些月份 里,他的当月所得总额为8000元,减去1600元的起征点(费用扣除额),应纳税所得额为6400元,对应的适用税率为20%,对应的速算扣除数为 375元,应纳税额为905元。所以说,2000元的季度奖为他当月增加了370元(905-535)的税收负担。一年四个季度,四次季度奖一共缴税 1480元。加上12000元的单次年终奖所得税为1175元,那么张先生需要全年总共2万元的奖金缴纳所得税2655元。

如果王老板平时不给张先生分次发季度奖,而是将总共8000元的季度奖与12000元的年终奖一起放在元月发放,那么张先生的年度奖金一共需要缴税1975元(20000×10%-25)。

显然,对于张先生而言,一般来说是更乐意接受老板一次性给他发放年终奖2万元,而不是将2万元拆开为季度奖和年终奖分次发放,因为这样可以减少680元的税金负担。

我国个人所得税税率表(工资、薪金所得适用)

级数 全月应纳税所得额 税率(%) 速算扣除数(元)
1 不超过500元的 5 0
2 超过500元至2000元的部分(含2000元) 10 25
3 超过2000元至5000元的部分(含5000元) 15 125
4 超过5000元至20000元的部分(含20000元) 20 375
5 超过20000元至40000元的部分(含40000元) 25 1375
6 超过40000元至60000元的部分(含60000元) 30 3375
7 超过60000元至80000元的部分(含80000元) 35 6375
8 超过80000元至100000元的部分(含100000元) 40 10375
9 超过100000元的部分 45 15375


注:正常月份下,工资,月应纳税所得额= 月工资、薪金所得-1600元;月应纳税额= 月应纳税所得额×适用税率-速算扣除数
posted @ 2009-11-09 10:57 王永庆 阅读(1003) | 评论 (0)编辑 收藏
    Web服务是构建B2B应用最为重要的利器之一,使用它能够构建、集成大型的系统。这一切都是通过发送XML消息给已定义的模块化接口完成的。借助于无状态会话Bean能够构建Web服务。为实现Java客户对Web服务的访问,需要使用到JAX-RPC.Web服务是构建SOA的一种方式。SOA是架构方法,它能够将各种异构应用集成起来,并组成更大的分布式应用,最后通过服务接口的形式将整个应用支撑起来。
    服务提供者创建抽象服务定义,并将它发布到服务注册器中。对于WEB服务而言,服务定义是通过WSDL文件给出的;服务注册器需要遵循UDDI标准。服务请求者可以使用一套查询机制从服务注册器中查找到WSDL文件,如果找到合适的服务定义,请求者将绑定到服务提供者上。
    服务接口类似于对象或EJB接口。但是对于WEB服务而言,服务接口更为灵活。比如服务实现和客户处于松耦合的关系,而这在其他EJB应用或分布式应用中是实现不了的。这种松耦合使得客户和服务实现能够运行在不同平台上,比如Microsoft.NET客户能够访问到J2EE应用服务器中所运行的服务。另外,相比Java对象而言,Web服务实体的粒度更粗。
    通过标准化接口,能够加强SOA的模块化;通过松耦合,能够提高SOA的灵活性;通过XML,能够增强SOA扩展性。在B2B场合,这些都是很重要的因素。WEB服务能够在那些使用了多种异构平台的企业应用中大显身手。所有的WEB服务都是构建在XML和Internet协议上的。EJB只能够采用Java开发。如果使用其他语言开发应用,互操作如何实现?
    组成WEB服务的事实标准可以通过如下等式表达:
    Web服务 = WSDL+SOAP+UDDI

1、WSDL语言比Java更抽象、广度更大。HelloWorldWSDL中存在<service>标签,它能够在具体地址提供若干<port>.<port>代表了服务接口及对具体协议的绑定。
2、服务描述包含了Endpoint地址。WSDL是由Java接口和对象引用构成的。换句话说,Web服务不存在真正的实体,它们不是对象,因此必须将它们按照模块化对待。
3、根据输入、输出定义操作。开发者需要将输入、输出消息表示为XML元素。
4、采用的绑定是SOAP绑定。到目前为止,只能采用SOAP绑定。也请注意,<soap:binding>标签还存在style="rpc"属性,因此可以看出style还存在其他取值,目前,用于交换SOAP消息的另一种风格是文档风格(document-style)。文档风格意味着在SOAP消息体中不在包含对具体操作的描述。

    SOAP协议为Web服务和它们的客户定义了XML消息格式。SOAP消息格式非常简单。它实际包含两部分内容:其一,HTTP POST请求头信息。其二,XML文档,这就是SOAP信封(Envelope)。另外,通过上述两分SOAP消息,我们还想表达Web服务中的另一个重要概念。从SOAP协议的使用过程来看,它非常轻量,因为它对处于交互中的客户和服务没有任何限制。但是,从SOAP协议的大小、性能角度考虑,它并不轻量。如果不压缩SOAP消息,则需要在网络上传递大量的SOAP数据消息。因此,采用二进制更为明智,比如CORAB的IIOP协议。对于含有大量的SOAP数据消息进行压包和解包操作将耗费大量的CPU时间。请不要忘记,WEB服务的诞生背景,它只是为集成应用而来的,而不是替换现有的高性能中间件平台技术。否则,就不需要使用web服务了。

    XML文档和平台无关性,借助于标准化的XML文档,WEB服务能够帮助集成异构的分布式系统。在实际应用开发过程中,它具有许多优势,比如松耦合、平台无关性、遵循XML方面的标准、能够合并不同的XML技术等。借助于XML能够实现应用和中间件平台的松耦合,这是很重要的优势。
    比如在开发EJB组件时,客户和服务器端同事需要使用组件接口。一旦组件接口发生变动,比如需要往方法签名中新增参数,则EJB组件本身需要重新构建、集成、部署。当然,客户也逃脱不了重新编译的命运。因此,它们之间并没有实现松耦合,开发者不能独立的开发应用组件。否则,一旦某些组件发生变动,应用的其他部分也需要变动。我们都希望应用具有良好的扩展性。对基于IIOP的请求消息而言,所有的参与者必须使用相同的类型信息,否则不能够正确对消息进行解包操作。而且,在IIOP技术领域中,应用必须能够理解消息的所有内容。如果基于XML和XML消息开发应用,则这些限制将不复存在。
   
    J2EE平台技术对WEB服务提供了一流的支持能力。无论WEB服务实现,还是WEB服务客户,都可以使用JAVA语言开发完成。在J2EE中,使用WEB服务同RMI、RMI-IIOP并没有多大区别。他们都将具体传输层屏蔽掉,使得开发者能够专注于业务逻辑。
    JSR921规范,即实现企业WEB服务,定义了WEB服务编程模型。它使用术语port组件,描述web服务的服务器端视图。它是服务接口的java实现。而且,它遵循服务接口到Java的映射,并提供了相应的Java实现。需要将它部署并运行在容器中。使用EJB开发WEB服务需要创建若干port组件。其中,还需要提供若干XML部署描述符。当然,直接基于EJB实现WEB服务是J2EE平台中实现WEB服务的最大优势。

    JAX-RPC服务端点接口,现在,来开发另一远程接口,它将企业Bean所提供的业务方法都列举出来,我们称之为服务Endpoint接口(Service Endpoint Interface,SEI),下面给出了代码。
   

package com.testproject.ejb.web;

/*
 * Hello服务Endpoint接口
 * 需要注意的是,这里使用的是EJB2.1无状态会话Bean,它作为WEB服务端点时可以不提供Home接口和Remote
 * Locale接口,它提供的是web服务端点接口,这个接口扩展了Remote接口。
 
*/

public interface HelloInterface extends java.rmi.Remote {
    
public String hello()throws java.rmi.RemoteException;
}

 

    JAX-RPC要求WEB服务提供SEI,EJB容器同WEB服务交互时需要使用它。JAX-RPC规范要求SEI遵循如下规则:
1、接口必须间接或直接继承java.rmi.Remote.
2、所有的方法必须抛出java.rmi.RemoteException.
3、方法参数和返回类型必须是JAX-RPC支持的JAVA类型。
4、SEI中不能包含敞亮。

    在J2EE1.4平台中,可以对EJB技术进行了升级,使得无状态会话Bean可以直接部署为Web服务端点。这样,在J2ee1.4平台下,开发Web服务将更加简单。这样,EJB2.1中的无状态会话Bean可以有3种不同的客户端:本地接口的客户端、远程接口的客户端和web服务客户端。EJB的WEB服务客户端视图通过WSDL文档描述。
    待续......

 


 


    

   
posted @ 2009-11-09 10:22 王永庆 阅读(145) | 评论 (0)编辑 收藏
当我们有很多类需要通知时,显示的创建每个代理就会显得很笨拙。幸运的是,Spring有一个自动代理机制,它可以让容器为我们产生代理。Spring有2个类提供这种服务:BeanNameAutoProxyCreate和DefaultAdvisorAutoProxyCreator.

BeanNameAutoProxyCreate:为匹配一系列名字的Bean自动创建代理。它也允许在名字的2端进行通配符的匹配。
<?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="performanceThresholdInterceptor" class="com.wyq.spring.common.aopinstance.autoproxy.PerformanceThresholdInterceptor">
        
<constructor-arg>
            
<value>5000</value>
        
</constructor-arg>
    
</bean>
    
<!-- 
        如果Bean是一个Advisor或拦截器,它将应用到代理对象的所有方法上。如果是通知的话,Advisor切入点
        会根据不同Bean将通知应用到不同的地方。
     
-->
    
<bean id="performanceThresholdProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        
<property name="beanNames">
            
<list>
                
<value>*Service</value>
            
</list>
        
</property>
        
<property name="interceptorNames">
            
<value>performanceThresholdInterceptor</value>
        
</property>
    
</bean>
</beans>
更强大的自动代理创建器是DefaultAdvisorAutoProxyCreator.当ApplicationContext读入所有Bean的配置信息后,DefaultAdvisorAutoProxyCreator将扫描上下文,寻找所有的Advisor.它将这些Advisor应用到所有符合Advisor切入点的Bean中。
<?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="performanceThresholdInterceptor" class="com.wyq.spring.common.aopinstance.autoproxy.PerformanceThresholdInterceptor">
        
<constructor-arg>
            
<value>5000</value>
        
</constructor-arg>
    
</bean>
    
<!-- 
        一个Advisor是一个切入点和一个通知的结合体。不用显示的将Advisor与其他东西结合,现在只要简单的定义他们,然后让他们自动
        应用到他们匹配的地方。这样松耦合Bean以及他们的通知就实现了。你只管写好你的Bean,写好你的通知,让容器来充当媒婆。
     
-->
    
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        
<property name="advice">
            
<bean class="com.wyq.spring.common.aopinstance.autoproxy.PerformanceThresholdInterceptor"></bean>
        
</property>
        
<property name="pattern">
            
<value>.+Service\..+</value>
        
</property>
    
</bean>
    
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    
</bean>
</beans>

posted @ 2009-11-06 16:00 王永庆 阅读(247) | 评论 (0)编辑 收藏
引入与其他类型的通知不同,引入影响的是整个类。他们通过给需要消息的类添加方法和属性来实现。引入让你能够动态地建立复合对象,提供了多态继承的好处。
Spring通过一个特殊的方法拦截器接口IntroductionMethodInterceptor来实现引入。这个接口添加一个方法:
boolean implementsInterface(Class intf);
如果IntroductionMethodInterceptor是为了实现指定接口,那么方法implementsInterface应该返回true.就是说,调用这个接口声明的方法的任何调用将被委派给IntroductionMethodInterceptor的invoke()方法.invoke()方法负责实现这个方法,不能调用MethodInvocation.proceed().他引入了新的接口,调用目标对象是没有用的。

Spring提供了一个方便的类来处理我们的大多数应用:DelegatingintroductionInterceptor.代码:
package com.wyq.spring.base.aopinstance;

import java.util.Date;

/** 
 * 
@author 作者 
 * 
@version 创建时间:2009-11-6 下午02:58:21 
 * 类说明 
 
*/
public interface Auditable {
    
void setLastModifiedDate(Date date);
    Date getLastModifiedDate();
}

package com.wyq.spring.base.aopinstance;

import java.util.Date;

import org.springframework.aop.support.DelegatingIntroductionInterceptor;

/** 
 * 
@author 作者 
 * 
@version 创建时间:2009-11-6 下午03:00:06 
 * 类说明 
 
*/
public class AuditableMixin extends DelegatingIntroductionInterceptor implements
        Auditable {
    
private Date lastModifiedDate;
    
/*
     * 注意我们不用实现invoke()方法了,DelegatingIntroductionInterceptor为我们实现了这
     * 个方法。DelegatingIntroductionInterceptor也要实现你的混合类暴露的任何方法,并且将
     * 任何对这些方法的调用委托给这个混合类。因为我们的类实现了Auditable,对这个接口的方法的
     * 所有调用都将调用我们的拦截器。任何其他方法将委托给目标对象。
     
*/
    
public Date getLastModifiedDate() {
        
return lastModifiedDate;
    }

    
public void setLastModifiedDate(Date lastModifiedDate) {
        
this.lastModifiedDate = lastModifiedDate;
    }

}

创建一个引入Advisor:
因为引入通知只应用在类层次上,所以引入有他们自己的Advisor:IntroductionAdvisor.Spring也提供了一个适合大多数情况的缺省实现。它的名字叫做DefaultIntroductionAdvisor.
BeanFactory对象是一个负责创建其他JavaBean的JavaBean.我们的ProxyFactoryBean创建代理对象。和其他JavaBean一样,它有控制行为的属性。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    
<!-- 创建一个目标对象 -->
    
<bean id="courseTarget" class="com.springinaction.training.model.Course"></bean>
    
<!-- 创建一个引入 -->
    
<bean id="auditableMixin" class="com.springinaction.training.advice.AuditableMixin"></bean>
    
<!-- 创建一个引入通知 -->
    
<bean id="auditableAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
        
<constructor-arg>
            
<ref bean="auditableMixin"/>
        
</constructor-arg>
    
</bean>
    
<!-- 创建一个代理
        
ProxyFactoryBean的属性
         target:代理的目标对象
         proxyinterfaces:代理应该实现的接口列表
         interceptorNames:需要应用到目标对象上的通知Bean的名字,可以是拦截器、Advisor或者其他通知类型的名字。
         singleton:在每次抵用getBean时,工厂是否返回的是同一个代理实例。如果是有状态通知,应该设置为false.
         aopProxyFactory:通常不需要使用这个属性。
         ProxyTargetClass:是否代理目标类,而不是接口类。
      -->
    
<bean id="course" class="org.springframework.aop.framework.ProxyFactoryBean">
        
<property name="proxyTargetClass">
            
<value>true</value>
        
</property>
        
<property name="singleton">
            
<value>false</value>
        
</property>
        
<property name="proxyInterfaces">
            
<value>com.springinaction.training.advice.Auditable</value>
        
</property>
        
<property name="interceptorNames">
            
<ref bean="auditableAdvisor"/>
        
</property>
        
<property name="target">
            
<ref bean="courseTarget"/>
        
</property>
    
</bean>
</beans>

posted @ 2009-11-06 15:35 王永庆 阅读(156) | 评论 (0)编辑 收藏
现在你可以为你的应用系统创建可重复使用的切入点了。Spring支持在这些切入点上进行操作-合并与交叉-来创建新的切入点
。只有当所有切入点都匹配时交叉集合才匹配,任何一个切入点匹配都会使合并集合匹配。为了对2个Pointcut对象进行合并,必须使用Pointcuts类。例如:
Pointcut union = Pointcuts.union(pointcut1,pointcut2);
这种方式的一个缺点是它需要通过编码来实现。
package com.wyq.spring.base.aopinstance;

import java.util.List;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.Pointcuts;

/** 
 * 
@author 作者 
 * 
@version 创建时间:2009-11-6 下午02:18:03 
 * 类说明 
 
*/
public class UnionPointcut implements Pointcut {
    
//声明合并的Pointcut实例
    private Pointcut delegate;
    
    
public Pointcut getDelegate() {
        
return delegate;
    }

    
public void setDelegate(Pointcut delegate) {
        
this.delegate = delegate;
    }
    
//委托给Pointcut的方法
    public ClassFilter getClassFilter() {
        
return getDelegate().getClassFilter();
    }

    
public MethodMatcher getMethodMatcher() {
        
return getDelegate().getMethodMatcher();
    }
    
//创建组合切入点
    public void setPointcuts(List pointcuts){
        
if(pointcuts == null || pointcuts.size()==0){
            System.out.println(
"没有要组合的切入点");
        }
        delegate 
= (Pointcut)pointcuts.get(0);
        
for(int i=1;i<pointcuts.size();i++){
            Pointcut pointcut 
= (Pointcut)pointcuts.get(i);
            delegate 
= Pointcuts.union(delegate,pointcut);
        }
    }

}
映射文件如下:
<?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="frequentCustomerAdvice" class="com.wyq.spring.common.aopinstance.namemachmethodpointcut.PrequentCustomerAdvice"></bean>
    
    
<bean id="queryInterceptor" class="com.wyq.spring.common.aopinstance.namemachmethodpointcut.QueryInterceptor"></bean>
    
    
<bean id="unionpointcut" class="com.wyq.spring.common.aopinstance.composablepointcut.UnionPointcut">
        
<property name="delegate">
            
<ref bean="frequentCustomerPointcutAdvisor"/>
        
</property>
        
<property name="pointcuts">
            
<list>
                
<ref bean="queryPointCutAdvisor"/>
            
</list>
        
</property>
    
</bean>
    
<bean id="frequentCustomerPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        
<property name="mappedName">
            
<value>order*</value>
        
</property>
        
<property name="advice">
            
<ref bean="frequentCustomerAdvice"/>
        
</property>
    
</bean>
    
    
<bean id="queryPointCutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        
<property name="pattern">
            
<value>.*get.+By.+</value>
        
</property>
        
<property name="advice">
            
<ref bean="queryInterceptor"/>
        
</property>
    
</bean>
</beans>

posted @ 2009-11-06 14:48 王永庆 阅读(156) | 评论 (0)编辑 收藏
    ThrowsAdvice让你定义在异常发生时该有什么动作。ThrowsAdvice是一个标示接口,它没有定义任何必须实现的方法。但

是,实现这个接口的类必须至少有一个如下形式的方法:
    void afterThrowing(Throwable throwable);
    void afterThrowing(Method method,Object[] args,Object target,Throwable throwable);
第一个方法只接受一个参数:需要抛出的异常。第二个方法接受异常、被调用的方法、参数以及目标对象。除非需要这些外加参数,你只要实现单参数方法就可以了。你的ThrowsAdvice要处理的异常取决于你的方法定义的异常类型。
    你也可以在一个类中实现多个afterThrowing方法。代码如下:

package com.wyq.spring.base.aopinstance;

import org.springframework.aop.ThrowsAdvice;

/** 
 * 
@author 作者 
 * 
@version 创建时间:2009-11-5 下午05:39:17 
 * 类说明 
 
*/
public class KwikEMartExceptionAdvice implements ThrowsAdvice {
    
/*
     * 根据抛出异常的类型恰当方法将被调用。注意,除了捕获和处理异常以外,这些方法都为应用添加了新的行为。
     * 这是因为你无法做到这一点。代理对象捕获异常并且调用合适的ThrowsAdvice方法,如果有的话。ThrowsAdvice
     * 被执行后,原来的异常继续被抛出,并且向其他异常一样被传播出去。
     
*/
    
public void afterThrowing(NoMoreSquisheesException e){
        orderMoreSquishees();
    }
    
public void afterThrowing(CustomerIsBrokeException e){
        showCustomerAtmMachine();
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    
<!-- 创建代理目标对象 -->
    
<bean id="kwikEMartTarget" class="com.springinaction.chapter03.store.ApuKwikEMart"></bean>
    
<!-- 创建通知 -->
    
<bean id="welcomeAdvice" class="com.springinaction.chapter03.store.WelcomeAdvice"></bean>
    
<!-- 创建代理对象 -->
    
<bean id="kwikEMart" class="org.springframework.aop.framework.ProxyFactoryBean">
        
<!-- 代理类实现的接口 -->
        
<property name="proxyInterfaces">
            
<value>com.springinaction.chaper03.store.kwikEMart</value>
        
</property>
        
<!-- 要织入的通知 -->
        
<property name="interceptorNames">
            
<list>
                
<value>welcomeAdvice</value>
            
</list>
        
</property>
        
<!-- 要代理的目标对象 -->
        
<property name="target">
            
<ref bean="kwikEMartTarget"/>
        
</property>
    
</bean>
</beans>


    引入通知:引入通知与前面的通知类型有点不同。其他类型的通知是在目标对象的方法被调用的周围织入。引入通知给目标对象添加新的方法。
    切入点决定了一个特定类的特定方法是否满足一条特定规则。如果一个方法确实符合,通知就应用到该方法上。Spring的切入点可以让我们以一种灵活的方式定义在什么地方将通知织入到我们的类中。
    Spring根据需要织入通知的类和方法来定义切入点。Spring的切入点框架的核心接口自然史pointcut.
    public interface Pointcut{
        ClassFilter getClassFilter();//这个接口决定了一个类是否复合通知的要求
    MethodMatcher getMethodMatcher();//方法过滤
    }
大多数切面是由定义切面行为的通知和定义切面在什么地方执行的切入点组合而成的。Spring认识到这一点,提供了Advisor,

它把通知和切入点组合到一个对象中。
   public interface PointcutAdvisor{
       Pointcut getPointcut();
    Advice getAdvice();
   }
大多数Spring自带的切入点都有一个对应的PointcutAdvisor.这样方便你在一个地方定义切入点和通知。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    
<!-- 创建代理目标对象 -->
    
<bean id="maidServiceTarget" class="com.springinaction.chapter03.cleaning.MaidServiceImpl"></bean>
    
<!-- 创建通知 -->
    
<bean id="frequentCustomerAdvice" class="com.springinaction.chapter03.cleaning.FrequentCustomerAdvicer"></bean>
    
<!-- 定义切入点 
         使用Namedmethodmatcher可以很清楚的指明哪些方法需要使用通知。然而,对于大型应用系统,把每个需要通知的方法都列出来
         会使配置文件显得非常冗长。使用通配符可以帮助我们解决这个问题。
         Spring的RegexpMethodPointcut让你利用正则表达式的力量来定义切入点。这样你可以使用Perl样式的正则表达式来定义模式,
         以得到你想要的方法。
         .匹配任何单个字符
         +匹配前一个字符一次或多次
         *匹配前一个字符0次或多次
         \匹配任何正则表达式符号
    
-->
    
<bean id="frequentCustomerPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        
<property name="mappedName">
            
<value>order*</value>
        
</property>
        
<property name="advice">
            
<ref bean="frequentCustomerAdvice"/>
        
</property>
    
</bean>
    
<!-- 如果你想匹配所有setXxx方法,我们需要使用.*set*.模板(第一个通配符匹配任何类名) -->
    
<bean id="queryPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        
<property name="pattern">
            
<value>.*get.+By.+</value>
        
</property>
        
<property name="advice">
            
<ref bean="frequentCustomerAdvice"/>
        
</property>
    
</bean>
    
<!-- 定义代理对象 -->
    
<bean id="maidService" class="org.springframework.aop.framework.ProxyFactoryBean">
        
<property name="proxyInterfaces">
            
<value>com.springinaction.chapter03.cleaning.MaidService</value>
        
</property>
        
<property name="interceptorNames">
            
<list>
                
<value>frequentCustomerAdvisor</value>
            
</list>
        
</property>
        
<property name="target">
            
<ref bean="maidServiceTarget"/>
        
</property>
    
</bean>
</beans>


posted @ 2009-11-06 12:29 王永庆 阅读(203) | 评论 (0)编辑 收藏
<2009年11月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

常用链接

留言簿(1)

随笔分类

随笔档案

关注blogs

搜索

  •  

最新评论

阅读排行榜

评论排行榜