現在你可以為你的應用系統創建可重復使用的切入點了。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 王永慶 閱讀(165) |
評論 (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 王永慶 閱讀(214) |
評論 (0) |
編輯 收藏
時間真的可以抹殺一切,歷史如此,人的感情也是如此,隨著時間的流逝,一切都變得模糊起來。不知不覺中,對你的迷戀不在那么執著了,不知道是自己放棄了這份執著,還是你有意保持我們的距離。面對著電腦的屏幕我也在問我同樣的問題,我為什么要放棄,我自己都無法回答。
時間一點點的過去,面對離開公司的日子越來越近,少不了要和兄弟姐妹們大吃幾頓,自己都不知道腐敗了多少次,但是這些次吃飯,都沒有帶上你,因為不知道怎么去面對你(是以朋友的身份還是同事的身份,還是我的另一半的身份去面對)。在你的面前我總是那么的拘謹,那么的緊張,致使連話都不會說,也許只有文字才能說出我的真心。雖然我不會說話,但是我的心是真誠的,雖然我不會體貼,但是我會學。不知道你心中的另一半要找什么樣子的,我的外表雖然不夠帥,但是我的心是熾熱的。回憶你剛來公司的時候,那時公司也進入了繁忙的階段,每天10點多才下班,你知道我心里在想什么么?我不想你一個人走在回家的路上,我不想你周末沒人陪,不想你一個人去吃飯......,不知道喜歡一個人是什么感覺,還從來沒對人這樣過。你要求的那一半我可能達不到,你的那一半可能是很帥氣,很高大,很會說話,很幽默,很浪漫......這些都不是我具備的,我能給的只有我的心,那些我真的給不了你,我不想成為那樣一個人,因為我就是我。想一個人真的好累,我不想這么累,既然我們沒有緣分,人們都說愛情是幸福快樂的,我怎么覺得愛一個人這么累,也許我還沒有真正的去愛。
既然不能愛你,那就祝福你吧,希望你永遠都幸福快樂,每天都像小時候一樣無憂無慮,找一個真心愛護你的人。再見了,我的她的回憶。
想用自己的真心去關心一個人,想讓心中的她快樂。
posted @
2009-11-05 23:21 王永慶 閱讀(155) |
評論 (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 王永慶 閱讀(227) |
評論 (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 王永慶 閱讀(323) |
評論 (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 王永慶 閱讀(148) |
評論 (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 王永慶 閱讀(166) |
評論 (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 王永慶 閱讀(221) |
評論 (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 王永慶 閱讀(182) |
評論 (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 王永慶 閱讀(169) |
評論 (0) |
編輯 收藏