随笔 - 9  文章 - 5  trackbacks - 0
<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(1)

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

(文章本人原创,若转载请注明出处)

下面看一下一些具体的实现,先看一下Sender接口的commitData方法的MySql实现,即SenderMySqlImp类:


    public void commitData(String path, String file) {
        ......
        Connection connect = null;
        try {
          //根据配置由Helper来确定是否用连接池,这只调用getConnection()即可
          connect = Helper.getConnection();
            connect.setAutoCommit(false);
            FileInputStream fis = new FileInputStream(path + file);
          
//insert语句,有三个参数分别为id,image,filename字段。
           ps = connect.prepareStatement(sql.toString());
           ps.setString(1, UUID.randomUUID().toString());
         //将图片文件流化,用jdbc直接写到数据库
          ps.setBinaryStream(2, fis, fis.available());
            ps.setString(3, file);
            ps.executeUpdate();
            connect.commit();
            count++;
             Logger.writeLog("已处理文件数:"+count+",时间:"+new java.util.Date());

        } catch (Exception e) {
         ........
    }

    很简单吧,其实就是用Stream来做,另外在网上可以找到有关Oracle上传blob的实现,一般都是先insert一条记录,blob字段用empty_blob()函数插入一个空数据,然后再取出这个blob字段,最后按字节写入blob,具体参考SenderOracleImp类吧。个人感觉还是在mysql的这个效率高些并且看起来简单了很多。

    然后来看看使用线程池的ProcessMulti类:


public class ProcessMulti implements Process{
    
private String path;
    
private Vector<String> files = new Vector<String>();
    
private Properties prop;

    ProcessMulti() {
        prop 
= ConfigMgr.getProperties();  //取config.properties中配置信息
        
this.path = prop.getProperty("path");
        
this.files = Helper.getFiles(prop.getProperty("filetype"), this.path);
    }

    
public void doProcess() {
//正如前面两篇所说,这里是线程池构建器,传入相关参数
        BlobSenderThreadPool tpe 
= new BlobSenderThreadPool(Integer
                .valueOf(prop.getProperty(
"corePoolSize")), Integer
                .valueOf(prop.getProperty(
"maxPoolSize")), Integer.valueOf(prop
                .getProperty(
"keepAliveTime")), TimeUnit.SECONDS,
                
new ArrayBlockingQueue<Runnable>(Integer.valueOf(prop
                        .getProperty(
"maxPoolSize"))),
                
new BlobSenderThreadPool.DiscardPolicy(), files.size());

        Logger.writeLogForce(
"开始处理." + new java.util.Date());
        
for (int i = 0; i < this.files.size(); i++) {
            //向线程池提交要处理的任务,线程池根据其配置进行处理
            //Helper.getSender()会根据配置取得支持mysql或是oracel的写入方法

            tpe.execute(
new Runner(path, files.get(i), Helper.getSender()));
            Logger.writeLog(
"已提交第" + (int)(i+1)  + "个文件" + ",时间为:"
                    
+ new java.util.Date());
        }
    //可以在这里写一个打印输出,实际上程序很快就执行完上面的for,运行到这里,但是处理并没有完成,
       //主程序好像职业经理人,他的工作就是分配任务给下属,自已就完成工作了,但下属们还要接着忙活呵呵...

       System.out.println("任务已分配...");
    }
    //线程池类
    
class BlobSenderThreadPool extends ThreadPoolExecutor {
        
volatile int planTask;//计划任务,即计划要写的文件数

        
public BlobSenderThreadPool(int corePoolSize, int maximumPoolSize,
                
long keepAliveTime, TimeUnit unit,
                BlockingQueue
<Runnable> workQueue,
                RejectedExecutionHandler handler, 
int planTask) {
            
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
            
this.planTask = planTask;
        }

        @Override
        //当某个线程处理完时会调用此方法
        
protected void afterExecute(Runnable r, Throwable t) {
            Logger.writeLog(
"当前已完成任务数:" + (int)(this.getCompletedTaskCount()+1)
                    
+ "计划任务数:" + planTask);
           //若已处理完任务和计划的任务数相同说明所有线程已完成处理,终止线程池处理
      if ((this.getCompletedTaskCount()+1)== planTask)
                
this.terminated();
            
super.afterExecute(r, t);
        }

        @Override
        
protected void terminated() {
            Logger.writeLogForce(
"所有任务已完成 ,处理结束时间===>>" + new java.util.Date());
            System.exit(
0);
        }

    }

//要使用线程进行处理,类要实现Runable接口
    
class Runner implements Runnable {
        String file;
        String path;
        Sender sender;

        Runner(String path, String file, Sender sender) {
            
this.file = file;
            
this.path = path;
            
this.sender = sender;
        }
//Runer的实例会被传入线程池,线程被运行时会调用run方法
        
public void run() {
            sender.commitData(path, file);
        }

    }
posted @ 2009-03-21 10:40 依然Fantasy 阅读(616) | 评论 (0)编辑 收藏

(文章本人原创,若转载请注明出处)

  在实际当中的情况是系统数据库中需要上传大量照片到数据库中,数据量比较大,且不能在界面中通过操作逐个上传,要批量自动进行。其实起来也比较简单直接利用线程池将照片数据读取成流再存入BLOB字段即可。但是在实现后些功能后又进入了一些改造,实现了线程池、单线程、是否使用用连接池、不同数据库等不同的配置,这样在不同配置下可以观察到程序性能的不同。并且经过设计切换这些配置不需要修改程序。


使用DbAccess接口的getConnect()取得数据库连接,DbImp和DbPoolingImp实现了不使用连接池和使用连接池的两个版本。Sender接口的commitData()用来把BLOB数据写到数据库中,因为不同数据库可能写法有点不同所以这里SenderMySqlImp和SenderOracleImp分别是Mysql和Oracle的实现。Process接口的doProcess()是开始进行处理的方法,无论是单线程还是多线程。因此ProcessMulti和ProcessSingle是分别使用线程池以及单线程处理的类。ConfigMgr用于取得config.properties文件内配置信息,Logger是日志类,Helper中汇集了一些共用的静态方法。最后DataSender是主程序的类:)

posted @ 2009-03-21 10:25 依然Fantasy 阅读(297) | 评论 (0)编辑 收藏

(文章本人原创,若转载请注明出处)

   在JDK1.5提供了一个线程池ThreadPoolExecutor,可以处理用户提交过来的线程。如果把要处理的任务比作盖一个大楼,那么每一个建筑工人就相当于一个线程,那么这个ThreadPoolExecutor就好像包工头,它来控制盖这个大楼需要多少个工人,何时招进新工人,何时辞退已经长时间没有事做的工人,等等此类事务。也就是说用户程序不断提交新的线程,ThreadPoolExecutor执行提交线程的同时会控制目前总共同时执行的线程数,销毁已执行完闲置的线程等控制行为,保留最少闲置线程数,并且可以配置不同的处理策略。

   为什么要使用线程池呢,这与数据库连接池的原理有点相仿,线程的创建是需要成本的,包括服务器CPU和内存资源,由于多线程是并行运行,程序运行过程中可能有的线程已经完成自身处理任务,处于闲置状态,如果在这种情况下再不断创建新任务就是在浪费服务器资源,此时应该尽量使用先前创建的好的并且是处理闲置状态的线程来处理新任务,而线程池就可以有效的对此进行自动化管理,当然这个管理是可以由用户配置的。

ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)

这是线程池的构建器,用户程序通过这个构建器传参数,corePoolSize是线程池中核心线程数,运行的线程数不能少于这个核心线程数,否则就新建线程。maximumPoolSize是充许最大的线程数。keepAliveTime设置除核心线程外其它线程的空闲时间,超过这个时间线程就自动终止。unit是指的keepAliveTime的时间单位。BlockingQueue接口按生产则消费者算法设计的一个线程池内部处理线程队列的接口,有三种实现SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue,在实际运行程序时可以根据这三种实现得到不同的性能,比如有的实现可能在有新任务来时不新建线程,而是将其加入等待队列,等有线程运行完时再分配给其使用。具体实现还是参看它们的JDK文档吧,这里站在使用的角度它们是可以调整运行性能的开关。当最大线程和工作队列容量都达到最大值时,再提交给线程池新任务就会被拒绝,此时线程池会调用RejectedExecutionHandler 接口进行处理,具体实现有四种策略。我们只需要选用其中一种在构建ThreadPoolExecutor时传入即可。具体四种实现还是参看JDK文档吧。关于ThreadPoolExecutor的JDK文档。至此控制线程池运作的几个参数都从构建器中传入了。

posted @ 2009-03-19 22:26 依然Fantasy 阅读(842) | 评论 (0)编辑 收藏

google了一篇不错的例子,加了点注解,这样看起来更方便了:)

Oracle不像SQLServer那样在存储过程中用Select就可以返回结果集,而是通过Out型的参数进行结果集返回的。实际上是利用REF CURSOR

--procedure返回记录集:

----------------------声明一个Package--------------
CREATE OR REPLACE PACKAGE pkg_test
AS
TYPE myrctype IS REF CURSOR;

PROCEDURE get (p_id NUMBER, p_rc OUT myrctype); --Package中声明名为get 的Procedure(只有接口没内容)

END pkg_test;

--------------------------------------------------------


-----------------声明Package Body,即上面Package中的内容,包括Procedure get---------------------
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE get (p_id NUMBER, p_rc OUT myrctype)
IS
sqlstr VARCHAR2 (500);
BEGIN
IF p_id = 0 THEN
OPEN p_rc FOR
SELECT ID, NAME, sex, address, postcode, birthday
FROM student;
ELSE
sqlstr :=
'select id,name,sex,address,postcode,birthday
from student where id=:w_id'; --w_id是个参数,

--以下 p_rc是个REF CURSOR游标类型,而且是OUT型参数,即可返回一个记录集了。USING p_id就是替换上面SQL中:w_id值拉:)
OPEN p_rc FOR sqlstr USING p_id; 

END IF;
END get;
END pkg_test;


--function返回记录集的例子,原理和上面相同,而是用function的return值来返回记录集。

函数返回记录集:
建立带ref cursor定义的包和包体及函数:
CREATE OR REPLACE
package pkg_test as
/* 定义ref cursor类型
不加return类型,为弱类型,允许动态sql查询,
否则为强类型,无法使用动态sql查询;
*/
type myrctype is ref cursor;
function get(intID number) return myrctype;
end pkg_test;
/

CREATE OR REPLACE
package body pkg_test as
--函数体
function get(intID number) return myrctype is
rc myrctype; --定义ref cursor变量
sqlstr varchar2(500);
begin
if intID=0 then
--静态测试,直接用select语句直接返回结果
open rc for select id,name,sex,address,postcode,birthday from student;
else
--动态sql赋值,用:w_id来申明该变量从外部获得
sqlstr := 'select id,name,sex,address,postcode,birthday from student where id=:w_id';
--动态测试,用sqlstr字符串返回结果,用using关键词传递参数
open rc for sqlstr using intid;
end if;

return rc;
end get;

end pkg_test;

posted @ 2009-01-13 21:55 依然Fantasy 阅读(1598) | 评论 (0)编辑 收藏

在ORACLE存储过程中创建临时表

存储过程里不能直接使用DDL语句,所以只能使用动态SQL语句来执行

--ON COMMIT DELETE ROWS 说明临时表是事务指定,每次提交后ORACLE将截断表(删除全部行)
--ON COMMIT PRESERVE ROWS 说明临时表是会话指定,当中断会话时ORACLE将截断表。


CREATE OR REPLACE PROCEDURE temptest
(p_searchDate IN DATE)
IS
v_count INT;
str varchar2(300);
BEGIN
v_count := 0;
str:='drop table SETT_DAILYTEST';
execute immediate str;
str:='CREATE GLOBAL TEMPORARY TABLE SETT_DAILYTEST (
NACCOUNTID NUMBER not null,
NSUBACCOUNTID NUMBER not null)
ON COMMIT PRESERVE ROWS';
execute immediate str; ----使用动态SQL语句来执行
str:='insert into SETT_DAILYTEST (select naccountid,nsubaccountid from sett_dailyaccountbalance)';
execute immediate str;
END temptest;

上面建立一个临时表的存储过程

下面是执行一些操作,向临时表写数据。

CREATE OR REPLACE PROCEDURE PR_DAILYCHECK
(
p_Date IN DATE,
p_Office IN INTEGER,
p_Currency IN INTEGER,
P_Check IN INTEGER,
p_countNum OUT INTEGER)
IS
v_count INT;
BEGIN
v_count := 0;
IF p_Date IS NULL THEN
dbms_output.put_line('日期不能为空');
ELSE
IF P_Check = 1 THEN
insert into SETT_DAILYTEST (select naccountid,nsubaccountid from sett_dailyaccountbalance
where dtdate = p_Date);

select
count(sd.naccountid) into v_count
from sett_subaccount ss,sett_account sa,sett_dailytest sd
where sd.naccountid = sa.id and sd.nsubaccountid = ss.id and sa.id = ss.naccountid
AND sa.nofficeid = p_Office AND sa.ncurrencyid = p_Currency
and rownum < 2;
COMMIT;
p_countNum := v_count;
dbms_output.put_line(p_countNum);
END IF;
IF P_Check = 2 THEN
insert into SETT_DAILYTEST (select naccountid,nsubaccountid from sett_dailyaccountbalance
where dtdate = p_Date);

select
count(sd.naccountid) into v_count
from sett_cfsubaccount ss,sett_account sa,sett_dailytest sd
where sd.naccountid = sa.id and sd.nsubaccountid = ss.id and sa.id = ss.naccountid
AND sa.nofficeid = p_Office AND sa.ncurrencyid = p_Currency
and rownum < 2;
COMMIT;
p_countNum := v_count;
dbms_output.put_line(p_countNum);
END IF;
END IF;
END PR_DAILYCHECK;

posted @ 2009-01-13 21:23 依然Fantasy 阅读(17344) | 评论 (0)编辑 收藏

Oracle本身没数组的概念,但是通过Oracle的Collections和Records类型可以模仿出单维数组和多维数组。

请参考<<Oracle PL/SQL Programming>> Chapter 11、Chapter 12。


---------------------- 单维数组 ------------------------
DECLARE
TYPE emp_ssn_array IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; ----注:声明一个Collection

best_employees emp_ssn_array;
worst_employees emp_ssn_array;

BEGIN
best_employees(1) := '123456';
best_employees(2) := '888888';

worst_employees(1) := '222222';
worst_employees(2) := '666666';

FOR i IN 1..best_employees.count LOOP
DBMS_OUTPUT.PUT_LINE('i='|| i || ', best_employees= ' ||best_employees(i)
|| ', worst_employees= ' ||worst_employees(i));
END LOOP;

END;



---------------------- 多维数组 ------------------------

DECLARE

TYPE emp_type IS RECORD ---------注:声明一个Record类型 emp_type
( emp_id employee_table.emp_id%TYPE,           ----Record类型中的成员...
emp_name employee_table.emp_name%TYPE,
emp_gender employee_table.emp_gender%TYPE );

TYPE emp_type_array IS TABLE OF ----注:声明一个Collection类型 emp_type_array ,其中元素为emp_type类型
emp_type INDEX BY BINARY_INTEGER;

emp_rec_array emp_type_array;
emp_rec emp_type;

BEGIN
emp_rec.emp_id := 300000000;
emp_rec.emp_name := 'Barbara';
emp_rec.emp_gender := 'Female';

emp_rec_array(1) := emp_rec;

emp_rec.emp_id := 300000008;
emp_rec.emp_name := 'Rick';
emp_rec.emp_gender := 'Male';

emp_rec_array(2) := emp_rec;

FOR i IN 1..emp_rec_array.count LOOP
DBMS_OUTPUT.PUT_LINE('i='||i
||', emp_id ='||emp_rec_array(i).emp_id
||', emp_name ='||emp_rec_array(i).emp_name
||', emp_gender = '||emp_rec_array(i).emp_gender);
END LOOP;

END;
-------------- Result --------------
i=1, emp_id =300000000, emp_name =Barbara, emp_gender = Female
i=2, emp_id =300000008, emp_name =Rick, emp_gender = Male

posted @ 2009-01-13 21:07 依然Fantasy 阅读(448) | 评论 (0)编辑 收藏

Oracle PL/SQL中如何使用%TYPE和%ROWTYPE (转载)
1. 使用%TYPE
在许多情况下,PL/SQL变量可以用来存储在数据库表中的数据。在这种情况下,变量应该拥有与表列相同的类型。例如,students表的first_name列的类型为VARCHAR2(20),我们可以按照下述方式声明一个变量:
DECLARE
v_FirstName VARCHAR2(20);
但是如果first_name列的定义改变了会发生什么(比如说表改变了,first_name现在的类型变为VARCHAR2(25))?那就会导致所有使用这个列的PL/SQL代码都必须进行修改。如果你有很多的PL/SQL代码,这种处理可能是十分耗时和容易出错的。
这时,你可以使用"%TYPE"属性而不是将变量类型硬性编码。
例如:
DECLARE
v_FirstName students.first_name%TYPE;
通过使用%TYPE,v_FirstName变量将同students表的first_name列的类型相同(可以理解为将两者邦定起来)。
每次匿名块或命名块运行该语句块以及编译存储对象(过程、函数、包、对象类和触发器)时,就会确定该类型。
使用%TYPE是非常好的编程风格,因为它使得PL/SQL更加灵活,更加适应于对数据库定义的更新。
2. 使用%ROWTYPE
2.1 PL/SQL记录
PL/SQL记录类型类似于C语言中的结构,是一种复合类型,是用户自定义的。
记录提供了一种处理独立的但又作为一个整体单元相关的变量的机制。请看:
DECLARE
v_StudentID NUMBER(5);
v_FirstName VARCHAR2(20);
v_LastName VARCHAR2(20);
这3个变量在逻辑上是相互关联的,因为他们指向students表中不同的字段。如果为这些变量声明一个记录类型,那么他们之间的关系就十分明显,可作为一个单元进行处理。
DECLARE
/*Define a record type to hold common student informationi*/
TYPE t_StudentRecord IS RECORD(
StudentID NUMBER(5),
FirstName VARCHAR2(20),
LastName VARCHAR2(20);
/*Declare a variable of this type.*/
v_StudentInfo t_StudentRecord;
2.2 记录赋值
可以用SELECT语句向记录赋值,这将会从数据库中检索数据并将该数据存储到记录中。注意的是,记录中字段应该和查询结果列表中的字段相匹配。
SELECT studentID,firstName,lastName
into v_StudentInfo
from students where studentID=32;
2.3 使用%ROWTYPE
在PL/SQL中将一个记录声明为具有相同类型的数据库行的作法是很常见的。PL/SQL提供了%ROWTYPE运算符,使得这样的操作更为方便。
例如:
DECLARE
v_RoomRecord rooms%ROWTYPE;
将定义一个记录,该记录中的字段将与rooms表中的列相对应。

posted @ 2009-01-07 08:38 依然Fantasy 阅读(1487) | 评论 (2)编辑 收藏

  周5手机不幸被盗,可恶的小偷,偷我的手机你就断手断脚吧。明天准备联系索爱和移动客服看是否能通过手机串码把手机屏蔽,据说从技术角度讲是可以行的通的,问题就是这种事手机运营商愿不愿意给做了。英国已经有这种服务可以使被盗手机不能再被使用,不知道特色中国啥时能有这种真正特色的服务呢。

       我的索爱W810听音乐音质超好,入耳式耳机也很棒,感觉比专业的魅族MP3还高一筹(主要魅族带的耳机太烂,而且魅族音质风格有点假,特别配上自带耳机后听音乐塑料感极强)。w810音质风格基本上就是SONY早年CDWalkMan那种日系风格,加上入耳式耳机低音很强。另外FM收音功能也很不错,信号很好,这个我也是和魅族的MP3对比过的,同样环境下FM收音信号也强一些,相对由于信号弱造成的背景噪音小一些,加上声音渲染修饰的好,不会有由于信号不好造成的刺耳声。W810的拍照功能也不错,拍的照片,特别是白天光线好时,拍出来的照片基本上可以冒充数码相机的了。总之,用了一年,感觉W810最大缺点就是短信有容量限制,我基本上半个月就得清一回收件箱。个人认为w810作为手机来用是比不上NOKIA的好用,但是随身听、拍照功能的确很好,估计索爱的W系列基本也都是这样。哎可惜现在连个尸体都没留下..怀念呀.....

w810

w810

  平时忙没时间逛商店,周6到滨江道溜达了一圈,据卖手机的服务员说,天语手机销量已经能和NOKIA有的一拼了,想想一年多前还是名不见经传,世界变化快呀。由于之前在淘宝上看过价格了,所以无论哪款手机门店里的价格基本上没法接受了。现在消费观念已不同从前了(有可能被全球经济危机吓得呵呵~),基本上2000元以上的手机就不看了,找了几款什么索爱M600i880i以及夏新E78...最后还是回发现干麻不买个NOKIA智能机的呢,我在W810前就是用的NOKIA6630呀,当时感觉智能机很强大很DIY。最后发现淘宝上N72行货价格已经掉到1300左右(刚出时得有三四千了),还给开发票和全国联保,OMG超值,就是它了..... 

  没想到那个卖家别看信用值不高,东西相对便宜,服务态度到是超好,基本上算是送了个1GB卡,周日拍周一晚上就收到货了,一看包装写着是空运...东西也不错,开封后机器没有任何问题,还有正规发票。有的信用值不高的卖家估计是想薄利多销招揽客户吧,不像有的几个钻或者是大皇冠的卖家,买个东西好像还不是很热情的样子,也有可能是客户多忙不过来了。

       说说N72,基实和我在w810之前用过的6630没啥太多区别,都是NOKIA S60系统,就是外形小了一点,薄了一点点,内存大了些,这回是1GB存储卡、摄像头由130万变成200万,多了FM收音功能,操作系统版本新了些。我基本上当它是新版6630用,呵呵~

  值得一提的是,NOKIA的耳机一般音质像地摊货,这回的也一样,打开包装原配的耳机就扔到了一边,我用NOKIA的AD-15转换器(一种NOKIA专用耳机转接器,外加功率放大功能的小东西)接上我的森海塞耳PX200,呵呵~~音质超爽,几乎已超越了W810的音质,但是另一种风格不像是日系随身听低音很闷的那种。但是很奇怪如果接SonyE888音质就不怎么地,费解中..。自此以后又可以下载S60各种应用软件、游戏、MP3播放软件....重回NOKIA的智能DIY世界......也许哪天花几百搞个蓝牙GPS定位器,装个地图软件,就可以实现手机GPS定位了。

       n72 6630

      n72                                                          6630

posted @ 2008-10-13 22:59 依然Fantasy 阅读(397) | 评论 (0)编辑 收藏

  用一个已不写程序的朋友的话说,现在Java世界里真是让人眼花缭乱,不仅对于新手,就算是过去熟悉Servlet、JSP,EJB的programmer,估计要完全搞明白现在的‘新生态架构’也不是很容易。从N多年前的‘一次编译到处运行’到后来的EJB,再到现在的Spring、Hibernate、Webwork、JSF等诸多表现层、数据层,以及支持MVC、AOP的框架,再加上JDK1.5后加入的泛型等新功能,如果是一直以Servlet,JSP、JDBC或是EJB开发的话,现在突然接触到这些东西真是要学习一阵了。最近在看《越狱》,想起一个并不恰当的比喻,好似一个服刑30年的老家伙,有一天终于刑满释放,结果出来后却发现外面的世界早已不是他所想的那样了。。。

  还好我们对于这些变化早有准备,最快的学习方法莫过于直接针对一个系统进行源码分析、学习、剥离出其中用到的技术方面,然后尝试用于我们自已的项目或产品中去。用google、baidu很快就把焦点定位在了一个开源的网上社区http://www.laoer.com/ 即天乙社区,我们关心的是技术架构,它用的是Struts+Spring+Hibernate,struts1.x版本虽然比起JSF、Tapestry、Webwork以及Webwork和Struts合并的Struts2.x,strtus1.x并不是很先进,但这套源码的成熟度和这个架构的使用率一定很高,N多项目都在用这种架构,况且3者结合其中必有玄机,还是有的学了:)
  
  粗看了一下这套系统的架构以及源码,大约理了一下思路,打算根据源码中用到的技术,按几个方面去研究,围绕社区系统中的应用,再写几篇文章就当是一种成果吧。
        主要有以下几方面:
  Struts与Spring集成应用
         Hibernate与Spring的集成应用
        OSCache的应用(这里主要用于缓存POJO)
        Intecepter即拦截器的应用
        Ajax的应用以及Prototype
        。。。。

       

posted @ 2007-03-13 16:09 依然Fantasy 阅读(1835) | 评论 (3)编辑 收藏
仅列出标题