现在你可以为你的应用系统创建可重复使用的切入点了。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) |
编辑 收藏