本文链接: http://blog.csdn.net/jrq/archive/2005/10/27/517428.aspx
正文:
1. 为了方便按列作外循环,想把ArrayList构造成一个二维数组,如下:
......
ArrayList result=GetResult();
int n=result.size();
String[][] myArray=new String[n][]; //定义二维数组
for (int i=0;i<n;i++) //构造二维数组
{
ArrayList tempArray= (ArrayList)result.get(i);
myArray[i]=(String[])tempArray.toArray();
}
......
程序可以编译通过。
但在运行到myArray[i]=(String[])tempArray.toArray()时,出现java.lang.ClassCastException的错误,很奇怪。
花了一晚上时间,查了N多资料,总算搞定了。现把问题记录下来,以备参考。
2. 此事从头说起。
ArrayList类扩展AbstractList并执行List接口。ArrayList支持可随需要而增长的动态数组。
ArrayList有如下的构造函数:
ArrayList( )
ArrayList(Collection c)
ArrayList(int capacity)
如果调用new ArrayList()构造时,其默认的capacity(初始容量)为10。
参见ArrayList源码,其中是这样定义的:
public ArrayList() {
this(10);
}
默认初始化内部数组大小为10。为什么是10?不知道。可能SUN觉得这样比较爽吧。
程序编译后执行ArrayList.toArray(),把ArrayList转化为数组时,该数组大小仍为capacity(为10)。
当 装入的数据和capacity值不等时(小于capacity),比如只装入了5个数据,数组中后面的(capacity - size)个对象将置为null,此时当数组强制类型转换时,容易出现一些问题,如java.lang.ClassCastException异常等。
解决办法是:在用ArrayList转化为数组装数据后,使用trimToSize()重新设置数组的真实大小。
3. 本例修改后的代码修如下,可顺利运行:
for (int i=0;i<n;i++) //构造二维数组
{
ArrayList tempArray= (ArrayList)result.get(i);
myArray[i]=(String[])tempArray.toArray(new String[0]); //注意此处的写法
}
看看下面这些也许就明白了--
ArrayList.toArray()之一:
public Object[] toArray() {
Object[] result = new Object[size];
System.arraycopy(elementData, 0, result, 0, size);
return result;
}
返回ArrayList元素的一个数组,注意这里虽然生成了一个新的数组,但是数组元素和集合中的元素是共享的,Collection接口中说这个是安全的,这是不严格的。
下面的例子演示了这个效果。
ArrayList al=new ArrayList();
al.add(new StringBuffer("hello"));
Object[] a=al.toArray();
StringBuffer sb=(StringBuffer)a[0];
sb.append("changed"); //改变数组元素同样也改变了原来的ArrayList中的元素
System.out.println(al.get(0));
这里不要用String来代替StringBuffer,因为String是不可变的。
ArrayList.toArray()之二:
public Object[] toArray(Object a[]) {
if (a.length < size)
a = (Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
这个方法有可能不需要生成新的数组,注意到如果数组a容量过大,只在size处设置为null。
如果这个数组a足够大,就会把数据全放进去,返回的数组也是指向这个数组,(数组多余的空间存储的是null对象);要是不够大,就申请一个跟参数同样类型的数组,把值放进去,然后返回。
4. 网上的资料一:
public String[] getPlatformIDList()
{
Vector result = new Vector();
try
{
Statement stmt = conn.createStatement();
String sql = "SELECT PlatformID FROM Platform";
rs = stmt.executeQuery(sql);
while(rs.next())
{
result.add(rs.getString(1));
}
if (result.size() > 0)
{
String[] str = (String[]) result.toArray(); // 出现ClassCastException
return str;
}
else
return null;
}
catch(Exception e)
{
System.err.println(e);
return null;
}
finally
{
try
{
rs.close();
conn.close();
}
catch(Exception e2)
{}
}
}
程序运行后,发现不能将Vector类经过toArray()方法得到的Object[]直接转换成String[]。
找到用另一个带有参数的 toArray(T[] a)方法才可以。
将该语句改为:
String[] str = (String[]) result.toArray(new String[1]);即告诉Vector,我要得到的数组的类型。
回想一下,应该是java中的强制类型转换只是针对单个对象的,想要偷懒,将整个数组转换成另外一种类型的数组是不行的。
5. 网上的资料二:
正确使用List.toArray()--
在程序中,往往得到一个List,程序要求对应赋值给一个array,可以这样写程序:
Long [] l = new Long[list.size()];
for(int i=0;i<list.size();i++)
l[i] = (Long) list.get(i);
要写这些code,似乎比较繁琐,其实List提供了toArray()的方法。
但是要使用不好,就会有ClassCastExceptiony异常。究竟这个是如何产生的,且看代码:
List list = new ArrayList();
list.add(new Long(1));list.add(new Long(2));
list.add(new Long(3));list.add(new Long(4));
Long[] l = (Long[])list.toArray();
for(int i=0; i<l.length; i++)
System.out.println(l[i].longValue());
红色代码会抛java.lang.ClassCastException。
当然,为了读出值来,你可以这样code:
Object [] a = list.toArray();
for(int i=0;i<a.length;i++)
System.out.println(((Long)a[i]).longValue());
但是让数组丢失了类型信息,这个不是我们想要得。
toArray()正确使用方式如下:
1) Long[] l = new Long[<total size>];
list.toArray(l);
2) Long[] l = (Long []) list.toArray(new Long[0]);
3) Long [] a = new Long[<total size>];
Long [] l = (Long []) list.toArray(a);
6. 总结补充:
java sdk doc 上讲:
public Object[] toArray(Object[] a)
a--the array into which the elements of this list are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
如果这个数组a足够大,就会把数据全放进去,返回的数组也是指向这个数组,(数组多余的空间存储的是null对象);要是不够大,就申请一个跟参数同样类型的数组,把值放进去,然后返回。
需要注意的是:你要是传入的参数为9个大小,而list里面有5个object,那么其他的四个很可能是null , 使用的时候要特别注意。
7. 完毕。
J.R.Q.
2005.10.27凌晨于广州
posted @
2010-06-25 00:49 阿伟 阅读(1200) |
评论 (0) |
编辑 收藏
之前看网上有人发帖说有个解决办法是“在viewservlet.jar下找到org/eclipse/birt/report/utility下找到ParameterAccessor.class文件,现在要修改这个文件的源代码,
你可以在你从CVS中签出的源代码中的org.eclipse.birt.report.viewer/birt/WEB-INF/classes/org/eclipse/birt/report/utility文件夹中找到ParameterAccessor.java文件,在这个java文件中找到这行:
public static final String UTF_8_ENCODE = "UTF-8"; //$NON-NLS-1$
将这行改为
public static final String UTF_8_ENCODE = "GBK"; //$NON-NLS-1$
然后将这个编译好的class文件压缩到viewservlets.jar的同等目录下,替换原来的文件”,本人也试了这种方法,但没见凑效,不过上面的解决方法好像是在birt2.1的环境下,这个没仔细试,也许对birt2.1有效吧。
本人使用birt版本2.3,解决办法如下:
在JSP中调用BIRT报表时,传的参数中有中文字符,即URL中包含中文字符,这时在BIRT端接收到的参数拼SQL语句后发现中文字符被转码,造成SQL中的条件不正确。
因为在JSP中调用BIRT是在一个JS脚本中,因此要想办法在JS中先将中文转成UTF-8,并且在BIRT获取参数后再将UTF-8转换回中文即可。
实现代码:
页面JS:
Var mychecktype = “中文”;
sqlWhere+="and t.CHECK_TYPE = '"+escape(encodeURI(mychecktype))+"'";
BIRT脚本:
sql+=decodeURI(mycondition);
posted @
2009-07-02 09:56 阿伟 阅读(2524) |
评论 (1) |
编辑 收藏
问题:
页面中写了一个函数a,在另一个函数中调用,页面用的是一个iframe框架,即通过iframe的src参数来刷新iframe中的页面,每次在第一次调用函数a时没问题,但第二次点击按钮调用函数a时老报“缺少函数”的错误?
function a(var1)
{
...
return a;
}
第一次 alert(a(var1)) 沒有問題,第二次就出錯了
解决:
后来发现函数中返回值变量名和函数名定义为同一个名字,把返回值变量名改成其它名字后问题就解决了,好弱的一个问题啊,折腾了我半天,气愤!
改成 return b 就好了 -_-
posted @
2008-11-27 14:58 阿伟 阅读(2254) |
评论 (0) |
编辑 收藏
经常遇到备份了一个几十M的SQL文件,还原时无法执行或执行一半就挂了,好像就是因为太大了,本来用MYSQL 的Administrator工具
还原备份会方便点,但有时也会出现无法还原的问题,不知如何是好,最后只能想到在命令窗口来执行SQL文件,好像还挺管用,具
体执行命令如下:
先进入cmd命令行;
然后进入mysql命令行;
>mysql -u admin -p
>*****
>use dbname
mysql>source %pass%\mySQLFile.sql(这里的%pass%指的就是SQL文件所在的路径,如F:/date/mysqlDB.sql,这里路径尽量别用中
文吧,像安装ORACLE一样,中文路径可能会出现问题,用英文保险一点^_^)
posted @
2008-09-13 00:23 阿伟 阅读(527) |
评论 (0) |
编辑 收藏
转自:http://www.blogjava.net/freeman1984/archive/2007/09/27/148850.html
六种异常处理的陋习
你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗?
1 OutputStreamWriter out = ...
2 java.sql.Connection conn = ...
3 try { // ⑸
4 Statement stat = conn.createStatement();
5 ResultSet rs = stat.executeQuery(
6 "select uid, name from user");
7 while (rs.next())
8 {
9 out.println("ID:" + rs.getString("uid") // ⑹
10 ",姓名:" + rs.getString("name"));
11 }
12 conn.close(); // ⑶
13 out.close();
14 }
15 catch(Exception ex) // ⑵
16 {
17 ex.printStackTrace(); //⑴,⑷
18 } |
作为一个Java程序员,你至少应该能够找出两个问题。但是,如果你不能找出全部六个问题,请继续阅读本文。
本文讨论的不是Java异常处理的一般性原则,因为这些原则已经被大多数人熟知。我们要做的是分析各种可称为“反例”(anti-pattern)的违背优秀编码规范的常见坏习惯,帮助读者熟悉这些典型的反面例子,从而能够在实际工作中敏锐地察觉和避免这些问题。
反例之一:丢弃异常
代码:15行-18行。
这段代码捕获了异常却不作任何处理,可以算得上Java编程中的杀手。从问题出现的频繁程度和祸害程度来看,它也许可以和C/C++程序的一个恶名远播的问题相提并论??不检查缓冲区是否已满。如果你看到了这种丢弃(而不是抛出)异常的情况,可以百分之九十九地肯定代码存在问题(在极少数情况下,这段代码有存在的理由,但最好加上完整的注释,以免引起别人误解)。
这段代码的错误在于,异常(几乎)总是意味着某些事情不对劲了,或者说至少发生了某些不寻常的事情,我们不应该对程序发出的求救信号保持沉默和无动于衷。调用一下printStackTrace算不上“处理异常”。不错,调用printStackTrace对调试程序有帮助,但程序调试阶段结束之后,printStackTrace就不应再在异常处理模块中担负主要责任了。
丢弃异常的情形非常普遍。打开JDK的ThreadDeath类的文档,可以看到下面这段说明:“特别地,虽然出现ThreadDeath是一种‘正常的情形’,但ThreadDeath类是Error而不是Exception的子类,因为许多应用会捕获所有的Exception然后丢弃它不再理睬。”这段话的意思是,虽然ThreadDeath代表的是一种普通的问题,但鉴于许多应用会试图捕获所有异常然后不予以适当的处理,所以JDK把ThreadDeath定义成了Error的子类,因为Error类代表的是一般的应用不应该去捕获的严重问题。可见,丢弃异常这一坏习惯是如此常见,它甚至已经影响到了Java本身的设计。
那么,应该怎样改正呢?主要有四个选择:
1、处理异常。针对该异常采取一些行动,例如修正问题、提醒某个人或进行其他一些处理,要根据具体的情形确定应该采取的动作。再次说明,调用printStackTrace算不上已经“处理好了异常”。
2、重新抛出异常。处理异常的代码在分析异常之后,认为自己不能处理它,重新抛出异常也不失为一种选择。
3、把该异常转换成另一种异常。大多数情况下,这是指把一个低级的异常转换成应用级的异常(其含义更容易被用户了解的异常)。
4、不要捕获异常。
结论一:既然捕获了异常,就要对它进行适当的处理。不要捕获异常之后又把它丢弃,不予理睬。
反例之二:不指定具体的异常
代码:15行。
许多时候人们会被这样一种“美妙的”想法吸引:用一个catch语句捕获所有的异常。最常见的情形就是使用catch(Exception ex)语句。但实际上,在绝大多数情况下,这种做法不值得提倡。为什么呢?
要理解其原因,我们必须回顾一下catch语句的用途。catch语句表示我们预期会出现某种异常,而且希望能够处理该异常。异常类的作用就是告诉Java编译器我们想要处理的是哪一种异常。由于绝大多数异常都直接或间接从java.lang.Exception派生,catch(Exception ex)就相当于说我们想要处理几乎所有的异常。
再来看看前面的代码例子。我们真正想要捕获的异常是什么呢?最明显的一个是SQLException,这是JDBC操作中常见的异常。另一个可能的异常是IOException,因为它要操作OutputStreamWriter。显然,在同一个catch块中处理这两种截然不同的异常是不合适的。如果用两个catch块分别捕获SQLException和IOException就要好多了。这就是说,catch语句应当尽量指定具体的异常类型,而不应该指定涵盖范围太广的Exception类。
另一方面,除了这两个特定的异常,还有其他许多异常也可能出现。例如,如果由于某种原因,executeQuery返回了null,该怎么办?答案是让它们继续抛出,即不必捕获也不必处理。实际上,我们不能也不应该去捕获可能出现的所有异常,程序的其他地方还有捕获异常的机会??直至最后由JVM处理。
结论二:在catch语句中尽可能指定具体的异常类型,必要时使用多个catch。不要试图处理所有可能出现的异常。
反例之三:占用资源不释放
代码:3行-14行。
异常改变了程序正常的执行流程。这个道理虽然简单,却常常被人们忽视。如果程序用到了文件、Socket、JDBC连接之类的资源,即使遇到了异常,也要正确释放占用的资源。为此,Java提供了一个简化这类操作的关键词finally。
finally是样好东西:不管是否出现了异常,Finally保证在try/catch/finally块结束之前,执行清理任务的代码总是有机会执行。遗憾的是有些人却不习惯使用finally。
当然,编写finally块应当多加小心,特别是要注意在finally块之内抛出的异常??这是执行清理任务的最后机会,尽量不要再有难以处理的错误。
结论三:保证所有资源都被正确释放。充分运用finally关键词。
反例之四:不说明异常的详细信息
代码:3行-18行。
仔细观察这段代码:如果循环内部出现了异常,会发生什么事情?我们可以得到足够的信息判断循环内部出错的原因吗?不能。我们只能知道当前正在处理的类发生了某种错误,但却不能获得任何信息判断导致当前错误的原因。
printStackTrace的堆栈跟踪功能显示出程序运行到当前类的执行流程,但只提供了一些最基本的信息,未能说明实际导致错误的原因,同时也不易解读。
因此,在出现异常时,最好能够提供一些文字信息,例如当前正在执行的类、方法和其他状态信息,包括以一种更适合阅读的方式整理和组织printStackTrace提供的信息。
结论四:在异常处理模块中提供适量的错误原因信息,组织错误信息使其易于理解和阅读。
反例之五:过于庞大的try块
代码:3行-14行。
经常可以看到有人把大量的代码放入单个try块,实际上这不是好习惯。这种现象之所以常见,原因就在于有些人图省事,不愿花时间分析一大块代码中哪几行代码会抛出异常、异常的具体类型是什么。把大量的语句装入单个巨大的try块就象是出门旅游时把所有日常用品塞入一个大箱子,虽然东西是带上了,但要找出来可不容易。
一些新手常常把大量的代码放入单个try块,然后再在catch语句中声明Exception,而不是分离各个可能出现异常的段落并分别捕获其异常。这种做法为分析程序抛出异常的原因带来了困难,因为一大段代码中有太多的地方可能抛出Exception。
结论五:尽量减小try块的体积。
反例之六:输出数据不完整
代码:7行-11行。
不完整的数据是Java程序的隐形杀手。仔细观察这段代码,考虑一下如果循环的中间抛出了异常,会发生什么事情。循环的执行当然是要被打断的,其次,catch块会执行??就这些,再也没有其他动作了。已经输出的数据怎么办?使用这些数据的人或设备将收到一份不完整的(因而也是错误的)数据,却得不到任何有关这份数据是否完整的提示。对于有些系统来说,数据不完整可能比系统停止运行带来更大的损失。
较为理想的处置办法是向输出设备写一些信息,声明数据的不完整性;另一种可能有效的办法是,先缓冲要输出的数据,准备好全部数据之后再一次性输出。
结论六:全面考虑可能出现的异常以及这些异常对执行流程的影响。
改写后的代码
根据上面的讨论,下面给出改写后的代码。也许有人会说它稍微有点?嗦,但是它有了比较完备的异常处理机制。
OutputStreamWriter out = ...
java.sql.Connection conn = ...
try {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(
"select uid, name from user");
while (rs.next())
{
out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name"));
}
}
catch(SQLException sqlex)
{
out.println("警告:数据不完整");
throw new ApplicationException("读取数据时出现SQL错误", sqlex);
}
catch(IOException ioex)
{
throw new ApplicationException("写入数据时出现IO错误", ioex);
}
finally
{
if (conn != null) {
try {
conn.close();
}
catch(SQLException sqlex2)
{
System.err(this.getClass().getName() + ".mymethod - 不能关闭数据库连接: " + sqlex2.toString());
}
}
if (out != null) {
try {
out.close();
}
catch(IOException ioex2)
{
System.err(this.getClass().getName() + ".mymethod - 不能关闭输出文件" + ioex2.toString());
}
}
} |
本文的结论不是放之四海皆准的教条,有时常识和经验才是最好的老师。如果你对自己的做法没有百分之百的信心,务必加上详细、全面的注释。
另一方面,不要笑话这些错误,不妨问问你自己是否真地彻底摆脱了这些坏习惯。即使最有经验的程序员偶尔也会误入歧途,原因很简单,因为它们确确实实带来了“方便”。所有这些反例都可以看作Java编程世界的恶魔,它们美丽动人,无孔不入,时刻诱惑着你。也许有人会认为这些都属于鸡皮蒜毛的小事,不足挂齿,但请记住:勿以恶小而为之,勿以善小而不为。
------------------------------------------------------------------下面是一些java异常集-------------------------------------------------------------------------------------------
算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
java.lang.AbstractMethodError
抽象方法错误。当应用试图调用抽象方法时抛出。
java.lang.AssertionError
断言错。用来指示一个断言失败的情况。
java.lang.ClassCircularityError
类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。
java.lang.ClassFormatError
类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。
java.lang.Error
错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。
java.lang.ExceptionInInitializerError
初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。
java.lang.IllegalAccessError
违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。
java.lang.IncompatibleClassChangeError
不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。
java.lang.InstantiationError
实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.
java.lang.InternalError
内部错误。用于指示Java虚拟机发生了内部错误。
java.lang.LinkageError
链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。
java.lang.NoClassDefFoundError
未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。
java.lang.NoSuchFieldError
域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。
java.lang.NoSuchMethodError
方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。
java.lang.OutOfMemoryError
内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
java.lang.StackOverflowError
堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。
java.lang.ThreadDeath
线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。
java.lang.UnknownError
未知错误。用于指示Java虚拟机发生了未知严重错误的情况。
java.lang.UnsatisfiedLinkError
未满足的链接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。
java.lang.UnsupportedClassVersionError
不支持的类版本错误。当Java虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。
java.lang.VerifyError
验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。
java.lang.VirtualMachineError
虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。
java.lang.ArithmeticException
算术条件异常。譬如:整数除零等。
java.lang.ArrayIndexOutOfBoundsException
数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
java.lang.ArrayStoreException
数组存储异常。当向数组中存放非数组声明类型对象时抛出。
java.lang.ClassCastException
类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。
java.lang.ClassNotFoundException
找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
java.lang.CloneNotSupportedException
不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。
java.lang.EnumConstantNotPresentException
枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象并不包含常量时,抛出该异常。
java.lang.Exception
根异常。用以描述应用程序希望捕获的情况。
java.lang.IllegalAccessException
违法的访问异常。当应用试图通过反射方式创建某个类的实例、访问该类属性、调用该类方法,而当时又无法访问类的、属性的、方法的或构造方法的定义时抛出该异常。
java.lang.IllegalMonitorStateException
违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。
java.lang.IllegalStateException
违法的状态异常。当在Java环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。
java.lang.IllegalThreadStateException
违法的线程状态异常。当县城尚未处于某个方法的合法调用状态,而调用了该方法时,抛出异常。
java.lang.IndexOutOfBoundsException
索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。
java.lang.InstantiationException
实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。
java.lang.InterruptedException
被中止异常。当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Thread的interrupt方法终止该线程时抛出该异常。
java.lang.NegativeArraySizeException
数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。
java.lang.NoSuchFieldException
属性不存在异常。当访问某个类的不存在的属性时抛出该异常。
java.lang.NoSuchMethodException
方法不存在异常。当访问某个类的不存在的方法时抛出该异常。
java.lang.NullPointerException
空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。
java.lang.NumberFormatException
数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。
java.lang.RuntimeException
运行时异常。是所有Java虚拟机正常操作期间可以被抛出的异常的父类。
java.lang.SecurityException
安全异常。由安全管理器抛出,用于指示违反安全情况的异常。
java.lang.StringIndexOutOfBoundsException
字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。
java.lang.TypeNotPresentException
类型不存在异常。当应用试图以某个类型名称的字符串表达方式访问该类型,但是根据给定的名称又找不到该类型是抛出该异常。该异常与ClassNotFoundException的区别在于该异常是unchecked(不被检查)异常,而ClassNotFoundException是checked(被检查)异常。
java.lang.UnsupportedOperationException
不支持的方法异常。指明请求的方法不被支持情况的异常。
异常
javax.servlet.jsp.JspException: Cannot retrieve mapping for action /Login (/Login是你的action名字)
可能原因
action没有再struts-config.xml 中定义,或没有找到匹配的action,例如在JSP文件中使用 <html:form action="Login.do".将表单提交给Login.do处理,如果出现上述异常,请查看struts-config.xml中的定义部分,有时可能是打错了字符或者是某些不符合规则,可以使用strutsconsole工具来检查。
-----------------------------------------------------------------------------------------------------------------
异常
org.apache.jasper.JasperException: Cannot retrieve definition for form bean null
可能原因
这个异常是因为Struts根据struts-config.xml中的mapping没有找到action期望的form bean。大部分的情况可能是因为在form-bean中设置的name属性和action中设置的name属性不匹配所致。换句话说,action和form都应该各自有一个name属性,并且要精确匹配,包括大小写。这个错误当没有name属性和action关联时也会发生,如果没有在action中指定name属性,那么就没有name属性和action相关联。当然当action制作某些控制时,譬如根据参数值跳转到相应的jsp页面,而不是处理表单数据,这是就不用name属性,这也是action的使用方法之一。
-----------------------------------------------------------------------------------------------------------------
异常
No action instance for path /xxxx could be created
可能原因
特别提示:因为有很多中情况会导致这个错误的发生,所以推荐大家调高你的web服务器的日志/调试级别,这样可以从更多的信息中看到潜在的、在试图创建action类时发生的错误,这个action类你已经在struts-config.xml中设置了关联(即添加了<action>标签)。
在struts-config.xml中通过action标签的class属性指定的action类不能被找到有很多种原因,例如:定位编译后的.class文件失败。Failure to place compiled .class file for the action in the classpath (在web开发中,class的的位置在r WEB-INF/classes,所以你的action class必须要在这个目录下。例如你的action类位于WEB-INF/classes/action/Login.class,那么在struts-config.xml中设置action的属性type时就是action.Login).
拼写错误,这个也时有发生,并且不易找到,特别注意第一个字母的大小写和包的名称。
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: No getter method for property username of bean org.apache.struts.taglib.html.BEAN
可能原因
没有位form bean中的某个变量定义getter 方法
这个错误主要发生在表单提交的FormBean中,用struts标记<html:text property=”username”>时,在FormBean中必须有一个getUsername()方法。注意字母“U”。
-----------------------------------------------------------------------------------------------------------------
异常
java.lang.NoClassDefFoundError: org/apache/struts/action/ActionForm
可能原因
这个错误主要发生在在classpath中找不到相应的Java .class文件。如果这个错误发生在web应用程序的运行时,主要是因为指定的class文件不在web server的classpath中(/WEB-INF/classes 和 /WEB-INF/lib)。在上面的错误中,原因是找不到ActionForm类。
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Exception creating bean of class org.apache.struts.action.ActionForm: {1}
可能原因
Instantiating Struts-provided ActionForm class directly instead of instantiating a class derived off ActionForm. This mightoccur implicitly if you specify that a form-bean is this Struts ActionForm class rather than specifying a child of this classfor the form-bean.
Not associating an ActionForm-descended class with an action can also lead to this error.
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Cannot find ActionMappings or ActionFormBeans collection
可能原因
不是标识Struts actionServlet的<servlet>标记就是映射.do扩展名的<sevlet-mapping>标记或者两者都没有在web.xml中声明。
在struts-config.xml中的打字或者拼写错误也可导致这个异常的发生。例如缺少一个标记的关闭符号/>。最好使用struts console工具检查一下。
另外,load-on-startup必须在web.xml中声明,这要么是一个空标记,要么指定一个数值,这个数值用来表servlet运行的优先级,数值越大优先级越低。
还有一个和使用load-on-startup有关的是使用Struts预编译JSP文件时也可能导致这个异常。
-----------------------------------------------------------------------------------------------------------------
异常
java.lang.NullPointerException at org.apache.struts.util.RequestUtils.forwardURL(RequestUtils.java:1223)
可能原因
在struts-config.xml中的forward元素缺少path属性。例如应该是如下形式:
<forward name="userhome" path="/user/userhome.jsp"/>
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Cannot find bean org.apache.struts.taglib.html.BEAN in any scope
Probable Causes
试图在Struts的form标记外使用form的子元素。这常常发生在你在</html:form>后面使用Struts的html标记。另外要注意可能你不经意使用的无主体的标记,如<html:form … />,这样web 服务器解析时就当作一个无主体的标记,随后使用的所有<html>标记都被认为是在这个标记之外的,如又使用了<html:text property=”id”>还有就是在使用taglib引入HTML标记库时,你使用的prefix的值不是html。
-----------------------------------------------------------------------------------------------------------------
异常
javax.servlet.jsp.JspException: Missing message for key xx.xx.xx
Probable Causes
这个key的值对没有在资源文件ApplicationResources.properties中定义。如果你使用eclipse时经常碰到这样的情况,当项目重新编译时,eclipse会自动将classes目录下的资源文件删除。
资源文件ApplicationResources.properties 不在classpath中应将资源文件放到 WEB-INF/classes 目录下,当然要在struts-config.xml中定义)
-----------------------------------------------------------------------------------------------------------------
异常
Cannot find message resources under key org.apache.struts.action.MESSAGE
可能原因
很显然,这个错误是发生在使用资源文件时,而Struts没有找到资源文件。
Implicitly trying to use message resources that are not available (such as using empty html:options tag instead of specifyingthe options in its body -- this assumes options are specified in ApplicationResources.properties file)
XML parser issues -- too many, too few, incorrect/incompatible versions
-----------------------------------------------------------------------------------------------------------------
异常
Strange and seemingly random characters in HTML and on screen, but not in original JSP or servlet.
可能原因
混和使用Struts的html:form标记和标准的HTML标记不正确。
使用的编码样式在本页中不支持。
-----------------------------------------------------------------------------------------------------------------
异常
"Document contained no data" in Netscape
No data rendered (completely empty) page in Microsoft Internet Explorer
可能原因
使用一个Action的派生类而没有实现perform()方法或execute()方法。在Struts1.0中实现的是perform()方法,在Struts1.1中实现的是execute()方法,但Struts1.1向后兼容perform()方法。但你使用Struts1.1创建一个Action的派生类,并且实现了execute()方法,而你在Struts1.0中运行的话,就会得到"Document contained nodata" error message in Netscape or a completely empty (no HTML whatsoever) page rendered in Microsoft Internet Explorer.”的错误信息。
---------------------------------------------------------------------------------------------------------------------------
异常
ServletException: BeanUtils.populate
解决方案
在用Struts上传文件时,遇到了javax.servlet.ServletException: BeanUtils.populate异常。
我的ActionServlet并没有用到BeanUtils这些工具类。后来仔细检查代码发现是在jsp文件里的form忘了加enctype="multipart/form-data" 了。所以写程序遇到错误或异常应该从多方面考虑问题存在的可能性,想到系统提示信息以外的东西。
----------------------------------------------------------------------------------------------------------------------------
1. 定义Action后, 如果指定了name, 那么必须要定义一个与它同名的FormBean才能进行form映射.2. 如果定义Action后, 提交页面时出现 "No input attribute for mapping path..." 错误, 则需要在其input属性中定义转向的页面.3. 如果插入新的数据时出现 "Batch update row count wrong:..." 错误, 则说明XXX.hbm.xml中指定的key的类型为原始类型(int, long),因为这种类型会自动分配值, 而这个值往往会让系统认为已经存在该记录, 正确的方法是使用java.lang.Integer或java.lang.Long对象.4. 如果插入数据时出现 "argument type mismatch" 错误, 可能是你使用了Date等特殊对象, 因为struts不能自动从String型转换成Date型,所以, 你需要在Action中手动把String型转换成Date型.5. Hibernate中, Query的iterator()比list()方法快很多.6. 如果出现 "equal symbol expected" 错误, 说明你的strtus标签中包含另一个标签或者变量, 例如:
<html:select property="test" onchange="<%=test%>"/>
或者
<html:hidden property="test" value="<bean:write name="t" property="p"/>"/>
这样的情况...
---------------------------------------------------------------------------------------------------------------------------
错误:Exception in thread "main" org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update原因与解决: 因为Hibernate Tools(或者Eclipse本身的Database Explorer)生成*.hbn.xml工具中包含有catalog="***"(*表示数据库名称)这样的属性,将该属性删除就可以了
---------------------------------------------------------------------------------------------------------------------------
错误:org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
原因与解决:
方法1 删除Set方的cascade
方法2 解决关联关系后,再删除
方法3 在many-to-one方增加cascade 但值不能是none
最后一招:
检查一下hashCode equals是否使用了id作为唯一标示的选项了;我用uuid.hex时是没有问题的;但是用了native,就不行了,怎么办?删除啊!
----------------------------------------------------------------------------------------------------------------------------
问题:今天用Tomcat 5.5.12,发现原来很好用的系统不能用了,反复测试发现页面中不能包含 taglib,否则会出现以下提示:HTTP Status 500 -type Exception reportMessage description The server encountered an internal error () that prevented it from fulfilling this request.exceptionorg.apache.jasper.JasperException: /index.jsp(1,1) Unable to read TLD "META-INF/tlds/struts-bean.tld" from JAR file"file:*****/WEB-INF/lib/struts.jar":原因:更新了工程用的lib文件夹下的jar,发布时也发布了servlet.jar和jsp-api.jar。解决:把jsp-api.jar删除就解决这个问题了。-----------------------------------------------------------------------------------------------------------------------------
错误: java.lang.NullPointerException
原因: 发现 dao 实例、 manage 实例等需要注入的东西没有被注入(俗称空指针异常)解决:这个时候,你应该查看日志文件;默认是应用服务器的 log 文件,比如 Tomcat 就是 [Tomcat 安装目录 ]/logs ;你会发现提示你:可能是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sf' defined in ServletContextresource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xmlorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xml……………………….Caused by: java.io.FileNotFoundException: src\hibernate.cfg.xml可能是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined inServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not foundorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not found然后你就知道原因是因为配置文件的解析出了错误,这个通过 Web 页面是看不出来的。更多的是持久化影射文件出的错误;导致了没有被解析;当然你需要的功能就无法使用了。
----------------------------------------------------------------------------------------------------------------------------
错误:StandardWrapperValve[action]: Servlet.service() for servlet action threw exception
javax.servlet.jsp.JspException: Cannot retrieve mapping for action /settlementTypeManage
或者: type Status report message Servlet action is not available description The requested resource (Servlet action is not available) is not available.
原因: 同 上
----------------------------------------------------------------------------------------------------------------------------
错误StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exceptionjava.lang.ClassNotFoundException: org.apache.struts.taglib.bean.CookieTei界面错误具体描述:
org.apache.jasper.JasperException: Failed to load or instantiate TagExtraInfo class: org.apache.struts.taglib.bean.CookieTei
原因与解决: <方案一>你的“html:”开头的标签没有放在一个<html:form>中 <方案二>重新启动你的应用服务器,自动就没有这个问题
posted @
2008-08-26 10:06 阿伟 阅读(598) |
评论 (0) |
编辑 收藏
转自:http://freeman983.javaeye.com/blog/215073
创建型模式
1、FACTORY―追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory
工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。
2、BUILDER―MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)
建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。
3、FACTORY METHOD―请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。
工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
4、PROTOTYPE―跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)
原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。
5、SINGLETON―俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)
单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。
结构型模式
6、ADAPTER―在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)
适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。
7、BRIDGE―早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了
桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。
8、COMPOSITE―Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?
合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。
9、DECORATOR―Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?
装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。
10、FACADE―我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。
门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。
11、FLYWEIGHT―每天跟MM发短信,手指都累dead了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。
享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。
12、PROXY―跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。
代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
行为模式
13、CHAIN OF RESPONSIBLEITY―晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!
责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接
起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
14、COMMAND―俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”,
命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
15、INTERPRETER―俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。
解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合.
posted @
2008-08-18 22:17 阿伟 阅读(283) |
评论 (0) |
编辑 收藏
一:
使用Limit参数优化MySQL查询
转自:http://www.blogjava.net/chenpengyi/archive/2006/07/29/60679.html
我们在做一些查询的时候总希望能避免数据库引擎做全表扫描,因为全表扫描时间长,而且其中大部分扫描对客户端而言是没有意义的。那么在 MySQL 中有那些方式是可以避免全表扫面的呢?除了我们大家很熟悉的通过使用索引列或分区等方式来进行查询的优化之外还有那些呢?
前些天看了一个老外写的程序,在 MySQL 查询中使用了很多 Limit 关键字,这就让我很感兴趣了,因为在我印象中, Limit 关键字似乎更多被使用 MySQL 数据库的程序员用来做查询分页(当然这也是一种很好的查询优化),那在这里举个例子,假设我们需要一个分页的查询 ,Oracle中一般来说都是用以下 SQL 句子实现:
SELECT * FROM
( SELECT a1.*, rownum rownum_
FROM testtable a1
WHERE rownum > 20)
WHERE rownum_ <= 1000
这个语句就能查询到 testtable 表中的 20 到 1000 记录,而且还需要嵌套查询,效率不会太高,看看 MySQL 的实现:
SELECT * FROM testtable a1 limit 20,980;
这样就能返回 testtable 表中的 21 条到( 20 + 980 =) 1000 条的记录。
实现语法确实简单,但如果要说这里两个 SQL 语句的效率,那就很难做比较了,因为在 MySQL 中 Limit 选项有多种不同的解释方式,不同方式下的速度差异是很大的,因此我们不能从这语句的简洁程度就说谁的效率高。
不过对程序员来说,够简单就好,因为维护成本低,呵呵。
下面讲讲这个 Limit 的语法吧:
SELECT ……. --Select 语句的其他参数
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
这里 offset 是偏移量(这个偏移量的起始地址是 0 ,而不是 1 ,这点很容易搞错的)顾名思义就是离开起始点的位置,而 row-count 也是很简单的,就是返回的记录的数量限制。
Eg. SELECT * FROM testtable a limit 10,20 where ….
这样就能使结果返回 10 行以后(包括 10 行自身)的符合 where 条件的 20 条记录。
那么如果没有约束条件就返回 10 到 29 行的记录。
那这跟避免全表扫描有什么关系呢? 下面是 MySQL 手册对 Limit 参数优化扫描的一些说明:
在一些情况中,当你使用 LIMIT 选项而不是使用 HAVING 时, MySQL 将以不同方式处理查询。
l 如果你用 LIMIT 只选择其中一部分行,当 MySQL 一般会做完整的表扫描时,但在某些情况下会使用索引(跟 ipart 有关)。
l 如果你将 LIMIT n 与 ORDER BY 同时使用,在 MySQL 找到了第一个符合条件的记录后,将结束排序而不是排序整个表。
l 当 LIMIT n 和 DISTINCT 同时使用时, MySQL 在找到一个记录后将停止查询。
l 某些情况下, GROUP BY 能通过顺序读取键 ( 或在键上做排序 ) 来解决,并然后计算摘要直到键值改变。在这种情况下, LIMIT n 将不计算任何不必要的 GROUP 。
l 当 MySQL 完成发送第 n 行到客户端,它将放弃余下的查询。
l 而 LIMIT 0 选项总是快速返回一个空记录。这对检查查询并且得到结果列的列类型是有用的。
l 临时表的大小使用 LIMIT # 计算需要多少空间来解决查询。
二:
在MySQL查询结果集中得到记录行号(转自:
http://www.zeali.net/entry/452)
如果需要在查询语句返回的列中包含一列表示该条记录在整个结果集中的行号, ISO SQL:2003 标准提出的方法是提供 ROW_NUMBER() / RANK() 函数。 Oracle 中可以使用标准方法(8i版本以上),也可以使用非标准的 ROWNUM ; MS SQL Server 则在 2005 版本中提供了 ROW_NUMBER() 函数;但在 MySQL 中似乎还没有这样的系统自带功能。虽然 LIMIT 可以很方便的对返回的结果集数量和位置进行过滤,但过滤出来的记录的行号却没办法被 SELECT 到。据说 MySQL 是早就想增加这个功能了,但我是还没找到。
解决方法是通过预定义用户变量来实现:
set @mycnt = 0;
select (@mycnt := @mycnt + 1) as ROWNUM , othercol from tblname order by othercol;
这样查询出来的结果集中 ROWNUM 就保存了行编号信息。这个行编号信息的某种用途在于当你需要根据需要对数据按照某种规则排序并取出排序之后的某一行数据,并且希望知道这行数据在之前排序中的位置时就用得着了。比如:
set @mycnt = 0;
select * from (
select (@mycnt := @mycnt + 1) as ROWNUM , othercol
from tblname order by othercol
) as A where othercol=OneKeyID;
当然你也可以通过创建临时表的方法把查询结果写到某个拥有 auto_increment 字段的临时表中再做查询,但考虑到临时表在 MySQL master / slave 模式下可能产生的问题,用这样临时用户定义变量的方式来计算查询结果集每一行对应的行号还是更为简洁 -- 除非你愿意在 PHP 或其他语言脚本中对返回的整个结果集再作处理。
posted @
2008-06-02 16:26 阿伟 阅读(868) |
评论 (0) |
编辑 收藏
开发环境:
Eclipse3.2
MyEclipse5.5.1GA
JDK1.5
创建一个WEB Service工程XFire,按照网上的实例开发了一个简单的HelloWorld程序,发布到TOMCAT一切OK。
客户端用XFire插件开发(比较方便),需要先下载插件,步骤如下:
打开Eclipse的Help菜单,选择”Software Updates”,然后再选择”Find and Install.”
选择"Search for new features to install",然后点击Next
选择"Create New Remote Site", 在name中输入"XFire",在eclipse update site中输入
http://dist.codehaus.org/xfire/update/
选择OK
选择Finish。
2、新建一个java project。命名为“Client”,其他默认,finish。
3、File->New->Other ,选择“XFire”文件夹下的“Code generation from WSDL document”,打开代码生成向导,
WSDL的地址栏填入http://localhost:8080/XFire/services/HelloWorldService?wsdl,Output directory栏中点浏览按钮,选择我们刚才新建的项目Client,这两项是必填的。可选项中,package一栏可以选已经存在的包名,如果不填这一项,代码生成器会在wsdl目标命名空间的基础上创建一个。
完成后,可以看到项目中多了XFire类库,还有package下面的生成的一些类。此时要保证IDE的Tomcat服务器是打开的。
此时,在调用服务之前,还有一个重要的步骤,从XFireProject项目的右键菜单里调出Properties配置窗口,选中左面一栏中的XFire项,右面会列出所有与XFire运行有关的类库,按列表中所示的,选中一些类库,这些类库在调用本服务时是必须的。
Commons Codec(commons-codec-1.3.jar)
Commons HttpClient(commons-httpclient-3.0.jar)
如果缺少这两个JAR文件,客户端在调用WEB 服务时会出错。
所有的配置都已经完成,最后就是编写代码完成调用。
在Tomcat5.0下正常发布,客户端也能正常调用。然而在发布到WebSphere5.1(base)上面时,就出现了一些列问题,下面是问题简单描述(只截取了LOG中部分信息)以及解决过程。
[08-5-14 7:02:31:500 GMT] 2a29c7a6 XFireServlet E org.codehaus.xfire.transport.http.XFireServlet TRAS0014I: 下列异常已记录 org.springframework.beans.factory.BeanDefinitionStoreException: Error registering bean with name 'xfire.typeMappingRegistry' defined in class path resource [org/codehaus/xfire/spring/xfire.xml]: Class that bean class [org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry] depends on not found; nested exception is java.lang.NoClassDefFoundError: org/codehaus/xfire/aegis/type/DefaultTypeMappingRegistry
java.lang.NoClassDefFoundError: org/codehaus/xfire/aegis/type/DefaultTypeMappingRegistry
[08-5-14 7:02:31:531 GMT] 2a29c7a6 ServletInstan E SRVE0100E: 未识别出由 servlet XFireServlet 抛出的 init() 异常:javax.servlet.ServletException: Error initializing XFireServlet.
at org.codehaus.xfire.transport.http.XFireServlet.init(XFireServlet.java:52)
[08-5-14 7:02:31:531 GMT] 2a29c7a6 WebGroup E SRVE0020E: [Servlet 错误]-[XFireServlet]:无法装入 servlet:org.springframework.beans.factory.BeanDefinitionStoreException: Error registering bean with name 'xfire.typeMappingRegistry' defined in class path resource [org/codehaus/xfire/spring/xfire.xml]: Class that bean class [org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry] depends on not found; nested exception is java.lang.NoClassDefFoundError: org/codehaus/xfire/aegis/type/DefaultTypeMappingRegistry
java.lang.NoClassDefFoundError: org/codehaus/xfire/aegis/type/DefaultTypeMappingRegistry
[08-5-14 7:02:31:547 GMT] 2a29c7a6 WebGroup E SRVE0026E: [Servlet 错误]-[XFireServlet]:org.springframework.beans.factory.BeanDefinitionStoreException: Error registering bean with name 'xfire.typeMappingRegistry' defined in class path resource [org/codehaus/xfire/spring/xfire.xml]: Class that bean class [org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry] depends on not found; nested exception is java.lang.NoClassDefFoundError: org/codehaus/xfire/aegis/type/DefaultTypeMappingRegistry
java.lang.NoClassDefFoundError: org/codehaus/xfire/aegis/type/DefaultTypeMappingRegistry
上述问题出现后开始在网上大肆查找资料,中间替换过WebSphere中lib里面的几个包,报的错误虽然有所变化,但仍然不能清除。最后觉得是否与JDK版本有关,因为在WebSphere启动日志里看到WebSphere用的JDK版本是1.4.1,而我所开发的XFire工程是JDK1.5,因此决定把XFire工程用JDK包换成1.4然后用1.4版本重新编译,然后导出WAR包重新在WebSphere中发布。
先将WebSphere下的lib包恢复到原来状态(因为中间替换过几个包)重新发布1.4版本后,错误依旧,好吧,开始替换WebSphere下的包:
1、 先将WebSphere下的lib包中的qname.jar包替换为最新版本。
2、 将WebSphere下的lib包中的jdom.jar替换为jdom-1.0.jar.
3、 将WebSphere下的lib包中的wsdl4j.jar包替换为wsdl4j-1.5.1.jar(本来替换为xfire-1.2.6下lib里面的wsdl4j-1.6.1.jar,但替换完之后服务起不来了,其他版本的没试,在网上看到好像说wsdl4j-1.5.1解决了之前版本的什么BUG.)
4、 在WebSphere下的lib包中添加:stax-api-1.0.1.jar和jaxen-1.1-beta-9.jar两个包。(还有说需要添加stax-untils-20040917.jar,不过好像不添加也没问题。这些包在xfire-1.2.6的lib里面都有。)
重新启动服务器,我们终于可以看到盼望已久的wsdl描述页面。
然后就测试客户端,注意XFire插件开发客户端程序好像必须使用JDK1.5,低版本好像不支持。(记得在工程属性里将JDK换成1.5后,然后在Java Compiler中选中“Enable project specific settings”,然后在下面的“Compiler compliance level选中5.0”,然后点Apply->ok,重新编译一下),好,编写一个测试类,运行就可以看到我们想要得到的HelloWorld了。
总结:注意JDK版本是否冲突。注意WebSphere下的lib中的包是否和项目中的包冲突以及包是否全。
posted @
2008-05-14 16:36 阿伟 阅读(3456) |
评论 (1) |
编辑 收藏
一个不错的编写properties文件的Eclipse插件(plugin),有了它我们在编辑一些简体中文、繁体中文等Unicode文本时,就不必再使用native2ascii编码了。您可以通过Eclipse中的软件升级(Software Update)安装此插件,步骤如下:
1、展开Eclipse的Help菜单,将鼠标移到Software Update子项,在出现的子菜单中点击Find and Install;
2、在Install/Update对话框中选择Search for new features to install,点击Next;
3、在Install对话框中点击New Remote Site;
4、在New Update Site对话框的Name填入“PropEdit”或其它任意非空字符串,在URL中填入http://propedit.sourceforge.jp/eclipse/updates/;
5、在Site to include to search列表中,除上一步加入的site外的其它选项去掉,点击Finsih;
6、在弹出的Updates对话框中的Select the features to install列表中将所有结尾为“3.1.x”的选项去掉(适用于Eclipse 3.2版本的朋友);
7、点击Finish关闭对话框;
8、在下载后,同意安装,再按提示重启Eclipse,在工具条看到形似vi的按钮表示安装成功,插件可用。此时,Eclpise中所有properties文件的文件名前有绿色的P的图标作为标识。
posted @
2008-04-15 13:10 阿伟 阅读(1818) |
评论 (2) |
编辑 收藏
Tomcat5.5 Administration Web Application配置
转自:http://www.blogjava.net/bisal/archive/2007/04/18/111669.html
Tomcat5.5默认安装时,并没有提供 Administration Web Application,需要另外下载安装,到这里下载:http://tomcat.apache.org/download-55.cgi ,在下载页面的Binary Distributions栏下的第四大项,Administration Web Application ,下载后解压,然后把apache-tomcat-5.5.20\conf\Catalina\localhost\admin.xml拷贝到 Tomcat 5.5\conf\Catalina\localhost下面,再把apache-tomcat-5.5.20\server\webapps下
的 admin文件夹拷贝到Tomcat5.5\server\webapps下面。
/*打开Tomcat5.5\conf\下的tomcat- users.xml,加入下面这句<user username="admin" password="admin" roles="admin, manager"/>,*/
最后重新启动tomcat就ok了。
posted @
2008-04-11 17:01 阿伟 阅读(491) |
评论 (0) |
编辑 收藏
转自:http://dev.csdn.net/article/68/68297.shtm
hibernate 三种查询方式 选择自 wangyihust 的 Blog
(一)HQL
HQL:Hibernate Qusery Language,如果你已经熟悉它,就会发现它跟SQL非常相像。不过你不要被表面的假象迷惑,HQL是面向对象的(OO,用生命的眼光看待每一个对象,他们是如此鲜活)。如果你对JAVA和SQL语句有一定了解的话,那么HQL对你简直易如反掌,你完全可以利用在公车上的时间掌握它。
以下从几个方面进行慢慢深入:
1。大小些敏感
大家知道SQL-92 Query是对大小写不敏感的,但是在HQL(前面提到它是OO的)中对对象类的名称和属性确实大小写敏感的(符合java编程语法)。
HQL 子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分
如:sElect cat.name from Cat as cat和select cat.name from Cat as cat是一样的
但是:
sElect cat.name from CAT as cat和select cat.name from Cat as cat确实不一样的。
2。from语句
最简单的:
from eg.Cat
它只是简单的返回所有eg.Cat的实例,通常我们此时会为eg.Cat其个别名,因为在query的其余部分可能会用到(参看上边关于大小写敏感时的例子情形),如:
from eg.Cat as cat 这里as可以省略。
上边只是单表查询,多表的情况如下写法:
from eg.Cat, eg.Dog
from eg.Cat as cat, eg.Dog as dog
3。join相关
(inner) join
left (outer) join
right (outer) join
full join
HQL同样对SQL中的这些特性支持
下面插播一个小话题,关于上边的那些特性,我一直都没怎么用,今天既然说到这里,就想把上边的几个特性的用法说一下,也算对自己的一个补充:
假设有两个表:部门、员工,下面列举一些数据:
员工(Employee):
ID Name DepNo
001 Jplateau 01
002 Jony 01
003 Camel 02
部门(Department):
ID Name
01 研发部
02 营销部
在Hibernate中我们操纵的都是对象,所以我们操纵的是部门类和员工
1).(inner) join
select employee.ID as id1,employee.Name as name1,
department.ID as id2,department.Name as name2 from Employee as employee
join Department as department on employee.DepNo=department.ID (注意到条件语句我用on 没有用where)
那么执行结果是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01 研发部
002 Jony 01 研发部
2).left (outer) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee left join Department as department on employee.DepNo=
department.ID
那么执行结果又该是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01 研发部
002 Jony 01 研发部
003 Camel null null
{就是说此时我要已第一个表的记录多少为准,第二个表中没有相应纪录的时候填充null}
3). right (outer) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee right join Department as department on employee.DepNo=
department.ID
那么执行结果又该是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01 研发部
002 Jony 01 研发部
null null 02 营销部
{就是说此时我要已第二个表的记录多少为准,第一个表中没有相应纪录的时候填充null}
4。select语句
就是要确定你要从查询中返回哪些对象或者哪些对象的属性。写几个例子吧:
select employee form Employee as employee
select employee form Employee as employee where employee.Name like 'J%'
select employee.Name form Employee as employee where employee.Name like 'J%'
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee right join Department as department on employee.DepNo=
department.ID
select elements(employee.Name) from Employee as employee
(不明白elements到底是做什么用的?望给于说明)
等等
5。数学函数
JDO目前好像还不支持此类特性。
avg(...), sum(...), min(...), max(...)
count(*)
count(...), count(distinct ...), count(all...)
其用法和SQL基本相同
select distinct employee.name from Employee as employee
select count(distinct employee.name),count(employee) from Employee as employee
6。polymorphism (暂时不知道如何解释?)
from com.test.Animal as animal
不光得到所有Animal得实例,而且可以得到所有Animal的子类(如果我们定义了一个子类Cat)
一个比较极端的例子
from java.lang.Object as o
可以得到所有持久类的实例
7。where语句
定义查询语句的条件,举几个例子吧:
from Employee as employee where employee.Name='Jplateau'
from Employee as employee where employee.Name like 'J%'
from Employee as employee where employee.Name like '%u'
在where语句中“=”不光可以比较对象的属性,也可以比较对象,如:
select animal from com.test.Animal as animal where animal.name=dog
8。表达式
在SQL语句中大部分的表达式在HQL中都可以使用:
mathematical operators +, -, *, /
binary comparison operators =, >=, <=, <>, !=, like
logical operations and, or, not
string concatenation ||
SQL scalar functions like upper() and lower()
Parentheses ( ) indicate grouping
in, between, is null
JDBC IN parameters ?
named parameters :name, :start_date, :x1 (这种应该是另一种"?"的变通解决方法)
SQL literals 'foo', 69, '1970-01-01 10:00:01.0'
Java public static final constants eg.Color.TABBY
其他不必解释了,在这里我只想对查询中的参数问题说明一下:
大家知道在SQL中进行传递参数进行查询的时候,我们通常用PreparedStatement,在语句中写一大堆的“?”,
在hql中也可以用这种方法,如:
List mates = sess.find(
"select employee.name from Employee as employee " +
"where employee.Name=? ",
name,
Hibernate.STRING
);
(说明:上面利用Session里的find方法,在hibernate的api Session中重载了很多find方法,它可以满足你多种形式的查询)
上边是一个参数的情形,这种情况下紧接着引入参数和定义参数的类型,当为多个参数,调用另一个find方法,它的后两个
参数都是数组的形式。
还有另外一种方法来解决上边的问题,JDO也有这样的方法,不过和hibernate的表现形式上有差别,但他们两个骨子里却是
一样的,如:
Query q = sess.createQuery("select employee.name from Employee as employee where employee.Name=:name");
q.setString("name", "Jplateau");
//当有多个参数的时候在此逐一定义
Iterator employees = q.iterate();
9。order 语句
和sql语句没什么差别,如:
select employee.name from Employee as employee where employee.Name like 'J%' order by employee.ID desc (或者asc)
10。group by 语句
同样和sql语句没什么差别,如:
select employee.name,employee.DepNo from Employee as employee group by employee.DepNo
select foo.id, avg( elements(foo.names) ), max( indices(foo.names) ) from eg.Foo foo group by foo.id
{Note: You may use the elements and indices constructs inside a select clause, even on databases with no subselects.}
谁帮我解释一下上边两句,谢过!
11。子查询
hibernate同样支持子查询,写几个例子:
from eg.Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from eg.DomesticCat cat )
(二)条件查询Criteria Query
。数学函数
JDO目前好像还不支持此类特性。
avg(...), sum(...), min(...), max(...)
count(*)
count(...), count(distinct ...), count(all...)
其用法和SQL基本相同
select distinct employee.name from Employee as employee
select count(distinct employee.name),count(employee) from Employee as employee
6。polymorphism (暂时不知道如何解释?)
from com.test.Animal as animal
不光得到所有Animal得实例,而且可以得到所有Animal的子类(如果我们定义了一个子类Cat)
一个比较极端的例子
from java.lang.Object as o
可以得到所有持久类的实例
7。where语句
定义查询语句的条件,举几个例子吧:
from Employee as employee where employee.Name='Jplateau'
from Employee as employee where employee.Name like 'J%'
from Employee as employee where employee.Name like '%u'
在where语句中“=”不光可以比较对象的属性,也可以比较对象,如:
select animal from com.test.Animal as animal where animal.name=dog
8。表达式
在SQL语句中大部分的表达式在HQL中都可以使用:
mathematical operators +, -, *, /
binary comparison operators =, >=, <=, <>, !=, like
logical operations and, or, not
string concatenation ||
SQL scalar functions like upper() and lower()
Parentheses ( ) indicate grouping
in, between, is null
JDBC IN parameters ?
named parameters :name, :start_date, :x1 (这种应该是另一种"?"的变通解决方法)
SQL literals 'foo', 69, '1970-01-01 10:00:01.0'
Java public static final constants eg.Color.TABBY
其他不必解释了,在这里我只想对查询中的参数问题说明一下:
大家知道在SQL中进行传递参数进行查询的时候,我们通常用PreparedStatement,在语句中写一大堆的“?”,
在hql中也可以用这种方法,如:
List mates = sess.find(
"select employee.name from Employee as employee " +
"where employee.Name=? ",
name,
Hibernate.STRING
);
(说明:上面利用Session里的find方法,在hibernate的api Session中重载了很多find方法,它可以满足你多种形式的查询)
上边是一个参数的情形,这种情况下紧接着引入参数和定义参数的类型,当为多个参数,调用另一个find方法,它的后两个
参数都是数组的形式。
还有另外一种方法来解决上边的问题,JDO也有这样的方法,不过和hibernate的表现形式上有差别,但他们两个骨子里却是
一样的,如:
Query q = sess.createQuery("select employee.name from Employee as employee where employee.Name=:name");
q.setString("name", "Jplateau");
//当有多个参数的时候在此逐一定义
Iterator employees = q.iterate();
9。order 语句
和sql语句没什么差别,如:
select employee.name from Employee as employee where employee.Name like 'J%' order by employee.ID desc (或者asc)
10。group by 语句
同样和sql语句没什么差别,如:
select employee.name,employee.DepNo from Employee as employee group by employee.DepNo
select foo.id, avg( elements(foo.names) ), max( indices(foo.names) ) from eg.Foo foo group by foo.id
{Note: You may use the elements and indices constructs inside a select clause, even on databases with no subselects.}
谁帮我解释一下上边两句,谢过!
11。子查询
hibernate同样支持子查询,写几个例子:
from eg.Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from eg.DomesticCat cat )
(二)条件查询Criteria Query
Criteria criteria = osession.createCriteria(Owner.class);
criteria.add(Expression.eq("age", new Integer(100)));
criteria.setFirstResult(2); //从返回结果的第二条记录开始的5条记录
criteria.setMaxResults(5);
List lc=criteria.list();
System.out.println("条件查询");
System.out.println(lc.size());
(三)原生SQL语句查询
posted @
2008-04-10 17:41 阿伟 阅读(279) |
评论 (0) |
编辑 收藏
转自:http://lihaiyan.javaeye.com/blog/127812
为了在Struts中加载Spring context,需要在struts-config.xml文件中加入如下部分:
<struts-config>
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml" />
</plug-in>
</struts-config>
第一种方法:
通过Struts的plug-in在Struts和Spring之间提供了良好的结合点。通过plug-in我们实现了Spring context的加载,不过仅仅加载Spring context并没有什么实际的意义,还应该经过配置将Struts的Action交给Spring容器进行管理。
<action-mappings>
<action path="/login"
type="org.springframework.web.struts.DelegatingActionProxy"
name="loginForm">
<forward name="success" path="/main.jsp" />
<forward name="failure" path="/login.jsp" />
</action>
在form bean这个节点上与传统的Struts配置没有什么区别,而在Action上面则发生了变化。在传统的action节点上type属性写入action类的完整类名,而和Spring结合后在这点上是使用了Spring提供的DelegatingActionProxy作为action的type属性,DelegatingActionProxy同样是org.apache.struts.action.Action的一个子类,它将把调用请求转交给真正的Action实现。通过这样的方式,Spring获得了Action实例的管理权,它将对Action进行调度,并为Struts提供所需的Action实例。这样,就可以将Action看作是Spring的一个bean,它就可以享受Spring的所有服务,如依赖注入、实例管理、事务管理等。
在applicationContext.xml中相应的配置如下的节点:
<beans>
.......
<bean name="/login" class="net.xiaxin.action.LoginAction"
singleton="false">
<property name="userDAO">
<ref bean="userDAOProxy" />
</property>
</bean>
</beans>
最后这个bean的配置是关键,这个名为“/login”的bean与Struts中的
<action path="/login" ……>
……
</action>节点相对应,这样,Spring Bean Name与Struts Action Path相关联,当Struts加载对应的Action时,DelegatingActionProxy就根据传入的path属性,在Spring Context寻找对应bean,并将其实例返回给Struts。与此同时,还可以看到,"/login" bean 中包含了一个userDAO 引用,Spring 在运行期将根据配置为其提供userDAO 实例,以及围绕userDAO 的事务管理服务。这样一来,对于Struts 开发而言,我们既可以延续Struts 的开发流程,也可以享受Spring 提供的事务管
理服务。而bean 的另外一个属性singleton="false",指明了Action 的实例获取方式为每次重新创建。这也解决了Struts中令人诟病的线程安全问题。
第二种方法:
为了在 struts-config.xml 文件中配置 DelegatingRequestProcessor,你需要重载 <controller> 元素的 “processorClass” 属性。 下面的几行应该放在 <action-mapping> 元素的后面。
<controller>
<set-property property="processorClass"
value="http://www.zhmy.com/org.springframework.web.struts.DelegatingRequestProcessor"/>
</controller>
增加这些设置之后,不管你查询任何类型的 Action,Sping都自动在它的context配置文件中寻找。 实际上,你甚至不需要指定类型。下面两个代码片断都可以工作:
<action path="/user" type="com.whatever.struts.UserAction"/>
<action path="/user"/>
如果你使用 Struts 的 modules 特性,你的 bean 命名必须含有 module 的前缀。 举个例子,如果一个 Action 的定义为 <action path="/user"/>,而且它的 module 前缀为“admin”, 那么它应该对应名为 <bean name="/admin/user"/> 的 bean
如果第二种方法不行,再用第一种方法。
至此,SS组合已经将Struts MVC以及Spring中的Bean管理、事务管理融为一体。如
果算上userDAO 中的Hibernate 部分,我们就获得了一个全面、成熟、高效、自顶而下的
Web 开发框架。
来源:http://deathmask1980.spaces.live.com/blog/cns!8633c46371110374!118.entry
posted @
2008-04-10 16:46 阿伟 阅读(775) |
评论 (0) |
编辑 收藏
转自:http://lihaiyan.javaeye.com/blog/127797
Web层实现
1、Web层的构件和交互流程
Web层包括主要3个功能:
·上传文件。
·列出所有已经上传的文件列表,以供点击下载。
·下载文件。
Web层实现构件包括与2个JSP页面,1个ActionForm及一个Action:
·file-upload.jsp:上传文件的页面。
·file-list.jsp:已经上传文件的列表页面。
·FileActionForm:file-upload.jsp页面表单对应的ActionForm。
·FileAction:继承org.apache.struts.actions.DispatchAction的Action,这样这个Action就可以通过一个URL参数区分中响应不同的请求。
Web层的这些构件的交互流程如图 6所示:
420){this.resized=true;this.style.width=420;}" border=0 resized="true">
图 6 Web层Struts流程图
|
其中,在执行文件上传的请求时,FileAction在执行文件上传后,forward到loadAllFile出口中,loadAllFile加载数据库中所有已经上传的记录,然后forward到名为fileListPage的出口中,调用file-list.jsp页面显示已经上传的记录。
2、FileAction功能
Struts 1.0的Action有一个弱项:一个Action只能处理一种请求,Struts 1.1中引入了一个DispatchAction,允许通过URL参数指定调用Action中的某个方法,如http://yourwebsite/fileAction.do?method=upload即调用FileAction中的upload方法。通过这种方式,我们就可以将一些相关的请求集中到一个Action当中编写,而没有必要为某个请求操作编写一个Action类。但是参数名是要在struts-config.xml中配置的:
1. <struts-config>
2. <form-beans>
3. <form-bean name="fileActionForm" type="sshfile.web.FileActionForm" />
4. </form-beans>
5. <action-mappings>
6. <action name="fileActionForm" parameter="method" path="/fileAction"
7. type="sshfile.web.FileAction">
8. <forward name="fileListPage" path="/file-list.jsp" />
9. <forward name="loadAllFile" path="/fileAction.do?method=listAllFile" />
10. </action>
11. </action-mappings>
12. </struts-config> |
第6行的parameter="method"指定了承载方法名的参数,第9行中,我们还配置了一个调用FileAction不同方法的Action出口。
FileAction共有3个请求响应的方法,它们分别是:
·upload(…):处理上传文件的请求。
·listAllFile(…):处理加载数据库表中所有记录的请求。
·download(…):处理下载文件的请求。
下面我们分别对这3个请求处理方法进行讲解。
2.1 上传文件
上传文件的请求处理方法非常简单,简之言之,就是从Spring容器中获取业务层处理类FileService,调用其save(FileActionForm form)方法上传文件,如下所示:
1. public class FileAction
2. extends DispatchAction
3. {
4. //将上传文件保存到数据库中
5. public ActionForward upload(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. {
9. FileActionForm fileForm = (FileActionForm) form;
10. FileService fileService = getFileService();
11. fileService.save(fileForm);
12. return mapping.findForward("loadAllFile");
13. }
14. //从Spring容器中获取FileService对象
15. private FileService getFileService()
16. {
17. ApplicationContext appContext = WebApplicationContextUtils.
18. getWebApplicationContext(this.getServlet().getServletContext());
19. return (FileService) appContext.getBean("fileService");
20. }
21. …
22. } |
由于FileAction其它两个请求处理方法也需要从Spring容器中获取FileService实例,所以我们特别提供了一个getFileService()方法(第15~21行)。重构的一条原则就是:"发现代码中有重复的表达式,将其提取为一个变量;发现类中有重复的代码段,将其提取为一个方法;发现不同类中有相同的方法,将其提取为一个类"。在真实的系统中,往往拥有多个Action和多个Service类,这时一个比较好的设置思路是,提供一个获取所有Service实现对象的工具类,这样就可以将Spring 的Service配置信息屏蔽在一个类中,否则Service的配置名字散落在程序各处,维护性是很差的。
2.2 列出所有已经上传的文件
listAllFile方法调用Servie层方法加载T_FILE表中所有记录,并将其保存在Request域中,然后forward到列表页面中:
1. public class FileAction
2. extends DispatchAction
3. {
4. …
5. public ActionForward listAllFile(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileService fileService = getFileService();
11. List fileList = fileService.getAllFile();
12. request.setAttribute("fileList",fileList);
13. return mapping.findForward("fileListPage");
14. }
15. } |
file-list.jsp页面使用Struts标签展示出保存在Request域中的记录:
1. <%@page contentType="text/html; charset=GBK"%>
2. <%@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
3. <%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
4. <html>
5. <head>
6. <title>file-download</title>
7. </head>
8. <body bgcolor="#ffffff">
9. <ol>
10. <logic:iterate id="item" name="fileList" scope="request">
11. <li>
12. <a href='fileAction.do?method=download&fileId=
13. <bean:write name="item"property="fileId"/>'>
14. <bean:write name="item" property="fileName"/>
15. </a>
16. </li>
17. </logic:iterate>
18. </ol>
19. </body>
20. </html> |
展现页面的每条记录挂接着一个链接地址,形如:fileAction.do?method=download&fileId=xxx,method参数指定了这个请求由FileAction的download方法来响应,fileId指定了记录的主键。
由于在FileActionForm中,我们定义了fileId的属性,所以在download响应方法中,我们将可以从FileActionForm中取得fileId的值。这里涉及到一个处理多个请求Action所对应的ActionForm的设计问题,由于原来的Action只能对应一个请求,那么原来的ActionForm非常简单,它仅需要将这个请求的参数项作为其属性就可以了,但现在一个Action对应多个请求,每个请求所对应的参数项是不一样的,此时的ActionForm的属性就必须是多请求参数项的并集了。所以,除了文件上传请求所对应的fileContent和remark属性外还包括文件下载的fileId属性:
420){this.resized=true;this.style.width=420;}" border=0>
图 7 FileActionForm
|
当然这样会造成属性的冗余,比如在文件上传的请求中,只会用到fileContent和remark属性,而在文件下载的请求时,只会使用到fileId属性。但这种冗余是会带来好处的--它使得一个Action可以处理多个请求。
2.3 下载文件
在列表页面中点击一个文件下载,其请求由FileAction的download方法来响应,download方法调用业务层的FileService方法,获取文件数据并写出到response的响应流中。通过合理设置HTTP响应头参数,将响应流在客户端表现为一个下载文件对话框,其代码如下所示:
代码 10 业务接口实现类之download
1. public class FileAction
2. extends DispatchAction
3. {
4. …
5. public ActionForward download(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileActionForm fileForm = (FileActionForm) form;
11. FileService fileService = getFileService();
12. String fileName = fileService.getFileName(fileForm.getFileId());
13. try
14. {
15. response.setContentType("application/x-msdownload");
16. response.setHeader("Content-Disposition",
17. "attachment;" + " filename="+
18. new String(fileName.getBytes(), "ISO-8859-1"));
19. fileService.write(response.getOutputStream(), fileForm.getFileId());
20. }
21. catch (Exception e)
22. {
23. throw new ModuleException(e.getMessage());
24. }
25. return null;
26. }
27. } |
第15~18行,设置HTTP响应头,将响应类型设置为application/x-msdownload MIME类型,则响应流在IE中将弹出一个文件下载的对话框,如图 4所示。IE所支持的MIME类型多达26种,您可以通过这个网址查看其他的MIME类型:
http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp。
如果下载文件的文件名含有中文字符,如果不对其进行硬编码,如第18行所示,客户文件下载对话框中出现的文件名将会发生乱码。
第19行代码获得response的输出流,作为FileServie write(OutputStream os,String fileId)的入参,这样文件的内容将写到response的输出流中。
3、web.xml文件的配置
Spring容器在何时启动呢?我可以在Web容器初始化来执行启动Spring容器的操作,Spring提供了两种方式启动的方法:
·通过org.springframework.web.context .ContextLoaderListener容器监听器,在Web容器初始化时触发初始化Spring容器,在web.xml中通过<listener></listener>对其进行配置。
·通过Servlet org.springframework.web.context.ContextLoaderServlet,将其配置为自动启动的Servlet,在Web容器初始化时,通过这个Servlet启动Spring容器。
在初始化Spring容器之前,必须先初始化log4J的引擎,Spring也提供了容器监听器和自动启动Servlet两种方式对log4J引擎进行初始化:
·org.springframework.web.util .Log4jConfigListener
·org.springframework.web.util.Log4jConfigServlet
下面我们来说明如何配置web.xml启动Spring容器:
代码 11 web.xml中对应Spring的配置内容
1. <web-app>
2. <context-param>
3. <param-name>contextConfigLocation</param-name>
4. <param-value>/WEB-INF/applicationContext.xml</param-value>
5. </context-param>
6. <context-param>
7. <param-name>log4jConfigLocation</param-name>
8. <param-value>/WEB-INF/log4j.properties</param-value>
9. </context-param>
10. <servlet>
11. <servlet-name>log4jInitServlet</servlet-name>
12. <servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
13. <load-on-startup>1</load-on-startup>
14. </servlet>
15. <servlet>
16. <servlet-name>springInitServlet</servlet-name>
17. <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
18. <load-on-startup>2</load-on-startup>
19. </servlet>
20. …
21. </web-app> |
启动Spring容器时,需要得到两个信息:Spring配置文件的地址和Log4J属性文件,这两上信息分别通过contextConfigLocationWeb和log4jConfigLocation容器参数指定,如果有多个Spring配置文件,则用逗号隔开,如:
/WEB-INF/applicationContext_1.xml, /WEB-INF/applicationContext_1.xm2
由于在启动ContextLoaderServlet之前,必须事先初始化Log4J的引擎,所以Log4jConfigServlet必须在ContextLoaderServlet之前启动,这通过<load-on-startup>来指定它们启动的先后顺序。
乱码是开发Web应用程序一个比较老套又常见问题,由于不同Web应用服务器的默认编码是不一样的,为了方便Web应用在不同的Web应用服务器上移植,最好的做法是Web程序自身来处理编码转换的工作。经典的作法是在web.xml中配置一个编码转换过滤器,Spring就提供了一个编码过滤器类CharacterEncodingFilter,下面,我们为应用配置上这个过滤器:
1. <web-app>
2. …
3. <filter>
4. <filter-name>encodingFilter</filter-name>
5. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
6. <init-param>
7. <param-name>encoding</param-name>
8. <param-value>GBK</param-value>
9. </init-param>
10. </filter>
11. <filter-mapping>
12. <filter-name>encodingFilter</filter-name>
13. <url-pattern>/*</url-pattern>
14. </filter-mapping>
15. …
16. </web-app> |
Spring的过滤器类是org.springframework.web.filter.CharacterEncodingFilter,通过encoding参数指定编码转换类型为GBK,<filter-mapping>的配置使该过滤器截获所有的请示。
Struts的框架也需要在web.xml中配置,想必读者朋友对Struts的配置都很熟悉,故在此不再提及,请参见本文所提供的源码。
总结
本文通过一个文件上传下载的Web应用,讲解了如何构建基于SSH的Web应用,通过Struts和FormFile,Spring的LobHandler以及Spring为HibernateBlob处理所提供的用户类BlobByteArrayType ,实现上传和下载文件的功能仅需要廖廖数行的代码即告完成。读者只需对程序作稍许的调整,即可处理Clob字段:
·领域对象对应Clob字段的属性声明为String类型;
·映射文件对应Clob字段的属性声明为org.springframework.orm.hibernate3.support.ClobStringType类型。
本文通过SSH对文件上传下载简捷完美的实现得以管中窥豹了解SSH强强联合构建Web应用的强大优势。在行文中,还穿插了一些分层的设计经验,配置技巧和Spring所提供的方便类,相信这些知识对您的开发都有所裨益。
作者:陈雄华出处:天极开发
posted @
2008-04-10 15:03 阿伟 阅读(358) |
评论 (0) |
编辑 收藏
转自:http://lihaiyan.javaeye.com/blog/127796
业务层
1、业务层接口
"面向接口而非面向类编程"是Spring不遗余力所推荐的编程原则,这条原则也已经为大部开发者所接受;此外,JDK的动态代理只对接口有效,否则必须使用CGLIB生成目标类的子类。我们依从于Spring的倡导为业务类定义一个接口:
代码 7 业务层操作接口
1. public interface FileService
2. {
3. void save(FileActionForm fileForm);//将提交的上传文件保存到数据表中
4. List getAllFile();//得到T_FILE所示记录
5. void write(OutputStream os,String fileId);//将某个文件的文件数据写出到输出流中
6. String getFileName(String fileId);//获取文件名
7. } |
其中save(FileActionForm fileForm)方法,将封装在fileForm中的上传文件保存到数据库中,这里我们使用FileActionForm作为方法入参,FileActionForm是Web层的表单数据对象,它封装了提交表单的数据。将FileActionForm直接作为业务层的接口入参,相当于将Web层传播到业务层中去,即将业务层绑定在特定的Web层实现技术中,按照分层模型学院派的观点,这是一种反模块化的设计,但在"一般"的业务系统并无需提供多种UI界面,系统Web层将来切换到另一种实现技术的可能性也微乎其微,所以笔者觉得没有必要为了这个业务层完全独立于调用层的过高目标而去搞一个额外的隔离层,浪费了原材料不说,还将系统搞得过于复杂,相比于其它原则,"简单"始终是最大的一条原则。
getAllFile()负责获取T_FILE表所有记录,以便在网页上显示出来。
而getFileName(String fileId)和write(OutputStream os,String fileId)则用于下载某个特定的文件。具体的调用是将Web层将response.getOutputStream()传给write(OutputStream os,String fileId)接口,业务层直接将文件数据输出到这个响应流中。具体实现请参见错误!未找到引用源。节下载文件部分。
2、业务层接口实现类
FileService的实现类为FileServiceImpl,其中save(FileActionForm fileForm)的实现如下所示:
代码 8 业务接口实现类之save()
1. …
2. public class FileServiceImpl
3. implements FileService
4. {
5. private TfileDAO tfileDAO;
6. public void save(FileActionForm fileForm)
7. {
8. Tfile tfile = new Tfile();
9. try
10. {
11. tfile.setFileContent(fileForm.getFileContent().getFileData());
12. }
13. catch (FileNotFoundException ex)
14. {
15. throw new RuntimeException(ex);
16. }
17. catch (IOException ex)
18. {
19. throw new RuntimeException(ex);
20. }
21. tfile.setFileName(fileForm.getFileContent().getFileName());
22. tfile.setRemark(fileForm.getRemark());
23. tfileDAO.save(tfile);
24. }
25. …
26. } |
在save(FileActionForm fileForm)方法里,完成两个步骤:
其一,象在水桶间倒水一样,将FileActionForm对象中的数据倒入到Tfile对象中;
其二,调用TfileDAO保存数据。
需要特别注意的是代码的第11行,FileActionForm的fileContent属性为org.apache.struts.upload.FormFile类型,FormFile提供了一个方便的方法getFileData(),即可获取文件的二进制数据。通过解读FormFile接口实现类DiskFile的原码,我们可能知道FormFile本身并不缓存文件的数据,只有实际调用getFileData()时,才从磁盘文件输入流中获取数据。由于FormFile使用流读取方式获取数据,本身没有缓存文件的所有数据,所以对于上传超大体积的文件,也是没有问题的;但是,由于数据持久层的Tfile使用byte[]来缓存文件的数据,所以并不适合处理超大体积的文件(如100M),对于超大体积的文件,依然需要使用java.sql.Blob类型以常规流操作的方式来处理。
此外,通过FileForm的getFileName()方法就可以获得上传文件的文件名,如第21行代码所示。
write(OutputStream os,String fileId)方法的实现,如代码 9所示:
代码 9 业务接口实现类之write()
1. …
2. public class FileServiceImpl
3. implements FileService
4. {
5.
6. public void write(OutputStream os, String fileId)
7. {
8. Tfile tfile = tfileDAO.findByFildId(fileId);
9. try
10. {
11. os.write(tfile.getFileContent());
12. os.flush();
13. }
14. catch (IOException ex)
15. {
16. throw new RuntimeException(ex);
17. }
18. }
19. …
20. } |
write(OutputStream os,String fileId)也简单地分为两个操作步骤,首先,根据fileId加载表记录,然后将fileContent写入到输出流中。
3、Spring事务配置
下面,我们来看如何在Spring配置文件中为FileService配置声明性的事务
1. <beans>
2. …
3. <bean id="transactionManager"
4. class="org.springframework.orm.hibernate3.HibernateTransactionManager">
5. <property name="sessionFactory" ref="sessionFactory"/>
6. </bean>
7. <!-- 事务处理的AOP配置 //-->
8. <bean id="txProxyTemplate" abstract="true"
9. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
10. <property name="transactionManager" ref="transactionManager"/>
11. <property name="transactionAttributes">
12. <props>
13. <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
14. <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
15. <prop key="save">PROPAGATION_REQUIRED</prop>
16. <prop key="write">PROPAGATION_REQUIRED,readOnly</prop>
17. </props>
18. </property>
19. </bean>
20. <bean id="fileService" parent="txProxyTemplate">
21. <property name="target">
22. <bean class="sshfile.service.FileServiceImpl">
23. <property name="tfileDAO" ref="tfileDAO"/>
24. </bean>
25. </property>
26. </bean>
27. </beans> |
Spring的事务配置包括两个部分:
其一,定义事务管理器transactionManager,使用HibernateTransactionManager实现事务管理;
其二,对各个业务接口进行定义,其实txProxyTemplate和fileService是父子节点的关系,本来可以将txProxyTemplate定义的内容合并到fileService中一起定义,由于我们的系统仅有一个业务接口需要定义,所以将其定义的一部分抽象到父节点txProxyTemplate中意义确实不大,但是对于真实的系统,往往拥有为数众多的业务接口需要定义,将这些业务接口定义内容的共同部分抽取到一个父节点中,然后在子节点中通过parent进行关联,就可以大大简化业务接口的配置了。
父节点txProxyTemplate注入了事务管理器,此外还定义了业务接口事务管理的方法(允许通过通配符的方式进行匹配声明,如前两个接口方法),有些接口方法仅对数据进行读操作,而另一些接口方法需要涉及到数据的更改。对于前者,可以通过readOnly标识出来,这样有利于操作性能的提高,需要注意的是由于父类节点定义的Bean仅是子节点配置信息的抽象,并不能具体实现化一个Bean对象,所以需要特别标注为abstract="true",如第8行所示。
fileService作为一个目标类被注入到事务代理器中,而fileService实现类所需要的tfileDAO实例,通过引用3.2节中定义的tfileDAO Bean注入。
posted @
2008-04-10 15:01 阿伟 阅读(227) |
评论 (0) |
编辑 收藏
转自:http://lihaiyan.javaeye.com/blog/127795
数据持久层
1、领域对象及映射文件
您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer等工具或手工的方式,编写Hibernate的领域对象和映射文件。其中对应T_FILE表的领域对象Tfile.java为:
代码 1 领域对象Tfile
1. package sshfile.model;
2. public class Tfile
3.{
4. private String fileId;
5. private String fileName;
6. private byte[] fileContent;
7. private String remark;
8. …//getter and setter
9. } |
特别需要注意的是:数据库表为Blob类型的字段在Tfile中的fileContent类型为byte[]。Tfile的Hibernate映射文件Tfile.hbm.xml放在Tfile .java类文件的相同目录下:
代码 2 领域对象映射文件
1. <?xml version="1.0"?>
2. <!DOCTYPE hibernate-mapping PUBLIC
3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
5. <hibernate-mapping>
6. <class name="sshfile.model.Tfile" table="T_FILE">
7. <id name="fileId" type="java.lang.String" column="FILE_ID">
8. <generator class="uuid.hex"/>
9. </id>
10. <property name="fileContent"
11. type="org.springframework.orm.hibernate3.support.BlobByteArrayType"
12. column="FILE_CONTENT" lazy="true"/>
13. …//其它一般字段的映射
14. </class>
15. </hibernate-mapping> |
fileContent字段映射为Spring所提供的BlobByteArrayType类型,BlobByteArrayType是用户自定义的数据类型,它实现了Hibernate 的org.hibernate.usertype.UserType接口。BlobByteArrayType使用从sessionFactory获取的Lob操作句柄lobHandler将byte[]的数据保存到Blob数据库字段中。这样,我们就再没有必要通过硬编码的方式,先insert然后再update来完成Blob类型数据的持久化,这个原来难伺候的老爷终于被平民化了。关于lobHandler的配置请见本文后面的内容。
此外lazy="true"说明地返回整个Tfile对象时,并不返回fileContent这个字段的数据,只有在显式调用tfile.getFileContent()方法时才真正从数据库中获取fileContent的数据。这是Hibernate3引入的新特性,对于包含重量级大数据的表字段,这种抽取方式提高了对大字段操作的灵活性,否则加载Tfile对象的结果集时如果总是返回fileContent,这种批量的数据抽取将可以引起数据库的"洪泛效应"。
2、DAO编写和配置
Spring强调面向接口编程,所以我们将所有对Tfile的数据操作的方法定义在TfileDAO接口中,这些接口方法分别是:
·findByFildId(String fileId)
·save(Tfile tfile)
·List findAll()
TfileDAOHibernate提供了对TfileDAO接口基于Hibernate的实现,如代码 3所示:
代码 3 基于Hibernate 的fileDAO实现类
1. package sshfile.dao;
2.
3. import sshfile.model.*;
4. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
5. import java.util.List;
6.
7. public class TfileDAOHibernate
8. extends HibernateDaoSupport implements TfileDAO
9. {
10. public Tfile findByFildId(String fileId)
11. {
12. return (Tfile) getHibernateTemplate().get(Tfile.class, fileId);
13. }
14. public void save(Tfile tfile)
15. {
16. getHibernateTemplate().save(tfile);
17. getHibernateTemplate().flush();
18. }
19. public List findAll()
20. {
21. return getHibernateTemplate().loadAll(Tfile.class);
22. }
23. } |
TfileDAOHibernate通过扩展Spring提供的Hibernate支持类HibernateDaoSupport而建立,HibernateDaoSupport封装了HibernateTemplate,而HibernateTemplate封装了Hibernate所提供几乎所有的的数据操作方法,如execute(HibernateCallback action),load(Class entityClass, Serializable id),save(final Object entity)等等。
所以我们的DAO只需要简单地调用父类的HibernateTemplate就可以完成几乎所有的数据库操作了。
由于Spring通过代理Hibernate完成数据层的操作,所以原Hibernate的配置文件hibernate.cfg.xml的信息也转移到Spring的配置文件中:
代码 4 Spring中有关Hibernate的配置信息
1. <beans>
2. <!-- 数据源的配置 //-->
3. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
4. destroy-method="close">
5. <property name="driverClassName" value="http://www.zhmy.com/oracle.jdbc.driver.OracleDriver"/>
6. <property name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/>
7. <property name="username" value="test"/>
8. <property name="password" value="test"/>
9. </bean>
10. <!-- Hibernate会话工厂配置 //-->
11. <bean id="sessionFactory"
12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
13. <property name="dataSource" ref="dataSource"/>
14. <property name="mappingDirectoryLocations">
15. <list>
16. <value>classpath:/sshfile/model</value>
17. </list>
18. </property>
19. <property name="hibernateProperties">
20. <props>
21. <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
22. <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
23. </props>
24. </property>
25. </bean>
26. <!-- Hibernate 模板//-->
27. <bean id="hibernateTemplate"
28. class="org.springframework.orm.hibernate3.HibernateTemplate">
29. <property name="sessionFactory" ref="sessionFactory"/>
30. </bean>
31. <!--DAO配置 //-->
32. <bean id="tfileDAO" class="sshfile.dao.TfileDAOHibernate">
33. <property name="hibernateTemplate" ref="hibernateTemplate" />
34. </bean>
35. …
36. </beans> |
第3~9行定义了一个数据源,其实现类是apache的BasicDataSource,第11~25行定义了Hibernate的会话工厂,会话工厂类用Spring提供的LocalSessionFactoryBean维护,它注入了数据源和资源映射文件,此外还通过一些键值对设置了Hibernate所需的属性。
其中第16行通过类路径的映射方式,将sshfile.model类包目录下的所有领域对象的映射文件装载进来,在本文的例子里,它将装载进Tfile.hbm.xml映射文件。如果有多个映射文件需要声明,使用类路径映射方式显然比直接单独指定映射文件名的方式要简便。
第27~30行定义了Spring代理Hibernate数据操作的HibernateTemplate模板,而第32~34行将该模板注入到tfileDAO中。
需要指定的是Spring 1.2.5提供了两套Hibernate的支持包,其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中,需要根据您所选用Hibernate版本进行正确选择。
3、Lob字段处理的配置
我们前面已经指出Oracle的Lob字段和一般类型的字段在操作上有一个明显的区别--那就是你必须首先通过Oracle的empty_blob()/empty_clob()初始化Lob字段,然后获取该字段的引用,通过这个引用更改其值。所以要完成对Lob字段的操作,Hibernate必须执行两步数据库访问操作,先Insert再Update。
使用BlobByteArrayType字段类型后,为什么我们就可以象一般的字段类型一样操作Blob字段呢?可以确定的一点是:BlobByteArrayType不可能逾越Blob天生的操作方式,原来是BlobByteArrayType数据类型本身具体数据访问的功能,它通过LobHandler将两次数据访问的动作隐藏起来,使Blob字段的操作在表现上和其他一般字段业类型无异,所以LobHandler即是那个"苦了我一个,幸福十亿人"的那位幕后英雄。
LobHandler必须注入到Hibernate会话工厂sessionFactory中,因为sessionFactory负责产生与数据库交互的Session。LobHandler的配置如代码 5所示:
代码 5 Lob字段的处理句柄配置
1. <beans>
2. …
3. <bean id="nativeJdbcExtractor"
4. class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
5. lazy-init="true"/>
6. <bean id="lobHandler"
7. class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">
8. <property name="nativeJdbcExtractor">
9. <ref local="nativeJdbcExtractor"/>
10. </property>
11. </bean>
12. …
13. </beans> |
首先,必须定义一个能够从连接池中抽取出本地数据库JDBC对象(如OracleConnection,OracleResultSet等)的抽取器:nativeJdbcExtractor,这样才可以执行一些特定数据库的操作。对于那些仅封装了Connection而未包括Statement的简单数据连接池,SimpleNativeJdbcExtractor是效率最高的抽取器实现类,但具体到apache的BasicDataSource连接池,它封装了所有JDBC的对象,这时就需要使用CommonsDbcpNativeJdbcExtractor了。Spring针对几个著名的Web服务器的数据源提供了相应的JDBC抽取器:
·WebLogic:WebLogicNativeJdbcExtractor
·WebSphere:WebSphereNativeJdbcExtractor
·JBoss:JBossNativeJdbcExtractor
在定义了JDBC抽取器后,再定义lobHandler。Spring 1.2.5提供了两个lobHandler:
·DefaultLobHandler:适用于大部分的数据库,如SqlServer,MySQL,对Oracle 10g也适用,但不适用于Oracle 9i(看来Oracle 9i确实是个怪胎,谁叫Oracle 公司自己都说Oracle 9i是一个过渡性的产品呢)。
·OracleLobHandler:适用于Oracle 9i和Oracle 10g。
由于我们的数据库是Oracle9i,所以使用OracleLobHandler。
在配置完LobHandler后, 还需要将其注入到sessionFactory的Bean中,下面是调用后的sessionFactory Bean的配置:
代码 6 将lobHandler注入到sessionFactory中的配置
1. <beans>
2. …
3. <bean id="sessionFactory"
4. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
5. <property name="dataSource" ref="dataSource"/>
6. <!-- 为处理Blob类型字段的句柄声明 //-->
7. <property name="lobHandler" ref="lobHandler"/>
8. …
9. </bean>
10. …
11. </beans> |
如第7所示,通过sessionFactory的lobHandler属性进行注入。
posted @
2008-04-10 14:55 阿伟 阅读(285) |
评论 (0) |
编辑 收藏
转自:http://lihaiyan.javaeye.com/blog/127794
引言
文件的上传和下载在J2EE编程已经是一个非常古老的话题了,也许您马上就能掰着指头数出好几个著名的大件:如SmartUpload、Apache的FileUpload。但如果您的项目是构建在Struts+Spring+Hibernate(以下称SSH)框架上的,这些大件就显得笨重而沧桑了,SSH提供了一个简捷方便的文件上传下载的方案,我们只需要通过一些配置并辅以少量的代码就可以完好解决这个问题了。
本文将围绕SSH文件上传下载的主题,向您详细讲述如何开发基于SSH的Web程序。SSH各框架的均为当前最新版本:
·Struts 1.2
·Spring 1.2.5
·Hibernate 3.0
本文选用的数据库为Oracle 9i,当然你可以在不改动代码的情况下,通过配置文件的调整将其移植到任何具有Blob字段类型的数据库上,如MySQL,SQLServer等。
总体实现
上传文件保存到T_FILE表中,T_FILE表结构如下:
420){this.resized=true;this.style.width=420;}" border=0>
图 1 T_FILE表结构
|
其中:
·FILE_ID:文件ID,32个字符,用Hibernate的uuid.hex算法生成。
·FILE_NAME:文件名。
·FILE_CONTENT:文件内容,对应Oracle的Blob类型。
·REMARK:文件备注。
文件数据存储在Blob类型的FILE_CONTENT表字段上,在Spring中采用OracleLobHandler来处理Lob字段(包括Clob和Blob),由于在程序中不需要引用到oracle数据驱动程序的具体类且屏蔽了不同数据库处理Lob字段方法上的差别,从而撤除程序在多数据库移植上的樊篱。
1.首先数据表中的Blob字段在Java领域对象中声明为byte[]类型,而非java.sql.Blob类型。
2.数据表Blob字段在Hibernate持久化映射文件中的type为org.springframework.orm.hibernate3.support.BlobByteArrayType,即Spring所提供的用户自定义的类型,而非java.sql.Blob。
3.在Spring中使用org.springframework.jdbc.support.lob.OracleLobHandler处理Oracle数据库的Blob类型字段。
通过这样的设置和配置,我们就可以象持久化表的一般字段类型一样处理Blob字段了。
以上是Spring+Hibernate将文件二进制数据持久化到数据库的解决方案,而Struts通过将表单中file类型的组件映射为ActionForm中类型为org.apache.struts.upload. FormFile的属性来获取表单提交的文件数据。
综上所述,我们可以通过图 2,描绘出SSH处理文件上传的方案:
420){this.resized=true;this.style.width=420;}" border=0>
图 2 SSH处理文件上传技术方案
|
文件上传的页面如图 3所示:
420){this.resized=true;this.style.width=420;}" border=0 resized="true">
图 3 文件上传页面
|
文件下载的页面如图 4所示:
420){this.resized=true;this.style.width=420;}" border=0 resized="true">
图 4 文件下载页面
|
该工程的资源结构如图 5所示:
420){this.resized=true;this.style.width=420;}" border=0>
图 5 工程资源结构
|
工程的类按SSH的层次结构划分为数据持久层、业务层和Web层;WEB-INF下的applicationContext.xml为Spring的配置文件,struts-config.xml为Struts的配置文件,file-upload.jsp为文件上传页面,file-list.jsp为文件列表页面。
本文后面的章节将从数据持久层->业务层->Web层的开发顺序,逐层讲解文件上传下载的开发过程。
posted @
2008-04-10 14:48 阿伟 阅读(338) |
评论 (1) |
编辑 收藏
转自:http://ttitfly.javaeye.com/blog/133000
Spring配置初始化ApplicationContext
1. 在struts-config.xml里,以插件的形式
xml 代码
< plug-in className="org.springframework.web.struts.ContextLoaderPlugIn" / >
< set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" / >
< / plug-in >
这种方式如果没有配置contextConfigLocation的值,则会自动加载xx-servlet.xml.
xx的值是和web.xml里的配置org.apache.struts.action.ActionServlet的servlet-name的值一样
如下:xx的值也就是 action,所以会自动加载action-servlet.xml
xml 代码
< servlet >
< servlet-name >action< / servlet-name >
< servlet-class >org.apache.struts.action.ActionServlet< / servlet-class >
< load-on-startup >1< / load-on-startup >
< / servlet >
< servlet-mapping >
< servlet-name >action< / servlet-name >
< url-pattern >*.do< / url-pattern >
< / servlet-mapping >
如果sturts-config.xml里配置了contextConfigLocation的值,那么就不会自动加载xx-servlet.xml了,而只会加载contextConfigLocation所指定的xml.
2. 第2种方式
在web.xml里配置Listener
xml 代码
<listener>
< listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
< / listener>
如果在web.xml里给该Listener指定要加载的xml,如:
xml 代码
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:spring/*.xmlparam-value>
context-param>
则会去加载相应的xml,而不会去加载/WEB-INF/下的applicationContext.xml。。但是,如果没有指定的话,默认会去/WEB-INF/下加载applicationContext.xml。
3. 第三种方式:ContextLoaderServlet
xml 代码
< servlet>
< servlet-name>context< / servlet-name>
< servlet-class>org.springframework.web.context.ContextLoaderServlet< / servlet-class>
< load-on-startup>1< / load-on-startup>
< / servlet>
这种方式和第二种Listener方式一样,唯一的区别就是用Listener方式初始化ApplicationContext,可以和用第一种方式(struts-config.xml里 plugin方式)同时存在,而ContextLoaderServlet则不可以和第一种方式同时存在
总结:
ContextLoaderServlet已经不推荐用了,它只是为了兼容低版本的servlet.jar才用的。
总的来说:Listerner要比Servlet更好一些,而且Listerner监听应用的启动和结束,而Servlet启动要稍微延迟一些。
posted @
2008-04-10 10:20 阿伟 阅读(857) |
评论 (0) |
编辑 收藏
转自:http://blog.tostudy.com.cn/blog/show_3574.html
优化Web应用的性能绝不象有些人想象的那样简单易行,它涉及到诸多技术,从最简单的HTML代码修改,到复杂的EJB改造,无不涉及性能问题。但有一点是非常清楚的:要想找出和解决Web应用的性能瓶颈,就必须深入全面地了解信息在Web应用中的流程。
改善Web应用的性能不一定要局限于Web应用的Java代码,例如有些时候,简单地改动一下HTML页面的质量、减少其传输频度和数据量就可以有效地提高应用的性能表现;有时提高性能的关键却在于修改Web应用的数据库访问部分——这只是Java代码之外影响性能的两个因素,其他还有许多因素会影响到Web应用的整体性能表现。另一方面,就Java程序本身而言,其性能优化又可以分成三个领域:基本的Java代码优化,JSP/Servlet优化,EJB优化。
一、表现层优化
Web应用的最大性能瓶颈常常不在其他地方,而在于最基本的网络带宽限制。如果你的Web应用也面临这类问题,提高性能最简单的办法是减少HTTP传输,例如用JavaScript实现客户端编辑功能以减少数据传输次数,避免将数据发送到服务器端再执行合法性验证之类的编辑操作。
应当采用一切可能措施减少通过网络传输的数据。例如,你可以要求浏览器缓冲模块化的JavaScript文件,在SCRIPT标记的SRC中指定:
SCRIPT LANGUAGE="JavaScript" SRC="FormChek.js"。
其他减少网络传输应当注意的地方还包括:避免过度使用隐藏域,减少超长Cookie值,在RADIO、CHECKBOX和SELECT域中用代码来替代长长的字符串,等等。不过在HTML优化方面本文不准备作全面的讨论,因为WebSphere应用的开发者一般不会担负设计表现层的责任,只要了解下面这个原理就足够了:
性能技巧之一:尽可能减少HTTP数据传输的总量和频度
二、数据库访问
朋友小A对Java的了解极为有限,但他却成功地改进了许多WebSphere应用的性能。他是怎么做到的呢?原来,小A是一个数据库专家,他通过优化数据库访问有效地改进了整个应用的性能,但对于Java,他只是略微了解一些有关JDBC的知识。在优化数据库访问时,小A做的第一件事情总是检查数据库的设计,有时他会建议重新构造数据库的结构(必须指出的是,为了提高性能而重新构造数据库结构有时可能使数据库反规格化(De-Normalization),从而带来维护方面的问题)。
性能技巧之二:规格化(Normalization)数据库结构
小A做的第二件事情是执行数据库分析,根据分析结果提出增加某个索引、减少某个索引的建议。完成这一步骤后,小A通常可以让应用有令人满意的性能表现,根本不必去查看应用的Java代码。
性能技巧之三:针对常用的SQL操作建立索引,删除多余的索引
有时,为了进一步优化应用的性能,小A会检查Java(也许应该说是SQL)代码,经常找到Java程序没有合理运用PreparedStatement和连接缓冲池的情形。只要把Statement类的动态SQL替换成PreparedStatement类的静态SQL,从连接池提取SQL连接(而不是直接创建连接),应用的性能将得到显著的改善。注意DB2 UDB(包括其他一些数据库)的PreparedStatement是可调整和配置的。
性能技巧之四:合理运用PreparedStatement和连接池
进一步分析应用的工作流程之后,小A有时会建议批量执行某些SQL命令,这样就只需一个对数据库服务器的请求就可以运行大量的SQL命令。
性能技巧之五:考虑批量执行SQL命令
既然如此,小A有时还会指出,如果应用中有些SQL命令可以组合成单个事务逻辑,那么应该可以用一个存储过程来替代。DB2 UDB的存储过程语言(SPL,Stored Procedure Language)非常强大,如果把数据库操作逻辑从Web应用转移到数据库,一般总是对性能有益。不过需要注意的是,虽然批量执行SQL命令或使用存储过程会提高性能,但就象重新构造数据库结构一样,有时会带来维护方面的困难。
性能技巧之六:考虑使用数据库存储过程
检查JDBC代码的时候,小A总是留意对象有没有及时正确释放。这一点其实很重要。
性能技巧之七:及时关闭不用的Statement、ResultSet、Connection等对象(但不是在finalize方法内)
三、Java代码
前面我们以小A的经验为例,探讨了Web应用中数据库访问性能的重要性。调整好数据库之后,接下来要做的自然是深入分析应用的Java代码。从哪里入手呢?你最好使用Java分析工具来找出性能问题的焦点所在。优化Java代码的性能是一个艰苦的过程,因此一个重要的原则是把精力集中到那些可能引起性能问题的代码上。换句话说,就是要尊重80/20规则:利用Java分析工具的结果,调整带来80%性能开销的那20%代码。
性能技巧之八:用Java分析工具清楚地界定性能问题所在
目前市场上已经有许多优秀的Java分析工具,例如ej-technologie的JProfile(http://www.ej-technologies.com),Klgroup的Jprobe(http://www.klg.com),以及Intuitive Systems的OptimizeIt(http://www.optimizeit.com)。不过不要忘记WebSphere Studio Application Developer(WSAD)本身也集成了一个优秀的分析器,有条件的话,最好多用几种分析工具分析Java代码。
考虑到资金问题,你不一定乐意购买昂贵的分析软件,但你可以用Java本身的命令行工具生成分析信息。例如,在JDK 1.3中,你可以用下面的命令将TestOrderProcessing类的CPU使用情况保存到java.hprof文件:java -Xrunhprof:cpu=times,format=a,file=java.hprof TestOrderProcessing。
这种办法的缺点是它提供的信息条理不够清楚,比较繁杂;也许可以找到一些源代码开放的工具辅助分析,但一般不如使用WSAD本身的分析工具或商业化的分析工具方便。另外,如果你已经了解哪些代码块可能引起性能问题,可以通过保存系统时间的方式获得分析信息,例如:
long startTime = System.currentTimeMillis();
// 执行某些操作
long endTime = System.currentTimeMillis();
3.1 基本篇
有人建议“稳定性第一,速度第二”,一般而言遵从这个建议是不会错的,但这并不妨碍我们在编写代码的同时运用某些已经证实的性能技巧。例如,我们都知道String类是不可变的,连接两个String是一项开销很大操作。
性能技巧之九:用StringBuffer来连接两个字符串
也许你已经注意到,SUN的许多标准Java类是线程安全的,这些类内部的同步机制实际上很容易造成性能问题。例如,Vector类就是一个线程安全的类,除非确实要用到同步机制,否则使用Vector是不值得的,如有可能,应当尽量改用非线程安全的类如ArrayList。
性能技巧之十:只有在必要时才运用线程安全的类
许多人习惯使用System.out.println来输出跟踪信息,但println要占用不少资源,所以输出跟踪信息最好使用专用日志记录框架,如IBM的JRas或Apache的Log4j。
性能技巧之十一:用日志记录框架类输出跟踪信息,而不是使用System.out.println
最后一个提高代码性能的简单技巧是清除类里面的调试信息,减小类的体积。IBM有一个WSAD插件,它提供了一个叫做setDebugInfo的任务,可以从Ant脚本调用。
性能技巧之十二:从正式发行的软件中删除调试信息
posted @
2008-04-08 13:03 阿伟 阅读(198) |
评论 (0) |
编辑 收藏