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(1, 1);
//除了在这里设置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) |
编辑 收藏
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 王永庆 阅读(252) |
评论 (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 王永庆 阅读(271) |
评论 (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 王永庆 阅读(131) |
评论 (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 王永庆 阅读(150) |
评论 (0) |
编辑 收藏
摘要: Spring的一个目标就是让你遵循对接口编程的面向对象原则。DAO的存在提供了读写数据库中数据的一种方
法。只要把这个功能通过接口暴露,应用的其他部分就可以通过这些接口访问数据库了。
在Spring的DAO框架里,Connection对象是通过DataSource获得的。
从JN...
阅读全文
posted @
2009-11-12 19:46 王永庆 阅读(182) |
评论 (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 王永庆 阅读(1004) |
评论 (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 王永庆 阅读(146) |
评论 (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 王永庆 阅读(252) |
评论 (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 王永庆 阅读(160) |
评论 (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 王永庆 阅读(159) |
评论 (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 王永庆 阅读(208) |
评论 (0) |
编辑 收藏
时间真的可以抹杀一切,历史如此,人的感情也是如此,随着时间的流逝,一切都变得模糊起来。不知不觉中,对你的迷恋不在那么执着了,不知道是自己放弃了这份执着,还是你有意保持我们的距离。面对着电脑的屏幕我也在问我同样的问题,我为什么要放弃,我自己都无法回答。
时间一点点的过去,面对离开公司的日子越来越近,少不了要和兄弟姐妹们大吃几顿,自己都不知道腐败了多少次,但是这些次吃饭,都没有带上你,因为不知道怎么去面对你(是以朋友的身份还是同事的身份,还是我的另一半的身份去面对)。在你的面前我总是那么的拘谨,那么的紧张,致使连话都不会说,也许只有文字才能说出我的真心。虽然我不会说话,但是我的心是真诚的,虽然我不会体贴,但是我会学。不知道你心中的另一半要找什么样子的,我的外表虽然不够帅,但是我的心是炽热的。回忆你刚来公司的时候,那时公司也进入了繁忙的阶段,每天10点多才下班,你知道我心里在想什么么?我不想你一个人走在回家的路上,我不想你周末没人陪,不想你一个人去吃饭......,不知道喜欢一个人是什么感觉,还从来没对人这样过。你要求的那一半我可能达不到,你的那一半可能是很帅气,很高大,很会说话,很幽默,很浪漫......这些都不是我具备的,我能给的只有我的心,那些我真的给不了你,我不想成为那样一个人,因为我就是我。想一个人真的好累,我不想这么累,既然我们没有缘分,人们都说爱情是幸福快乐的,我怎么觉得爱一个人这么累,也许我还没有真正的去爱。
既然不能爱你,那就祝福你吧,希望你永远都幸福快乐,每天都像小时候一样无忧无虑,找一个真心爱护你的人。再见了,我的她的回忆。
想用自己的真心去关心一个人,想让心中的她快乐。
posted @
2009-11-05 23:21 王永庆 阅读(146) |
评论 (0) |
编辑 收藏
Spring的AOP框架允许你将分散在系统中的功能块放到一个地方-切面。重用通用功能的常用面向对象技术是使用继承和委
托模式。但由于基础类在系统中到处使用,使用继承会引起脆弱的继承关系。委托模式比较笨拙,依然需要重复调用委托对象
。使用AOP,你也是在一个地方定义通用功能,只是你可以声明式定义何时何地应用这些功能,而不一年欧冠在需要新功能的地
方修改代码。交叉业务现在可以被模块化到特定对象切面中。这样做有2个好处,第一,现在每个业务逻辑放在一个地方,而不
是分散到代码的各个角落。第二,我们的服务模块现在更加清晰,因为他们只包含他们的核心功能,辅助功能转移到切面中。
切面(Aspect):切面是你要实现的交叉功能。切面最常用的例子是日志记录。日志记录在系统中到处需要用到。
连接点(Joinpoint):连接点是应用程序执行过程中插入切面的地点。这个地点可以是方法调用,异常抛出,或者是要修改
字段。切面代码在这些地方插入到你的应用流程中,添加新的行为。
通知(Advice):通知切面的实际实现。它通知应用系统新的行为。
切入点(PointCut):切入点定义了通知应该应用在哪些连接点。通知可以应用到AOP框架支持的任何连接点。你并不希望把
所有切面应用到所有可能的连接点上。切入点让你指定通知应用到什么地方。
引入(Introduction):引入允许你为已存在类添加新方法和属性。
目标对象(Target):目标对象是被通知对象。它既可以是你编写的类也可以是你要添加定制行为的第三方类。
代理(Proxy):代理是将通知应用到目标对象后创建的对象。对于客户对象来说,目标对象和代理对象是一样的。
织入(Weaving):织入是将切面应用到目标对象从而创建一个新的代理对象的过程。
切入点定义了哪些连接点要被通知。
Spring的AOP实现:
在Spring中所有的通知都以Java类的形式编写。代理Bean只有在第一次被应用系统需要的时候才被创建。如果你使用的是ApplicationContext,代理对象在BeanFactory载入所有Bean的时候被创建。因为Spring在运行期创建代理,所以使用Spring AOP不需要特殊编译期。
Spring有2种代理创建方式。如果目标对象实现了一个接口暴露的方法,Spring将使用JDK的Java.lang.reflect.Proxy类创建代理。这个类让Spring动态产生一个新的类,它实现了所需的接口,织入了通知,并且代理对目标对象的所有请求。
如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个子类的时候,Spring将通知织入,并且对目标对象的调用委托给这个子类。
通知包含了切面的逻辑。所以当你创建一个通知对象的时候,你是在编写实现交叉功能的代码。而且,记住Spring的连接点模型是建立在方法拦截上。这意味着你编写的Spring通知会在方法调用周围的各个地方织入系统中。通知的类型有:Around,Before,After,Throws
前置通知:需要扩展MethodBeforeAdvice接口
public interface MethodBeforeAdvice{
void before(Method method,Object[] args,Object target)throws Throwable;
}
<?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>
ProxyFactoryBean类是一个在BeanFactory中显示的创建代理对象的中心类。像我们展示的那样,你可以给它一个要实现的接口,一个要代理的目标对象,一个要织入的通知,并且它将创建一个崭新的代理对象。通常配置ProxyFactoryBean,让它实现和目标对象一样的接口。
后置通知:需要实现AfterReturningAdvice
public interface AfterReturningAdvice{
void afterReturning(Object returnValue,Method method,Object[] args,Object target)throws Throwable;
}
环绕通知:需要实现MethodInterceptor,同时实现前置和后置通知
public interface MethodInterceptor extends Interceptor{
Object invoke(MethodInvocation invocation)throws Throwable;
}
MethodInterceptor接口和前面介绍的2种通知不同点:
1、MethodInterceptor能够控制目标方法是否真的被调用。通过调用MethodInvocation.proceed()方法来调用目标方法。这一点不同于前两个,目标方法总是被调用的。
2、MethodInterceptor让你可以控制返回的对象。就是说你可以返回一个与proceed()方法返回对象完全不同的对象。
package com.wyq.spring.base.aopinstance;
import java.util.HashSet;
import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jboss.remoting.samples.transporter.basic.Customer;
/**
* @author 作者
* @version 创建时间:2009-11-5 下午05:19:19
* 类说明
*/
public class OnePerCustomerInterceptor implements MethodInterceptor {
//定义包含用户的集合
private Set customers = new HashSet();
/*
* 当你在方法调用的前后都需要交叉切面逻辑时,应该使用MethodInterceptor。由于你必须要记得显示调用
* invocation.proceed()方法,所以,在满足要求的情况下,最好还是使用MethodBeforeAdvice或
* AfterReturningAdvice.
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
Customer customer = (Customer)invocation.getArguments()[0];
if(customers.contains(customer)){
System.out.println("抛出异常");
}
//调用目标方法
Object squishee = invocation.proceed();
//添加用户
customers.add(customer);
//返回目标方法结果
return squishee;
}
}
posted @
2009-11-05 17:31 王永庆 阅读(221) |
评论 (0) |
编辑 收藏
Hibernate数据检索:
Criteria Query:通过面向对象化的设计,将数据查询条件封装为一个对象。Criteria本身只是一个查询容器,具体的查询条件需要通过Criteria.add方法添加到Criteria实例中。Expression对象具体描述了查询条件。
示例查询并不常用,一方面它的使用比较繁琐,另外从可读性上来讲也不如Expression来的直观。但是在某些情况下却有其特别的用途。
示例查询最常用的场景是组合查询。我们常常需要在界面上提供若干查询选项,然后根据用户的选择返回复合条件的结果。实例查询在这里能发
挥其特长:
package com.wyq.demo.common.criteriaquery;
import java.util.Iterator;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
/**
* @author 作者
* @version 创建时间:2008-11-29 上午09:26:48
* 类说明
*/
public class TestCriteria {
/**
* @authorwyq
* @功能:
* @param args
*/
public static void main(String[] args) {
Session session = null;
//新建一个criteria查询容器
Criteria criteria = session.createCriteria(TUser.class);
/*(1)cireria查询
* 构造查询条件,Expression对象具体描述了查询条件
* 在Hibernate3中,引入了Restrictions类作为Expression的替代
* Expression.eq 等于
* Expression.allEq 参数为一个map包好了多个属性-值对应关系
* Expression.gt 大于
* Expression.ge 大于等于
* Expression.lt 小于
* Expression.le 小于等于
* Expression.between 表示某个字段位于2个数之间
* Expression.like 模糊查询
* Expression.in 范围查询
* Expression.eqProperty 用于比较2个属性之间的值"field=field"
* Expression.gtProperty 属性1>属性2
* Expression.geProperty 属性1>=属性2
* Expression.ltProperty 属性1<属性2
* Expression.leProperty 属性1<=属性2
* Expression.and and关系组合
* Expression.or or关系组合
* Expression.sql 通过这个方法直接通过SQL语句限定查询条件
*/
criteria.add(Expression.eq("name","Erica"));
criteria.add(Expression.eq("sex",new Integer(1)));
/*(2)示例查询
* Example类实现了Criterion接口,同样,它也可以用作Criteria的查询条件。Example
* 的作用是:根据已有对象,查找属性与之相符的其他对象
* 示例查询最常用的场景是组合查询。我们常常需要在界面上提供若干查询选项,然后
* 根据用户的选择返回符合条件的结果。
* Example example = Example.create(cat)
* excludeZeroes() //exclude zero valued properties
* excludeProperty("color") //exclude the property named "color"
* ignoreCase() //perform case insensitive string comparisons
* enableLike(); //use like for string comparisons
* List results = session.createCriteria(Cat.class)
* add(example)
* list();
*/
TUser exampleUser = new TUser();
exampleUser.setName("Erica");
criteria.add(Example.create(exampleUser));
/*
* (3)复合查询
* 在原有查询的基础上,针对TUser对象的addresses属性构造了新的查询过滤条件
*/
Criteria addCriteria = criteria.createCriteria("addresses");
addCriteria.add(Expression.like("address", "%Shanghai%"));
/*
* (4)DetachedCriteria
* 使Criteria脱离session实例独立存在,这样,我们就可以将某些通用的Criteria
* 查询条件进行抽离,每次使用时再与当前Session实例绑定以获得更好的代码重用效果
*/
DetachedCriteria deCriteria = DetachedCriteria.forClass(TUser.class);
deCriteria.add(Expression.eq("name", "Erica"));
deCriteria.add(Expression.eq("sex",new Integer(1)));
Criteria creiterias = deCriteria.getExecutableCriteria(session);
Iterator it = criteria.list().iterator();
/*
* (5)高级特性
* 通过criteria.setFirstResult/setMaxResults方法可以限制一次查询返回的记录范围:
*/
creiterias.setFirstResult(100);
creiterias.setMaxResults(20);
/*
* 排序
*/
creiterias.addOrder(Order.asc("name"));
/*
* 分组与统计
* 分组、统计表达式由Hibernate3新引入的Projections Class进行封装
* 按照age分组查询
* Projections.groupProperty()方法实际上是对SQL group by子句的封装。同
* 样,我们可以通过Projections.avg(),rowCount(),count(),max(),min(),countDistinct()
* 等方法实现查询统计功能
*/
creiterias.setProjection(Projections.groupProperty("age"));
}
}
posted @
2009-11-05 11:23 王永庆 阅读(318) |
评论 (0) |
编辑 收藏
由于多对多的性能不佳(由于引入了中间表,一次读取操作需要反复数次查询),因才在设计中应该避免大量使用。同时,在多对多关系中,应根据情况,采取延迟加载机制来避免无谓的性能开销。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.redsage.hibernate.db.entity.TGroup" table="t_group" dynamic-insert="false" dynamic-update="false">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<set name="roles" table="t_gourp_role" lazy="false" inverse="false" cascade="save-update">
<key column="group_id"></key>
<many-to-many class="com.redsage.hibernate.db.entity.TRole" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
1、t_gourp_role为t_group和t_role之间的映射表,它保存了group和role之间的映射关系。
2、一般情况下,cascade应该设置为"save-update",对于多对多逻辑而言,很少出现删除一方需要级联删除所有关联数据的情况,如删除一个Group,一般不会删除其包含的Role,反之删除Role一般也不会删除其所关联的所有Group.
3、映射表中对于t_group表记录的标示字段。
4、映射表中对于t_role表记录的标示字段。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.redsage.hibernate.db.entity.TRole" table="t_role" dynamic-insert="false" dynamic-update="false">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<set name="groups" table="t_gourp_role" lazy="false" inverse="true" cascade="save-update">
<key column="role_id"></key>
<many-to-many class="com.redsage.hibernate.db.entity.TGroup" column="group_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
posted @
2009-11-04 17:11 王永庆 阅读(140) |
评论 (0) |
编辑 收藏
单向一对多关联:
主控方映射配置如下:
被动方(TAddress)的记录由Hibernate负责读取,之后存放在主控方(TUser)指定的Collection类型属性中。对于one-to-many
关联关系,我们可以采用java.util.Set类型的Collection,表现在XML映射文件中就是<set>节点。单向一对多存在一个问题
,由于是单向关联,为了保持关联关系,我们只能通过主控方对被动方进行级联更新。如果被关联方的关联字段为"NOT
NULL",当Hibernate创建或者更新关联关系时,可能出现约束违例。
由于关联方向是单向的,关联关系由TUser对象维持,而被关联的addr对象本身并不知道自己与哪个TUser对象相关联,也就
是说,addr对象本身并不知道自己的user_id应该设为什么数值。在使用one-to-many进行单向关联时,由于Hibernate实现机
制中,采用了2条SQL语句进行一次数据插入操作,相对单条insert操作,几乎是2倍的性能开销,效率较低,因此,对于性能
敏感的系统而言,这样的解决方案所带来的开销可能难以承受。
如果addr对象知道如何获取user_id字段的内容,那么执行insert语句的时候直接将数据植入即可。这样不但绕开了约束违例
的可能,而且还节省了一条update语句的开销,大幅度提高了性能。双向一对多关系的出现解决了这个问题。它除了避免约
束和提高性能的好处之外,还带来另外一个优点,由于建立了双向关联,我们可以在关联双方中任意一方,访问关联的另一
方,这提供了更丰富灵活的控制手段。
双向一对多关联,实际上是"一对多"与"多对一"关联的组合。也就是说我们必须在主控方配置单向一对多关系的基础上,在
被控方配置与其对应的多对一关系。
上代码:
package com.wyq.demo.common.reference.onetomany;
import java.io.Serializable;
import java.util.Set;
/**
* @author 作者
* @version 创建时间:2008-11-28 上午10:10:23
* 类说明 在one-to-many关系中,将many一方设为主控方(inserse=false)将有助于性能的改善。
*/
public class TUser implements Serializable {
private Integer id;
private Integer age;
private String name;
private Set addresses;
public Set getAddresses() {
return addresses;
}
public void setAddresses(Set addresses) {
this.addresses = addresses;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对应的映射文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.wyq.demo.common.reference.onetomany.TUser" table="t_user" dynamic-insert="true" dynamic-update="true" catalog="sample">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<property name="age" type="java.lang.Integer">
<column name="age"></column>
</property>
<!-- 控制方向反转,Tuser不再作为主控方,而是将关联关系的维护工作
交给关联对象Taddress来完成,这样Taddress对象在持久化过程中
,就可以主动获取其关联的TUser对象的id,并将其作为自己的user_id,
之后执行一次insert操作就可以完成全部工作。
-->
<set name="addresses" table="t_address" inverse="true" cascade="all" order-by="zipcode asc">
<key column="user_id"></key>
<one-to-many class="com.wyq.demo.common.reference.onetomany.TAddress"/>
</set>
</class>
</hibernate-mapping>
关联对象:
package com.wyq.demo.common.reference.onetomany;
import java.io.Serializable;
/**
* @author 作者
* @version 创建时间:2008-11-28 上午10:11:01
* 类说明 双向一对多关系除了避免约束违例和提高性能的好处之外,还带来了另外一个优点
* 由于建立双向关联,我们可以在关联双方中任意一方,访问关联的另一方。
*/
public class TAddress implements Serializable {
private Integer id;
private String address;
private String zipcode;
private String tel;
private String type;
private Integer userId;
private TUser user;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public TUser getUser() {
return user;
}
public void setUser(TUser user) {
this.user = user;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
}
关联对象的映射文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.wyq.demo.common.reference.onetomany.TAddress" table="t_address" dynamic-insert="false" dynamic-update="false" catalog="sample">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="address" type="java.lang.String" column="address"></property>
<property name="zipcode" type="java.lang.String" column="zipcode"></property>
<property name="tel" type="java.lang.String" column="tel"></property>
<property name="type" type="java.lang.String" column="type"></property>
<many-to-one name="user" class="com.wyq.demo.common.reference.onetomany.TUser" cascade="none" outer-join="auto" column="user_id" not-null="true"></many-to-one>
</class>
</hibernate-mapping>
Inverse指的是关联关系的控制方向,而cascade指的是层级之间的连锁操作。在one-to-many关系中,将many一方设为主控方(inverse=false)将有助性能的改善。
posted @
2009-11-04 16:06 王永庆 阅读(161) |
评论 (0) |
编辑 收藏
对于任何关联操作,首先操作的是主体,然后查看主体的映射文件是否关联其他对象,如果关联,查看关联方式,并进行操作。
name: 属性名。
column (可选): 表字段名。
class : 关联的类的名字。
cascade(级联) (可选): 指明哪些操作会从父对象级联到关联的对象。
property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。 如果没有指定,会使用对方关联类的主键。
unique (可选): 使用DDL为外键字段生成一个唯一约束。此外, 这也可以用作property-ref的目标属性。这使关联同时具有 一对一的效果。
not-null (可选): 使用DDL为外键字段生成一个非空约束。
Hibernate唯一外键关联的一对一关系只是多对以关系的一个特例:
package com.wyq.demo.common.reference.onetoone.primekey;
import java.io.Serializable;
/**
* @author 作者
* @version 创建时间:2008-11-28 上午09:39:56
* 类说明 Hibernate中的唯一外键关联由"many-to-one"节点定义
* 使用外键来完成一对一,其实就是限制多对一关系中,多的一方只能有一个参考至一的一方,也就是多对一关系
* 的一个特例,这可以在映射文件中使用<many-to-one>标签时,加上"unique"属性来设定外键的唯一性。
*/
public class TUser implements Serializable {
private Integer id;
private Integer age;
private String name;
private TGroup group;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public TGroup getGroup() {
return group;
}
public void setGroup(TGroup group) {
this.group = group;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对应的映射文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.wyq.demo.common.reference.onetoone.primekey.TUser" table="t_user" catalog="sample">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<property name="age" type="java.lang.Integer">
<column name="age"></column>
</property>
<many-to-one name="group" class="com.wyq.demo.common.reference.onetoone.primekey.TGroup" column="group_id" unique="true"></many-to-one>
</class>
</hibernate-mapping>
一端的关联类:
package com.wyq.demo.common.reference.onetoone.primekey;
import java.io.Serializable;
/**
* @author 作者
* @version 创建时间:2008-11-28 上午09:40:21
* 类说明 如果要实现双向的一对一关系,则需要对TGroup进行修改,为其增加一个TUser类
* 完成双向一对一,要在<one-to-one>中,property-ref告诉hibernate,查询出user并将其参考至group
*/
public class TGroup implements Serializable {
private Integer id;
private String name;
private TUser user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TUser getUser() {
return user;
}
public void setUser(TUser user) {
this.user = user;
}
}
对应的映射文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.wyq.demo.common.reference.onetoone.primekey.TGroup" table="t_group" catalog="sample">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<one-to-one name="user" class="com.wyq.demo.common.reference.onetoone.primekey.TUser" property-ref="group"></one-to-one>
</class>
</hibernate-mapping>
posted @
2009-11-04 11:05 王永庆 阅读(215) |
评论 (0) |
编辑 收藏
数据关联:
一对一关联包括2种类型:1、主键关联2、唯一外键关联
一对一的主键关联形式,即2张关联表通过主键形成一对一映射关系。
<one-to-one>节点定义了类与类之间的关系。
package com.wyq.demo.common.reference.onetoone.primekey;
import java.io.Serializable;
/**
* @author 作者
* @version 创建时间:2008-11-28 上午09:39:56
* 类说明 Hibernate中的唯一外键关联由"many-to-one"节点定义
* 使用外键来完成一对一,骑士就是限制多对一关系中,多的一方只能有一个参考至一的一方,也就是多对以关系
* 的一个特例,这可以在映射文件中使用<many-to-one>标签时,加上"unique"属性来设定
*/
public class TUser implements Serializable {
private Integer id;
private Integer age;
private String name;
private TGroup group;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public TGroup getGroup() {
return group;
}
public void setGroup(TGroup group) {
this.group = group;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对应的映射文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.wyq.demo.common.reference.onetoone.TUser" table="t_user" catalog="sample">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<property name="age" type="java.lang.Integer">
<column name="age"></column>
</property>
<one-to-one name="passport" class="com.wyq.demo.common.reference.onetoone.TPassport" cascade="all" outer-join="true"></one-to-one>
</class>
</hibernate-mapping>
级联(cscade)在Hibernate映射关系中是个非常重要的概念。它指的是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作。如对主控对象调用save-update或delete方法时,是否同时对关联对象(被动方)进行save-update或delete
package com.wyq.demo.common.reference.onetoone.foreignkey;
import java.io.Serializable;
/**
* @author 作者
* @version 创建时间:2008-11-28 上午09:14:10
* 类说明 由于采用了主键关联方式,那么通过主键关联的2张表,其关联记录的主键值须保持
* 同步。这也就是说,我们只须为一张表设定主键生成器,而另一张表的主键与之共享
* 相同的主键值。我们可以通过"foreign"类型的主键生成器与外键共享主键值。
* 同时,one-to-one节点的constrained属性必须设定为"true",以告知hibernate
* 当前表主键上存在一个约束。
* 在TPassport的id主键上,使用foreign标示与外键共享主键,也就是与User实体共享主键,而
* constrained设定为true,表示约束TPassport的主键必须与user中对应资料的主键相同
*/
public class TPassport implements Serializable {
private Integer id;
private String serial;
private Integer expiry;
private TUser user;
public Integer getExpiry() {
return expiry;
}
public void setExpiry(Integer expiry) {
this.expiry = expiry;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSerial() {
return serial;
}
public void setSerial(String serial) {
this.serial = serial;
}
public TUser getUser() {
return user;
}
public void setUser(TUser user) {
this.user = user;
}
}
对应的映射文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.wyq.demo.common.reference.onetoone.TPassport" table="t_passport" catalog="sample">
<id name="id" column="id">
<!-- 定义根据user表的主键生成 -->
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<!-- 定义约束constrained -->
<one-to-one name="user" class="com.wyq.demo.common.reference.onetoone.TUser" constrained="true"></one-to-one>
<property name="serial" type="java.lang.String" column="serial"></property>
<property name="expiry" type="java.lang.Integer" column="expiry"></property>
</class>
</hibernate-mapping>
由于采用了主键关联方式,那么通过主键关联的2张表,其关联记录的主键值须保持同步。这也就意味着,我们只需要为一张表设定主键生成器,而另一张表的主键与之共享相同的主键值。
在Hibernate中,我们可以通过"foreign"类型的主键生成器与外键共享主键值。同时,one-to-one节点的constrained属性设定为"true",以告知Hibernate当前表主键上存在一个约束:"T_Passport"表引用了T_User表的主键。
posted @
2009-11-04 10:01 王永庆 阅读(176) |
评论 (0) |
编辑 收藏
解析文本信息:
有时,你不希望硬编码显示给用户信息。也许是因为这个信息经常发生改变,或者是你的应用系统提供国际化功能,你要用用户的本地语言显示文本。
java对文本国际化的支持使你能够定义一个或多个属性文件保存应用系统中需要显示的文本。Spring的ApplicationContext通过MessageSource接口为容器提供参数化信息支持,Spring提供了一个现成的MessageSource实现。ResourceBundleMessageSource只是调用java自己的java.util.ResourceBundle来解析信息。
例如:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>trainingtext</value>
</property>
</bean>
这个Bean的名字必须是messageSource,因为ApplicationContext在装配应用系统Bean的时候查找这个名字的Bean,你不需要将messageSource注入到应用系统中的Bean中,而是使用ApplicationContext自己的getMessage()方法。
Locale locale = ...;
第一个参数表示文本文件中的ID
第二个参数表示传递到资源文件中的数组,显示最终文件
第三个参数表示采用哪种方式显示
String text = context.getMessage("computer",new Object[0],locale);
监听事件与发布事件:
如果你想对一个类的方法进行监听,首先要定义事件,然后在这个方法中通过ApplicationContext发布它,最后在ApplicationContext.xml中定义这个监听器。这样,每当方法执行的时候,监听器就会监听到对应的事件的触发。
事件分为ApplicationContext发布的事件和自定义的事件。这些事件都是抽象类org.springframework.context.ApplicationEvent的子类。
在应用系统生命周期中,ApplicationContext会发布很多事件,告诉感兴趣的监听器发生了什么事情。。系统事件有如下几个:
1、ContextClosedEvent:在应用上下文关闭的时候发布的事件;
2、contextRefreshedEvent:在应用上下文初始化或刷新的时候发布的事件;
3、RequestHandledEvent:在web应用上下文中,当一个请求被处理后发布的事件。
首先要编写为哪个类的哪个方法添加事件:
public class Animal implements ApplicationContextAware {
private ApplicationContext ac;
private String name;
private int age;
public String speak(){
ac.publishEvent(new AnimalSpeakEvent(this,this.name));
return " 我的名字是;"+this.name+",我的年龄是:"+this.age;
}
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
this.ac = arg0;
}
//Getet和Seter省略
}
自定义事件入下:
import org.springframework.context.ApplicationEvent;
public class AnimalSpeakEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private String animalName;
public AnimalSpeakEvent(Object source) {
super(source);
}
public AnimalSpeakEvent(Object source,String animalName) {
super(source);
this.animalName = animalName;
}
public String getAnimalName() {
return animalName;
}
}
然后是实现监听器,监听器必须实现org.springframework.context.ApplicationListener接口。这个接口要求你的Bean实现onApplicationEvent()方法:
public class RefreshListener implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event){
}
}
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class AnimalEventListener implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof AnimalSpeakEvent) {
AnimalSpeakEvent a = (AnimalSpeakEvent) event;
System.out.println("事件监听器" + this.getClass().getSimpleName()+":有一个动物在讲话!它的名字是:"+ a.getAnimalName());
}
}
}
最后就是在映射文件中定义这个监听器:
<?xml version="1.0" encoding="UTF-8"?>
<beans …………>
<bean id="Listener" class="ioc.test.AnimalEventListener" />
<bean id="Animal" class="ioc.test.Animal">
<property name="name" value="老虎" />
<property name="age" value="5" />
</bean>
</beans>
最后是测试类:
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
public static void main(String[] args) {
AbstractApplicationContext ac = new ClassPathXmlApplicationContext(
"applicationContext.xml");
//从容器获取动物实例
Animal animal = (Animal)ac.getBean("Animal");
//让动物讲话
System.out.println(animal.speak());
}
}
感知其他Bean:
在极大程度上,运行在Spring容器中的Bean就像生活在The Matrix里的人类。对于这些Bean来说,他们不知道自己的注册名,甚至不知道自己运行在容器中。通常这是好事,因为如果一个Bean知道容器的存在的话,他就和Spring耦合在一起了,在容器以外无法存在。
但有时候Bean需要知道更多信息。有时他们需要知道他们是谁,他们在哪里运行。有时他们需要服用那颗红色药丸。
在Spring Bean环境中,红色药丸就是BeanNameAware、BeanFactoryAware和ApplicationContextAware接口。通过实现这3个接口,Bean分别可以知道自己的名字,他们所处的BeanFactory以及他们所处的ApplicationContext.
注意,通过实现这些接口,一个Bean就和Spring耦合在一起。
感知系统容器对于Bean来说是福是祸。一方面,应用上下文的获得给Bean提供了很多权利。另一方面,知道容器会把Bean和Spring耦合起来,这是要尽量避免的事情。
posted @
2009-11-03 20:52 王永庆 阅读(164) |
评论 (0) |
编辑 收藏
时间到了,继续我心中的那个她,每天晚上坚持写博客,感觉真的很好,我想把自己所想的写出来。这两天出奇的冷,北京的天气真郁闷,是不是后天要来了,这种温度让我的大脑都结冰了,都语无伦次了,虽然很冷,但是,还是要用我那冰冷的手指敲打键盘。
昨天说到美女生气了,她的沉默使我也很沉默,我也不是怎么会哄女孩的人,天生的呆板,属于榆木脑袋那种,只能等她心情好的时候跟她多说几句话。还好没过几天,她的心情多云转晴,我也就自然的晴天了。过了段时间,作为我对她细心培训的回报,她要请我吃饭。这顿饭可把我害苦了,就是从这顿饭后,每天脑子里都是她的身影(有点过了),但是每当想到她的时候,脑袋就乱,而且很不舒服。吃饭那天自己都不知道说了些什么,本来有很多话要说的,但是不知为什么,没有说出口。回家的时候她把我送到了车站,在回家的车上,脑子一片空白。还好自己知道在哪里下车,出站的时候突然感觉自己好像丢了什么,心里空荡荡的,原来把自己的心丢了,以前在学校的时候,知道没有思想的痛苦,跟她吃了顿饭,知道了没有心的痛苦,没有心的感觉真好,不管身心如何痛苦,时间不会因为你而停止,生活还在继续。以后的日子里,就不用说了,每天都在快乐并痛苦着,每天白天能有她相伴在身边是快乐的,下班回家之后想她是痛苦的。
时间一点点的流逝,转眼快到10.1了,大家都在讨论放假回家什么的,说到这里有点对不起自己的父母了,我离家里比较近,动车也就3个小时的路程,但是每年只有过年的时候才回家一次,感觉跟家里人相聚的时间太短了,愧对自己的父母,没办法啊,我们这种在外面漂流的人...说她吧,那次听到她要回家买票难什么的,我也没打算帮她买,我也没有那么大的本事,事情凑巧,以前的一个朋友打电话聊天,我想到他是做导游的应该很方便弄票,于是就跟兄弟说了一声,兄弟是答应了,但是我当她说的时候,她却说不用了(因为不知道做火车还是汽车),这是明显的搪塞,那天我生气了,生气的不是因为她搪塞,是因为她那不冷不热、莫不关心的态度,气的我和她保持沉默了,但是工作上的关系,又不得不说话。还好我的天气也不是那种南方的阴雨连绵,过几天就好了,这样我们还保持着普通朋友的关系。
明天就结束我们2个月的关系了,自己都没想到,想一个人会是这个样子,可能她不适合我,我也不适合她,朋友们都说,自己都做程序员了,还找个程序员做老婆。不合适,我也这么觉得。希望我们能成为好朋友,如果你给我当朋友的话,我也会视你为我的朋友。你只能活在我的记忆中了,不是我的心底。因为你没有走进我。
时间真快,又一个小时了,明天早起去吃早餐,^_^,天气凉了,胃口上来了。
Good night 我的那个她。
posted @
2009-11-03 00:15 王永庆 阅读(249) |
评论 (3) |
编辑 收藏
会话Bean同其他企业Bean类型最主要的区别是生命周期的差异性。会话Bean实例是存活短暂的对象,会话Bean实例并不能够在多客户间共享。
通常,客户会话的持续期决定了使用中的会话Bean的存活期,一旦应用服务器瘫痪,会话Bean实例也应该不复存在。因为,会话Bean仅仅是内存对象,一旦其生存的周边环境遭到破坏,会话Bean也将不复存在。会话Bean并不是持久化的,因此,会话Bean并不会保存到持久化存储源中,这同实体Bean不一样,会话Bean能够操作RDBMS,但是其本身并不是持久化对象。
会话指客户同EJB组件的交互,它由客户和EJB组件间的多次方法调用构成。会话Bean存在2种子类型:有状态会话Bean和无状态会话Bean,各自用于建模不同类型的会话。有状态会话Bean是这样一种EJB,即其服务的业务过程能够延伸到多个方法请求或者事务中,为完成这种业务过程,有状态会话Bean需要为单个客户保存状态信息。如果在方法调用期间有状态会话Bean的状态发生改变,则这种改变必须反映到同一客户的随后调用中。无状态会话Bean是这样一种EJB,即其服务的业务过程只需要单个业务方法即可完成。由于他们不需维护客户多个方法调用间的会话状态,因此它是无状态的。在每次方法调用结束后,EJB容器可能会销毁无状态会话Bean实例,或者实例化新的实例,或者清楚掉上次方法调用中的相关信息。
无状态意指不存在会话状态。无状态会话Bean能够含有同特定客户不相关的状态信息,比如所有客户将使用到数据库链接工厂,开发者可以将它存储在private变量中。如果开发者将数据存储在private变量中,则将随时丢失其中存储的数据。
EJB容器将维护EJB实例池,而且这些EJB实例是可重用的。在每次方法调用时,都会有不同EJB实例或同一实例服务客户。为了限制内存中运行的有状态会话Bean实例的数量,EJB容器需要将有状态会话Bean的会话状态保存到硬盘或者其他存储源中。该过程称之为挂起。在挂起有状态会话Bean后,会话状态被安全的保存下来,而且其释放的内存可以供其他应用使用。一旦被挂起的有状态会话Bean实例的客户再次调用它,被挂起的会话状态将重新回到有状态会话Bean实例中,该过程称之为激活。
有状态会话Bean实例的会话状态必须遵循Java对象序列化设定的规则。在挂起有状态会话Bean实例时,EJB容器借助于对象序列化将会话状态转换成二进制blob,然后将它写入到硬盘中。在转移会话状态信息后,有状态会话Bean实例(指刮起了会话状态的那些EJB实例)还能够服务于其他客户,即同新的客户进行新的会话过程。
一旦EJB中的成员变量符合如下条件,则可以认为它是会话状态的组成部分之一。
1、成员变量是非transient类型的java原始类型。
2、成员变量是非transient类型的Java对象类型。
当容器将EJB实例挂起时,它需要将实例的会话状态写入到二级存储源中,比如文件或者RDBMS中。通过调用EJB实例的ejbPassivate()回调方法,容器能够完成实例的挂起工作。借助于ejbPassivate()方法能够告知EJB实例:EJB容器需要挂起它,这使得释放其持有的资源成为可能。比如EJB实例可能持有的资源有:RDBMS连接、已打开的Socket和文件或者其他任何资源。
在实际场合中,客户调用了EJB对象中的某个方法,而当时在内存中暂时找不到该EJB对象,与此同时,EJB容器持有的企业Bean实例的个数已经到达了设定的上限。因此在处理客户请求前,容器需要挂起最近未使用的EJB实例,在挂起它后,容器才能够获得所需的EJB对象。
有状态会话Bean的部署描述符。
<?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">
<enterprise-beans>
<session>
<ejb-name>Count</ejb-name>
<home>examples.CountHome</home>
<remote>examples.Count</remote>
<ejb-class>examples.CountBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
服务端企业Bean:
package com.wyq.ejb02;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示有状态Bean
* 它会初始化val,并提供业务方法。
* 该实例演示了最简单的有状态会话Bean,并给出了挂起、激活的工作机理。
*/
public class CountBean implements SessionBean {
//会话状态
public int val;
//业务方法
public int count(){
System.out.println("count");
return ++val;
}
public void ejbCreate(int val)throws CreateException{
this.val = val;
System.out.println("ejbCreate()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
}
}
客户端调用:
package com.wyq.ejb02;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import com.wyq.ejb01.HelloHome;
/**
* 1、获得JNDI InitialContext上下文。
* 2、借助于JNDI,定义Home对象。
* 3、使用Home对象创建3个不同的CountEjb对象。因此,这将建立起3个不同的会话过程,而且模拟了3个不同的客户。
* 4、由于内存中仅能存活2个EJB实例,因此在创建Count EJB实例期间,EJB容器需要完成实例的挂起操作。
* 5、调用各个EJB对象的count()方法。
* 6、最后,删除所有的EJB对象。
*/
public class CountClient {
/**
* 客户代码示例
*
* 此时,创建了3个EJB对象。但我们规定容器:在内存中最多存在2个实例。
* 因为,能够看到挂起操作的发生。
*/
public static void main(String[] args) {
try{
/*
* 获得JNDI环境属性
*/
Properties props = System.getProperties();
/*
* 获得对Home对象的引用,Home对象是EJB对象的工厂
*/
Context ctx = new InitialContext(props);
Object obj = ctx.lookup("CountHome");
CountHome home =(CountHome)javax.rmi.PortableRemoteObject.narrow(obj,CountHome.class);
/*
* 能够持有3个Count对象的数组
*/
Count count[] = new Count[3];
int countVal = 0;
/*
* 创建EJB实例,并调用各自的count()
*/
System.out.println("Instantiating beans");
for(int i=0;i<3;i++){
/*
* 创建EJB对象,并初始化它们
*/
count[i] = home.create(countVal);
/*
* 加1,并打印出来
*/
countVal = count[i].count();
System.out.print(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 调用各个EJB对象的count()方法,从而能够浏览到EJB被挂起,并被成功激活
*/
System.out.println("Calling count() on beans");
for(int i=0;i<3;i++){
/*
* 加1,并打印出来
*/
countVal = count[i].count();
System.out.println(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 使用完后,销毁它们
*/
for(int i=0;i<3;i++){
count[i].remove();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
会话Bean的声明周期流程图
1、起初,EJB实例并不存在。
2、EJB容器将决定是否需要实例化新的EJB实例。容器将何时实例化新的EJB实例,取决于容器使用的EJB实例池策略。
3、容器实例化EJB Bean类。EJB容器将调用Class.newInsatance("HelloBean.class");即动态创建HelloBean实例,这使得容器不会将EJBBean类的名字硬编码在Java代码中。最后,这使得容器更具通用性,能够操控任何企业Bean.
4、容器调用setSessionContext()方法。这为EJB实例设置了上下文对象。最终,EJB实例将能够访问到EJB容器。
5、容器调用ejbCreate().这将初始化EJB实例。由于无状态会话Bean的ejbCreate()方法并不存在参数,因此EJB客户不可能为它提供任何启动EJB实例的参数信息。
6、EJB容器调用EJB实例的业务方法。对于EJB实例提供的所有业务方法,EJB容器都可以使用。由于所有EJB实例间不存在区别,因此完全不同的客户可以调用相同的业务方法。在业务方法调用结束后,各个无状态会话Bean实例依然是相同的,因此,EJB容器能够针对客户请求,在每个方法级将各个EJB实例指定给客户,即使同一客户对同一业务方法的多次调用,都可以由不同的EJB实例响应它,当然,将EJB实例指定给客户的具体实现策略取决于具体的EJB容器。
7、最后,容器调用ejbRemove()方法。
posted @
2009-11-02 16:35 王永庆 阅读(183) |
评论 (0) |
编辑 收藏
刚洗了个澡,看了下时间,还不算太晚,23点刚过一点,突然想写博客了,有点神经质了,o(∩_∩)o...
眼看就要辞职了,离开现在的公司了,马上就要离开旁边的那个她了,不知道离开之后,她还能不能想起旁边的我,我们虽然不是一类人,我这么感觉,不知道离开之后,她的工作还顺利不。我还在公司的时候,每当她遇到问题,还有耐心的我帮她解决,遇到问题的时候应该让她自己去解决,感觉她还是比较独立的,可能解决问题费劲一点。她不适合做程序员,现在太多人不适合做程序员了,现在的程序员已经不是程序员了。
认识她是在2个月前,那时我已经打算辞职了,但是还没有提出来。这时的工作到了关键的时候,面临着上线的压力,公司还在不断的招人,这时她进入了我们组,第一眼看到她的时候,没什么感觉,咋一看去,很普通的一个女孩,算不上特别漂亮,但是也有几分姿色,万没有想到就是这么一个有几分姿色的女孩对我有这么大的杀伤力。不知道经理怎么分的,让我去带她,可能经理早就知道我要走。没有过几天,公司的座位开始调整,因为现在组员太分散了,比较难沟通,所以又重新排列了座位,不知道怎么回事,把我分到了她的旁边,也可能比较容易培训吧。教导她的时候可谓是尽心尽力了,虽然我人比较好,对任何人的请求来之不惧,但是不知不觉中我感觉对她的付出超出了我的义务。开始的时候,感觉她还真把我当了个哥哥,旁边的几个好兄弟也总是拿我们开玩笑,总是管她叫嫂子,他们叫嫂子的时候,我的心里可紧张,紧张她生气,紧张她的心情,紧张她...,不过还好,她的性格很傻很天真的那种,一个耳朵进,一个耳朵出,今天生气,明天就好的那种,不知道哪一天她真的生气了,好严肃,:-),一脸的阴霾,呵呵,女人生起气来也很可怕,她用她自己独特的方式回击了我们,沉默。沉默得让我的心不能平静,不知道从什么时候起,我的心中有了她的一席之地,一颗被栓着的心真的很痛苦,之后的几天里,她的脸色依旧严肃着,感觉和她的距离拉远了,之后和她说话也都是工作上的多。
0:00,今天先写到这里吧,明天白天还要继续技术博客呢,晚上继续我的那个她,1个小时才写了这么点,因为认真的回忆了,能让我回忆的东西真的不多。现在的心中还有这个结,这个结打的好死,费了我好多脑细胞差不多打开了。
脱衣服,准备睡觉,晚安,我心中的那个她。
posted @
2009-11-02 00:03 王永庆 阅读(317) |
评论 (2) |
编辑 收藏
分散配置:
有时你会发现将配置文件分成几个分散的配置文件是很有益的。
将Data Source配置到Bean装配文件中不是很适合。数据库细节是一个发布信息。而Bean装配文件的目的是定义如何装配系统的各个模块。如果使用ApplicationContext当作Spring容器,那么,在Spring中分离属性配置是很简单的。使用Spring的PropertyPlaceholderConfigurer告诉Spring从外部属性文件装载一些配置信息。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>jdbc:hsqldb:Training</value>
</property>
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="username">
<value>appUser</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
location属性告诉Spring从哪里找到属性文件。location属性允许你使用单个配置文件。如果你想将配置信息分散到多个配置文件中,请使
用PropertyPlaceholderConfigurer的locations属性设置文件列表,使用这种方式,可以使用占位符变量代替Bean装
配文件中的硬编码配置了。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
<property name="locations">
<list>
<value>jdbc.properties</value>
<value>security.properties</value>
<value>application.properties</value>
</list>
</property>
</bean>
<bean id="dataSources" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>${database.url}</value>
</property>
<property name="driverClassName">
<value>${database.driver}</value>
</property>
<property name="username">
<value>${database.user}</value>
</property>
<property name="password">
<value>${database.password}</value>
</property>
</bean>
定制属性编辑器:
java.beans.PropertyEditor接口提供了将字符串值映射成非String类型的方法。有一个好用的这个接口的实现-
java.beans.PropertyEditorSupport,它有2个方法我们会感兴趣:
1、getAsText():方法返回一个表示属性值的字符串。
2、setAsText(String value):将传递进来的字符串赋给Bean的属性。
Spring带了几种建立在propertyEditorSupport之上的定制编辑器,包括
org.springframework.beans.propertyeditors.URLEditor,它是一个用于将字符串与java.net.URL相互转换的定制编辑器。
Spring提供的其他定制编辑器包括:
1、ClassEditor-使用包含全称类名的字符串设置java.lang.Class属性。
2、CustormDateEditor-使用某种java.text.DateFormat对象将一个字符串设置给java.util.Date属性。
3、FileEditor-使用包含文件路径的字符串设置java.io.File属性。
4、LocalEditor-使用包含地域信息的字符串设置java.util.Local属性。
5、StringArrayPropertyEditor-将一个包含逗号的String转换成String数组属性。
6、StringTrimmerEditor-自动修正字符串属性,可以选择将空字符串转变为null.
<bean id="costomEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.wyq.spring.PhoneNumber">
<bean id="phoneEditor" class="com.wyq.spring.PhoneEditor"></bean>
</entry>
</map>
</property>
</bean>
其中的map中的key表示要添加自定义属性的类,value表示自定义属性实现的类。
package com.wyq.spring;
import java.beans.PropertyEditorSupport;
public class PhoneEditor extends PropertyEditorSupport {
public void setAsText(String textValue) throws IllegalArgumentException {
String stripped = stripNonNumberic(textValue);
String areaCode = stripped.substring(0,3);
String prefix = stripped.substring(3,6);
String number = stripped.substring(6);
PhoneNumber phone = new PhoneNumber(areaCode,prefix,number);
setValue(phone);
}
private String stripNonNumberic(String original){
StringBuffer allNumberic = new StringBuffer();
for(int i=0;i<original.length();i++){
char c = original.charAt(i);
if(Character.isDigit(c)){
allNumberic.append(c);
}
}
return allNumberic.toString();
}
}
posted @
2009-10-31 17:54 王永庆 阅读(315) |
评论 (0) |
编辑 收藏
为将企业Bean的中间件服务需求告知EJB容器,开发者还需生成部署描述符。
<?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">
<!--
<ejb-name>:企业Bean的昵称。在部署描述符的其他地方能够引用它,供设置其他参数使用。
<home>:Home接口的全限定名。
<remote>:远程接口的全限定名。
<local-home>:本地Home接口的全限定名。
<local>:本地接口的全限定名。
<ejb-class>:企业Bean类的全限定名。
<session-type>:标识企业Bean是有状态的,还是无状态的。
<transaction-type>:事务类型。
-->
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<home>examples.HelloHome</home>
<remote>examples.Hello</remote>
<local-home>examples.HelloLocalHome</local-home>
<local>examples.HelloLocal</local>
<ejb-class>examples.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
Ejb-jar文件:
HelloWorld EJB组件准备好了之后只需将他们打包成Ejb-jar文件。手工创建jar文件
jar cf HelloWorld.jar *
注意:ejb-jar.xml文件必须放置在META-INF子目录中。当EJB容器初次打开Ejb-jar文件时,将在ejb-jar.xml中查找Ejb-jar所包含的EJB组件信息。
如何调用EJB组件:
目前,存在2类客户:
1、基于Java RMI-IIOP的客户。这类客户使用JNDI,即通过网络查找对象。另外,它们使用JTA控制事务。
2、CORBA客户。这类客户村寻CORBA标准。这对于使用其他语言访问EJB组件的客户而言,意义深远,CORBA客户使用CORBA命名服务,即通过网络查找对象。
访问EJB组件的步骤:
1、查找Home对象
2、使用Home对象创建EJB对象
3、调用EJB对象的业务方法
4、销毁EJB对象
查找Home对象:
开发EJB应用的最主要目标之一是,应用代码能够"一次编写,到处运行".如果将已部署在机器A上的EJB应用迁移到机器B上,EJB应用代码不需要改动,因为EJB实现了位置透明性。
借助于命名和目录服务能够实现EJB的位置透明性。命名和目录服务是能够在网络中存储和查找资源的产品。
在企业部署场景中,EJB服务器使用命名服务存储位置信息,而这些位置信息是用于标识资源的,其中的资源涉及到EJB Home对象、企业Bean环境属性、数据库JDBC驱动、消息服务驱动和其他资源等。通过使用命名服务,应用代码不用将具体机器或资源名硬编码在代码中,这就是EJB所具备的位置透明性,它使得代码具有便携性。
为实现位置透明性,EJB容器需要屏蔽掉Home对象的具体位置,使得EJB组件的客户代码感知不到其具体位置。客户不用将Home对象宿主的机器名硬编码于代码中。相反,使用JNDI就能够查找到Home对象。物理上,Home对象可以存在于网络上的任何地方,比如在运行EJB容器的同一进程地址空间中,其他机器上的EJB容器中。
客户为定位Home对象,它必须提供企业Bean的Home对象的JNDI昵称。客户使用该昵称标识所需的Home对象。一旦将EJB组件部署到容器中,容器会自动将HelloHome昵称绑定到Home对象。因此,在对已部署EJB组件的物理机器位置不知情时,开发者能够在任一机器上使用该昵称查找到上述Home对象,借助于JNDI能够查找到它。JNDI能够在网络上查找命名服务,或在JNDI树中查找到Home对象,当然,Home对象也可能位于客户同一进程中,最后一旦找到Home对象,客户将获得对它的引用。
package com.wyq.ejb;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
* 客户代码实例,用于调用简单、无状态会话Bean中的方法。
*
*/
public class HelloClient {
public static void main(String[] args)throws Exception {
/*
* 设置属性,用于JNDI初始化,从命令行读入属性。
*/
Properties props = System.getProperties();
/*
* 初始上下文是连接到JNDI树的入口。
* 借助于环境属性,能够完成JNDI驱动、服务器的网络地址等的设置
*/
Context ctx = new InitialContext(props);
/*
* 获得对Home对象的引用。Home对象是创建EJB对象的工厂。
*/
Object obj = ctx.lookup("HelloHome");
/*
* Home对象是RMI-IIOP对象。因此,需要借助于特殊的RMI-IIOP造型操作将它们造型成RMI-IIOP对象
*/
HelloHome home =(HelloHome)javax.rmi.PortableRemoteObject.narrow(obj,HelloHome.class);
/*
* 使用工厂,来创建Hello EJB对象。
*/
Hello hello = home.create();
/*
* 调用EJB对象的hello()方法。它会将调用委派给EJB Bean类实例。一旦接收到响应结果,它便会返回。
*/
System.out.println(hello.hello());
/*
* 在使用完EJB对象后,需要销毁它。EJB容器负责销毁EJB对象。
*/
hello.remove();
}
}
posted @
2009-10-31 12:36 王永庆 阅读(121) |
评论 (0) |
编辑 收藏
为创建和销毁EJB对象,开发者需要使用Home接口。Home接口的实现是Home对象,而Home对象由EJB服务器提供的工具生成。
package com.wyq.ejb;
import javax.ejb.EJBHome;
/**
* Hello EJB组件的Home接口。它由EJB服务器提供的工具实现。Home接口实现
* 称之为Home对象。Home对象充当了创建EJB对象的工厂。
*
* 在该Home接口中,存在create()方法。它对应于HelloBean中的ejbCreate()方法。
*/
public interface HelloHome extends EJBHome {
/**
* 创建EJB对象,
* @return 新建的EJB对象。
*/
Hello create() throws java.rmi.RemoteException,javax.ejb.CreateException;
/**
* 1、为获得对EJB对象的引用,客户需要使用create方法。其中,create方法还能够完成
* EJB组件的初始化工作。
* 2、create方法抛出了如下异常:RemoteException和CreateException。由于Home对象是网络
* 使能的基于RMI-IIOP的远程对象,因此要抛出RemoteException异常。
*/
}
本地Home接口是Home接口的高性能版本。
package com.wyq.ejb;
import javax.ejb.EJBLocalHome;
/**
* Hello EJB组件的本地Home接口。它由EJB服务器提供的工具实现。
* 本地Home接口实现称之为本地Home对象。本地Home对象充当了创建EJB本地对象的工厂。
*
*/
public interface HelloLocalHome extends EJBLocalHome {
/**
* 创建EJB本地对象
* @return 返回新创建的EJB本地对象。
*/
HelloLocal create()throws javax.ejb.CreateException;
}
创建企业Bean类
package com.wyq.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示无状态会话Bean.
*
*/
public class HelloBean implements SessionBean {
private SessionContext ctx;
//EJB规范定义的方法
public void ejbCreate(){
System.out.println("ejbCreate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
/**
* 由于企业Bean宿主在受管容器中,因此容器能够在任何时候调用企业Bean的方法。但如果企业Bean
* 需要查询容器的当前信息的话,需要为EJB提供何种信息呢?比如在EJB中,组件本身可能需要查询当前用户
* 的安全性凭证信息。
*
* 容器将这些信息包含在称之为"EJB上下文"的对象中。EJB上下文对象是通往EJB容器的关口。其中,EJB上
* 下文是容器的组成部分,开发者能够在企业Bean中访问到它。因此,在某种程度上,EJB上下文是为企业Bean
* 访问容器提供的回调。该回调有助于企业Bean探知其自身的状态,并修改他们。
*
* EJB上下文对于会话Bean、实体Bean、消息驱动Bean而言很有用。比如会话Bean含有会话上下文、实体Bean
* 含有实体上下文、消息驱动Bean含有消息驱动上下文。
* 依据EJB类型的不同,开发者能够分别通过调用setSessionContext、setEntityContext、setMessageDivenContext
* 方法设置EJB上下文信息。
*/
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
this.ctx = ctx;
}
//业务方法
public String hello(){
System.out.println("hello()");
return "Hello,World!";
}
}
posted @
2009-10-30 13:01 王永庆 阅读(146) |
评论 (0) |
编辑 收藏
首先开发远程接口,远程接口支持EJB组件暴露的所有业务方法。
package com.wyq.ejb;
import javax.ejb.EJBObject;
/**
* Hello EJB组件的远程接口
*
* 在客户同EJB对象交互时,需要使用这一接口。容器厂商会实现这一接口,而相应的实现对象
* 就是EJB对象。EJB对象会将客户请求委派给实际的EJB Bean类。
*
*/
public interface Hello extends EJBObject {
/**
* 返回欢迎信息给客户。
*/
public String hello()throws java.rmi.RemoteException;
/**
* 注意:远程接口继承于javax.ejb.EJBObject.其含义是:容器生成的EJB对象实现了远程接口(EJBObject)
* 即javax.ejb.EJBObject接口中定义的各个方法。其中包括比较不同EJB对象的方法,删除EJB对象的方法等。
* 仅实现了单个业务方法,即hello().HelloWorld EJB组件的Bean类需要实现hello方法。另外,由于Hello
* 接口是RMI-IIOP类型的远程接口,因此必须抛出远程异常。这也是企业Bean类中hello方法签名同远程接口中
* hello方法签名的区别。
*/
}
为访问EJB组件的业务方法,本地客户应该使用本地接口,而不是远程接口。
package com.wyq.ejb;
import javax.ejb.EJBLocalObject;
/**
* Hello EJB组件的本地接口。
*
* 当本地客户同EJB本地对象交互时,需要使用这一接口。容器厂商会实现这一接口。
* 而相应的实现对象就是EJB本地对象。EJB本地对象会将客户请求委派给实际的EJB Bean类。
*
*/
public interface HelloLocal extends EJBLocalObject {
/**
* 返回欢迎信息给客户,本地接口继承EjbLocalObject接口,并且不需要抛出RemoteException.
*/
public String hello();
}
posted @
2009-10-30 13:00 王永庆 阅读(114) |
评论 (0) |
编辑 收藏
由于EJB对象可以运行在与客户不同的机器上,因此客户不能够直接实例化EJB对象。EJB推崇位置透明性,因此从这个角
度考虑客户不应该对EJB对象的位置信息进行关注。
为获得对EJB对象的引用,客户代码需要从EJB对象工厂中请求EJB对象。该工厂负责实例化EJB对象。EJB规范将这种工厂
称之为Home对象。Home对象的职责主要有:
1、创建EJB对象
2、查找现有的EJB对象
3、删除EJB对象
同EJB对象一样,Home对象专属于特定的EJB容器。Home对象含有容器特定的逻辑,如负载均衡逻辑、借助于图形化管理
控制台追踪信息等。与此同时,Home对象也是EJB容器的组成部分,通过容器提供的工具能够自动创建它。
Home对象是创建EJB对象的工厂。但是Home对象是如何实例化EJB对象的呢?为创建Home对象,EJB容器需要掌握这方面的
信息。通过指定Home接口给容器即可完成这方面信息的注入。Home接口简单的定义了用于创建、销毁和查找EJB对象的方法。
容器的Home对象实现了Home接口。
使用Home接口存在一个问题,即通过Home接口创建EJB实例速度很慢,而且,借助于远程接口调用EJB实例也是如此。当
访问EJB对象时,通常会依次触发如下内容:
1、客户调用本地存根
2、存根将参数压包成适合网络传输格式
3、存根借助于网络将参数传递给骨架
4、骨架将参数解包成适合Java的格式。
5、骨架调用EJB对象。
6、EJB对象获得所需的中间件服务,如连接池、事务、安全性和生命周期服务。
7、EJB对象调用企业Bean实例,Bean实例处理客户请求。
从EJB2.0开始,客户能够通过本地对象(而不是EJB对象)快速、高效的访问企业Bean组件。具体过程如下:
1、客户访问本地对象
2、本地对象获得所需的中间件服务
3、一旦企业Bean实例处理完客户请求,则将结果返回给本地对象,最终传回给客户。
EJB对象指请求拦截器,远程接口指供请求拦截器使用的接口,Home对象指工厂,Home接口指工厂接口。
本地接口存在的缺点:
1、只在同一进程中有效。如在同一应用服务器中存在访问银行账号实体Bean的银行出纳会话Bean,如果EJB组件代码本身依赖
于本地接口实现,则不能够通过远程访问到它。
2、通过引用(传址),而不是传值来marshal参数。
部署描述符:借助于部署描述符文件,EJB组件能够声明其依赖的中间件服务。然后,EJB容器将通过部署描述符了解到组件
待使用的中间件服务。
posted @
2009-10-29 15:56 王永庆 阅读(195) |
评论 (0) |
编辑 收藏
构造函数注入:
Set注入法的缺点是,它无法清晰的表示出哪些属性是必须的,哪些是可选的。而构造函数注入法的优势是通过构造函数来强制依赖关系。
使用Set注入时,我们通过<property>元素来注入属性的值。构造函数注入也一样,只不过是通过<bean>元素下的<constructor-arg>元素来指定实例化这个bean的时候需要传递的参数。constructor-arg没有name属性。
解决构造函数参数的不确定性:
有2种方法可以用来处理构造函数的不确定性:通过序号和类型。<constructor-arg>元素有一个可选的index属性,可以用它来指定构造函数的顺序。
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg index="1">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg index="0">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
另一种方法是使用type属性。可以通过type属性确定参数的类型
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg type="java.lang.String">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg type="java.net.URL">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
使用构造函数注入的理由:
1、构造函数注入强制使用依赖契约。就是如果没有提供所有需要的依赖,一个Bean就无法被实例化。
2、由于Bean的依赖都通过它的构造函数设置了,所以没有必要再写多余的Set方法。
3、因为只能通过构造函数设置类的属性,这样你有效的保证了属性的不可变性。
Set注入的依据:
1、如果Bean有很多依赖,那么构造函数的参数列表会很长。
2、构造函数只能通过参数的个数和类型来区分。
3、如果构造函数的参数中有2个以上是相同类型的,那么很难确定每个参数的用途。
4、构造函数注入不利于自身的继承。
自动装配:
你可以让Spring自动装配,只要设置需要自动装配的<bean>中的autowire属性。
<bean id="foo" class="com.springinaction.Foo" autowire="autowire type"/>
byName-视图在容器中寻找和需要自动装配的属性名相同的Bean.
byType-视图在容器中寻找一个与需要自动配置的属性类型相同的Bean.
constructor-视图在容器中查找与需要自动装配的Bean的构造参数一致的一个或多个Bean.
autodetect-首先尝试使用congstructor来自动装配,然后使用byType方式。
使用Spring的特殊Bean
编写一个后处理Bean,Spring为你提供了2次机会,让你切入到Bean的生命周期中,检查或者修改它的配置,这叫做后处理,后处理实在Bean实例化以及装配完成之后发生的。postProcessBeforeInitialization()方法在Bean初始化之前被调用。postProcessAfterInitialization()方法在初始化之后马上被调用。
package com.wyq.hibernate;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class Fuddifier implements BeanPostProcessor {
/**
* postProcessAfterInitialization()方法循环Bean的所有属性,寻找java.lang.String类型的属性。对于每个String属性,把它传递给
* fuddify()方法,这个方法将String将变成唠叨用语。
*/
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
try{
for(int i=0;i<fields.length;i++){
if(fields[i].getType().equals(java.lang.String.class)){
fields[i].setAccessible(true);
String original = (String)fields[i].get(bean);
fields[i].set(bean,fuddify(original));
}
}
}catch(IllegalAccessException e){
e.printStackTrace();
}
return bean;
}
/**
* postProcessBeforeInitialization()方法没有做任何有意义的工作,它只是简单的返回没有修改过的Bean.
*/
public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
return bean;
}
private String fuddify(String orig){
if(orig==null)return orig;
return orig.replaceAll("(r|l)", "w").replaceAll("(R|L)", "W");
}
}
注册后处理Bean:如果你的应用系统运行在Bean工厂中,你需要调用工厂的addBeanPostProcessor()方法来注册BeanPostProcessor.
BeanPostProcessor fuddifier = new Fuddifier();
factory.addBeanPostProcessor(fuddifier);
如果你是使用应用上下文,你只需要像注册其他Bean那样注册后处理Bean.
posted @
2009-10-29 14:59 王永庆 阅读(250) |
评论 (0) |
编辑 收藏
传统建立应用系统对象之间关联关系的方法会导致复杂的代码,使它们很难被服用,很难做单元测试。在Spring中,组件无需自己负责与其他组件的关联。取而代之的是,容器负责把协作左键的引用给予各个组件。创建系统组件之间协作关系的这个动作叫做装配。
容器是Spring框架的核心。Spring容器使用IOC管理所有组成应用系统的组件。这包括在协作组件之间建立关联。Spring实际上有2种不同的容器:Bean工厂(org.springframewor.bens.factory.BeanFactory接口定义)是最简单的容器,提供了基础的依赖注入支持。应用上下文(由org.springframework.contextApplicationContext接口定义)建立在Bean工厂基础之上,提供了系统架构服务。
(1)、BeanFactory这个类负责创建和分发Bean.由于Bean工厂知道应用系统中的很多对象,所以它可以在实例化这些对象的时候,创建协作对象间的关联关系。这样就把配置的负担从Bean自身以及Bean的调用者中脱离出来。在Spring中有几种BeanFactory的实现。其中最常使用的是org.springframework.beans.factory.xml.XmlBeanFactory,它根据XML文件中的定义装载Bean.
例如:BeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml"));
这行代码告诉Bean工厂从XML文件中读取Bean的定义信息。但是,现在Bean工厂还没有实例化Bean.Bean是被延迟载入到Bean工厂中的,就是说Bean工厂会立即把Bean定义信息载入进来,但是Bean只有在被需要的时候才被实例化。
MyBean myBean = (MyBean)factory.getBean("myBean");
当getBean()方法被调用的时候,工厂就会实例化Bean并且使用依赖注入开始设置Bean的属性。
(2)、ApplicationContext的诸多实现中,有3个实现经常用到:
ClassPathXmlApplicationContext-从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。
FileSystemXmlApplicationContext-从文件系统中的XML文件载入上下文定义信息。
XmlWebApplicationContext-从web系统中的XML文件载入上下文定义信息。
例如:ApplicationContext context = new FileSystemXmlApplicationContext("c:/foo.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("foo.xml");
FileSystemXmlApplicationContext只能在指定的路径中寻找foo.xml文件,而ClassPathXmlApplicationContext可以在整个类路径中寻找foo.xml.
应用上下文与Bean工厂的另一个重要区别是关于单实例Bean是如何被载入的。Bean工厂延迟载入所有的Bean,知道getBean()方法被调用时Bean才被创建。应用上下文启动后预载入所有的单实例Bean.
Spring中的Bean缺省情况下是单例模式。在容器分配Bean的时候,它总是返回同一个实例。如果想每次得到不同的实例你需要将Bean定义为原型模式。定义为原型模式意味着你是定义一个Bean蓝图,而不是一个单一的Bean.<bean>的singleton属性告诉这个bean是否是单例的,如果是true表示是单例的,如果是false表示是原型的。
<bean id="connectionPool" class="com.springinaction.chapter02.MyConnectionPool" init-method="initialize" destroy-method="close"/>
按这样配置,MyConnectionPool被实例化后initialize()方法马上被调用,给Bean初始化池的机会。在Bean从容器中删除之前,close()方法将释放数据库连接。
设值注入:它是一种基于标准命名规范的设置Bean属性的技术。JavaBean规范规定使用对应的set和get方法来设置和获得Bean的属性值。<property>元素的子元素<value>用来设置普通类型的属性,子元素<ref bean="">用来设置要关联的Bean.
内部Bean:另一种不常使用的装配Bean引用的方法是在<property>元素中嵌入一个<bean>元素。
<bean id="courseService" class="com.springinaction.service.training.CourseServiceImpl">
<property name="studentService">
<bean class="com.springinaction.service.training.StudentServiceImpl"/>
</property>
</bean>
这种装配Bean引用的方式的缺点是你无法在其他地方重用这个StudentServiceImpl实例,因为它是一个专门的courseServiceBean建立的实例。
注入的是对象集:如果你有一个属性是一个列表或是一个Bean引用集合,Spring支持多种类型的集合作为属性。
<list><set><map><props>
装配List和数组:装配一个List属性,List里的元素可以是任何一种元素,包括<value><ref>甚至是其他<list>
<property name="barList">
<list>
<value>bar1</value>
<ref bean="bar2"/>
</list>
</property>
装配Set:和List一样,Set可以包含任何类型的属性。
<property name="barSet">
<set>
<value>bar1</value>
<ref bean="bar2"/>
</set>
</property>
<set>的使用方法和<list>是一样的。唯一不同的地方是它被装到什么样的属性中。<list>是向java.util.List或数组里装配数据,而<set>是向java.util.Set中装配数据。
装配Map:
<property name="barMap">
<map>
<entry key="key1">
<value>bar1</value>
</entry>
<entry key="key2">
<value>bar2</value>
</entry>
</map>
</property>
Map中的<entry>的数值和<list>以及<set>的一样,可以是任何有效的属性元素。重申一边,包括<value>、<ref>、<list>甚至是另一个<map>。注意,配置<entry>时,属性key的值只能是String.这对于java.util.Map的功能来说有一点限制,java.util.Map是允许任何类型的对象作为主键的。
装配properties:java.util.Properties集合是最后一个能在Spring中装配的集合类。使用<props>元素来装配它。使用<prop>元素表示每条属性。
<property name="barProps">
<props>
<prop key="key1">bar1</prop>
<prop key="key2">bar2</prop>
</props>
</property>
设置null
为了将一个属性设为null,你只要使用<null/>元素就行了。
例如:
<property name="foo"><null/><property>
posted @
2009-10-29 10:28 王永庆 阅读(1587) |
评论 (2) |
编辑 收藏
EJB技术有赖于2种重要技术的存在:Java RMI-IIOP和JNDI.EJB客户仅仅同其暴露的单一接口进行交互。其中暴露的接口
和企业Bean本身必须遵循EJB规范。EJB规范要求企业Bean暴露若干所要求的方法,而这些暴露的方法是供EJB容器统一管理他
们使用的,无论EJB组件部署在何种EJB容器中。
EJB2.1规范定义了3种EJB组件类型。
会话Bean:会话Bean用于建模业务过程。即为完成某些任务而存在。
实体Bean:实体Bean用于建模业务数据。他们是数据对象。实体Bean是用于缓存数据库信息的Java对象。
消息驱动Bean:MDB类似于会话Bean,因为它们都是为完成某些任务而存在的。
EJB组件是基于分布式对象构建的,远程系统能够调用分布式对象。当然,进程内的客户、进程外的客户及网络中的其他
客户都能够与分布式对象进行交互。具体过程如下:
客户调用存根:即为客户端提供的代理对象。存根负责将网络通信屏蔽掉,这对于客户而言意义深远。存根能够使用
Socket访问网络中的其他资源,其间将调用所需的参数构建成网络要求的调用格式。
借助于网络:存根能够实现与服务器端的骨架进行交互。骨架是为服务器端提供的代理对象。骨架负责将网络通信屏蔽
掉,这对于分布式对象而言意义深远。骨架能够处理来自Socket的访问请求。当然,骨架还能够将存根构建好的参数转换成
Java表示。
骨架将具体的客户调用委派给合适的分布式对象实现。因此,该对象将完成具体的访问请求,并将结果返回给骨架。依
此类推,骨架将结果返回给存根,最后客户将获得分布式对象的响应结果(通过存根获得)。
重要:存根和服务器端实现的对象都实现了同一接口(称之为远程接口)。其意味着存根克隆了分布式对象的方法签名。
调用存根中的方法的客户以为其自身直接调用了分布式对象,但实际情况是客户直接调用存根,而存根处理具体的网络访问
工作。因此,这可以称之为分布透明性。
分布式对象是对如下几个协作对象的一种抽象:存根、骨架、实现的对象,单一的参与者不可能构成分布式对象。
分布式对象是很诱人的,因为开发者能够基于网络构建应用。在开发大型的分布式对象应用的时候,需要借助于中间件
服务,如事务和安全性。
远程对象:
(1)、企业Bean的业务逻辑需要在Bean类中实现。EJB规范定义了企业Bean类待实现的若干接口。这些接口使得所有的企
业Bean类能够暴露同一套接口。各种企业Bean的Bean类都需要实现javax.ejb.EnterpriseBean接口。
当然,EJB规范分别为会话Bean、实体Bean和消息驱动Bean提供了更专属的接口类型,
SessionBean,EntityBean,MessageDriverBean.
(2)、当客户使用企业Bean类的某实例时,它并不是直接调用Bean类的方法,EJB容器将拦截客户调用,然后委派给企业
Bean实例。通过拦截客户请求,EJB容器能够自动操作隐式中间件服务。
因此,在客户和EJB组件间,引入了间接层,即EJB容器。引入的间接层将自身封装为单一的网络使能对象,即EJB对象,
EJB对象正是请求拦截器。EJB对象是代理对象,它可以与网络、事务、安全性等进行交互。企业Bean客户调用EJB对象提供的
方法,而不是企业Bean本身。因此,EJB对象必须复制企业Bean类暴露的业务方法。但是,自动生成EJB对象的工具是怎么知
道业务方法的呢?答案是,通过企业Bean提供者提供的接口来实现,即远程接口。这个接口复制企业Bean类暴露的业务方法。
EJB容器负责管理EJB组件,通过调用EJB组件提供的回调方法,EJB容器能够同EJB组件交互。这些回调方法仅供EJB容器管理
EJB组件使用。
EJB容器最为重要的一项工作是,提供EJB组件运行的环境。EJB容器宿主了EJB组件,从而供远程客户调用。因此,可以
认为EJB容器充当了客户和EJB组件的中间人。EJB容器负责将客户连接到EJB组件,完成事务协调工作,提供持久化能力,管
理EJB组件的声明周期及其他任务。
EJB的远程接口还需复制企业Bean类的业务方法。一旦企业Bean客户调用业务方法,EJB对象将把调用操作委派给相应的
Bean类。
(3)、Java RMI-IIOP和EJB对象:javax.ejb.EJBObject继承于java.rmi.Remote.任何实现了Remote接口的对象都是远程
对象,即能够从其他JVM中访问到它,这就是Java实现RMI方式。由于容器提供的EJB对象实现了EJB组件的远程接口,因此EJB
对象也间接实现了java.rmi.Remote接口。EJB对象是具有完整网络功能的RMI-IIOP对象,供其他JVM或者网络中其他物理机器
访问。实际上,EJB远程接口仅仅是RMI-IIOP远程接口,只不过它还遵循EJB规范罢了。
远程接口还必须遵从RMI-IIOP的参数传递约定。并不是所有的内容都能够跨越JVM传递的。方法中传递的参数必须是符合
RMI-IIOP约定的有效Java类型,如Java原型、序列化对象和RMI-IIOP对象。
总之,EJB容器需要负责权衡资源管理和管理已部署EJB组件的生命周期。
posted @
2009-10-28 15:39 王永庆 阅读(254) |
评论 (0) |
编辑 收藏
EJB组件技术吸收了企业级计算领域中各个方面的丰富经验和知识,比如分布式计算、数据库、安全性、组件驱动软件等。服务器端开发平台,即Java2平台企业版,企业Bean组件架构是J2EE的重要部分。
J2EE是对概念、编程标准、技术革新的统称,即基于Java编程语言开发服务器端应用。借助于J2EE,开发者能够快速构建分布式、扩展性强、可靠的、便携性好、安全的服务器端应用。
EJB,是基于Java的服务器端组件架构技术。在Java应用服务器提供商提供的分布式基础框架(比如,J2EE应用服务器)的基础之上,开发者能够快速、轻松的构建服务器端组件。EJB的重要设计初衷是保证应用能够在任一提供商提供的企业中间件服务上畅行无阻,而且其便携性、可重用性都是一流的。
分布式系统:通过将传统的大型应用系统分解成多层系统,这使得组成应用系统的各层相互独立,并且承担的职责各不相同。早先,在整个中间件领域至少存在50个应用服务器。起初,各个应用服务器分别以非标准、专有的方式提供组件服务。因此,它们对于组件服务的认识各不相同,而且对具体提供组件服务的方式都不尽相同。更不用谈组件同应用服务器的具体交互了。这样的结果是:一旦组件部署到某个应用服务器,则它就被锁定到该应用服务器上。这对于客户而言,是不希望看到的情景。
如果能够在不同ISV间达成共识,比如在应用服务器和组件间提供相同的接口,供开发和部署应用使用,则这将使得任何组件能够运行在任一应用服务器中。与此同时,组件也能够在不同应用服务器间实现热插拔,而且不用修改应用代码,甚至也不用重新编译组件本身。本书将这种共识称之为组件架构。
服务:就是一组提供具体业务操作功能的相关组件。SOA具有极强的独立性,这些SOA提供的服务能够同其他服务进行交互,而不管其实现方式和部署的目标平台如何。因此,将这些独立、松耦合的服务集成起来是很有现实意义的。
SOAP:简单对象访问协议,SOAP是基于XML、应用级的协议,供分布式网络中交换信息使用。目前,SOAP支持2种分布式计算模型:面向RPC和面向文档风格的SOAP消息。
Web服务为那些分布式、松耦合SOA应用场合提供了强有力的解决方案。借助于WSDL能够描述WEB服务;借助于SOAP能够访问到WEB服务。
EJB仅仅是J2EE的组成部分之一。其中,J2EE平台规范是由JCP指定。J2EE平台规范的使命是,提供平台独立的、便携的、多用户的、安全的、遵循标准的企业级平台。而且,它必须基于java和在服务器端部署。
posted @
2009-10-27 22:52 王永庆 阅读(185) |
评论 (0) |
编辑 收藏
Blob、Clob字段的映射
Blob和Clob字段的区别在于,Blob字段采用单字节存储,适合保存二进制数据,如图片文件。Clob字段采用多字节存储,适合保存大型文本数据。
在Oracle中Blob/Clob字段独特的访问方式,Oracle Blob/Clob字段本身拥有一个游标,JDBC必须必须通过游标对Blob/Clob字段进行操作,在Blob/Clob字段被创建之前,我们无法获取其游标句柄,这也意味着,我们必须首先创建一个空Blob/Clob字段,再从这个空Blob/Clob字段获取游标,写入我们所期望保存的数据。
实体粒度设计:
在Hibernate世界里,我们经常听到"fine-grained object model"直接翻译就是适当的细粒度对象模型。
适当的比较模糊,细粒度就是将原本业务模型中的对象加以细分,从而得到更加精细的对象模型。就是划分出更多的对象。分为:面向设计的细粒度划分和面向性能的细粒度划分。
对于单表的对象细分,在Hibernate中可借助Component节点的定义来完成。何谓Component?从名字上来看,既然称之为组件,则其必然是从属于某个整体的一个组成部分。在Hibernate语义中,我们将某个实体对象中的一个逻辑组成成为Component.Component与实体对象的根本差别,就在于Component没有标示,它作为一个逻辑组成,完全从属于实体对象。通过Component定义,我们将T_User表实际映射到了3个类,TUser,Contact和Name,这样我们就可以在传统关系型库表上,实现了面向对象的领域划分。
面向性能的细粒度划分:
当我们通过Hibernate加载TUser对象时,Hibernate会从库表中读取所有的字段数据,并构造TUser实例返回。这里就产生了一个性能方面的问题,作为blob/clob等重量级字段类型,数据库读取操作代价较高。对于需要处理resume和image的应用逻辑而言,这样的代价无法避免,而对于那些无需resume和image信息的操作而言,如此性能无谓的损耗实在可惜。
如何避免这个问题,Hibernate3提供了属性的延迟加载功能,通过这个功能,我们可以在调用TUser.getResume/getImage时才真正从数据库中读取数据。对于hibernate2来说我们通过继承关系,我们将一个对象进行纵向细分来解决这个问题。我们通过在子类的映射文件中class节点指定polymorphism="explicit"声明了一个显示多态关系。声明为显示多态的类,只有在明确指定类名的时候才会返回此类实例。
package com.wyq.hibernateLoad;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.wyq.hibernate.TUser2;
public class TUserOperator {
Session session = null;
//复合主键加载数据
public void loadData(){
TUser2 user = new TUser2();
user.setFirstname("Kevin");
user.setLastname("Shark");
user = (TUser2)session.load(TUser2.class, user);
System.out.println("User age is=>"+user.getAge());
}
//SQL保存图片
public void saveImage(){
TUser2 user = new TUser2();
user.setAge(new Integer(20));
try {
FileInputStream imgis = new FileInputStream("C:\\inimage.jpg");
Blob img = Hibernate.createBlob(imgis);
user.setImage(img);
Clob resume = Hibernate.createClob("This is Clob");
user.setResume(resume);
Transaction tx = session.beginTransaction();
session.save(user);
tx.commit();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//SQL读取图片
public void getImageObj(){
TUser2 user = (TUser2)session.load(TUser2.class,new Integer(3));
Clob resume = user.getResume();
Blob img = user.getImage();
try {
InputStream is = img.getBinaryStream();
FileOutputStream fos = new FileOutputStream("c:\\outimage.jpg");
byte[] buf = new byte[102400];
int len;
while((len=is.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
is.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Oracle保存图片
public void saveForOracleImage(){
TUser2 user = new TUser2();
user.setAge(new Integer(20));
user.setImage(Hibernate.createBlob(new byte[1]));
user.setResume(Hibernate.createClob(" "));//注意这里的参数是一个空格
Transaction tx = session.beginTransaction();
session.save(user);
//调用flush方法,强制Hibernate立即执行insert sql
session.flush();
//通过refresh方法,强制Hibernate执行select for update
session.refresh(user, LockMode.UPGRADE);
//向Blob写入实际内容
oracle.sql.BLOB blob = (oracle.sql.BLOB)user.getImage();
try {
OutputStream out = blob.getBinaryOutputStream();
FileInputStream imgis = new FileInputStream("c:\\inimage.jpg");
byte[] buf = new byte[10240];//10k缓存
int len;
while((len=imgis.read(buf))>0){
out.write(buf,0,len);
}
imgis.close();
out.close();
//向CLOB写入实际内容
oracle.sql.CLOB clob = (oracle.sql.CLOB)user.getResume();
java.io.Writer writer = clob.getCharacterOutputStream();
writer.write("this is my resume");
writer.close();
session.save(user);
tx.commit();
} catch (SQLException e) {
e.printStackTrace();
}catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
实体层次设计:继承关系是关系型数据与面向对象数据结构之间的主要差异之一。
Hibernate中支持3种类型的继承形式:
1、表与子类之间的独立一对一关系。
2、每个子类对应一张子表,并与主类共享主表
3、表与类的一对多关系
posted @
2009-10-27 22:32 王永庆 阅读(219) |
评论 (0) |
编辑 收藏
对于新系统的设计开发而言,我们应该尽量避免在哭表中引入与业务逻辑相关的主键关系。
将业务逻辑主键引入库表,将使得底层库表结构与业务逻辑相耦合,之后业务逻辑的变化,将很可能对底层数据库结构产生连带影响。
复合主键的引入,很大程度上意味着业务逻辑已经侵入到数据存储逻辑之中。因此在新系统的设计过程中,我们应该尽量避免这样的情况出现。
Hibernate中,通过composite-id节点对复合主键进行定义。
对于复合主键而言,我们可以通过2种方式确定主键:
1>基于实体类属性的复合主键
复合主键由实体类中的属性组成,此时,实体类本身即同事扮演复合主键类的角色。
<hibernate-mapping>
<class name="com.wyq.hibernate.TUser2" table="T_User2">
<composite-id>
<key-property name="lastname" column="lastname" type="string"/>
<key-property name="firstname" column="firstname" type="string"/>
</composite-id>
<property name="age" column="age" type="integer">
</property>
</class>
</hibernate-mapping>
Hibernate要求复合主键类实现equals和hashcode方法,以作为不同数据之间识别的标志。
主键类的加载:我们可以将TUser2类对象本身作为查询条件进行检索:
2>基于主键类的复合主键
我们可以将主键属性独立到一个单独的类中。实现方法类似,只不过映射文件的composite-id节点要引入class和name属性。
posted @
2009-10-27 22:30 王永庆 阅读(142) |
评论 (0) |
编辑 收藏
有一周的时间没有更新技术了,最近离职(南京合荣欣业),交接的东西太多,最后还要完成一个场景(支付信息业务登记薄查询)的开发,当初都不应该接这个场景,现在每天都在弄,还好昨天弄完了,明天可以继续看书更新技术了,这3年一直在技术上追寻着,可能没有学到新的技术,但是,感觉架构的思想理解了不少,每一种技术的出现都是有他的原因的,不一定是新的技术就要用,要选择适合自己的。
3年的时间,做IT的自己认识的朋友有限,不管男女(女的更少,能称得上朋友的),很大程度上是自己的性格所限,没办法啊,老天爷给了这个性格,都说性格可以改变,那都是扯,俗话说得好,狗改不了吃屎,性格是天生的,除非后天遇到什么重大事件,否则很难改变。自己的这种沉默的性格,也有点好处,就是对程序来说,可能只关注代码吧,但是也没做到牛B的那种,是说技术大牛。
讨厌做程序的人,可能是太枯燥了,说到这里吧,下周开始继续博客,希望这次能坚持住。
posted @
2009-10-24 14:13 王永庆 阅读(131) |
评论 (0) |
编辑 收藏