??xml version="1.0" encoding="utf-8" standalone="yes"?>
使用 hibernate 快一q了Q一直用得比较肤浅Q甚x有正式用过对象关系。近D|间想深入研究一下,以便在项目中推广Q减不必要的对象维护和~程。问题不期而遇Q在多对多关pMQ出C递规加蝲的现象,例如Q用户和角色的关p,一个用户可能有多个角色Q一个角色中包含多个用户。我是通过带有q接表的多对多关pd现的Q用户和角色对象中都l持了一?/span> Set 对象Q用以gq加载关pR但是,在我延迟加蝲用户拥有的角色时Q被加蝲的角色又加蝲它所包含的用P被加载的用户又加载所拥有的角Ԍq样递规加蝲下去Q由?/span> session 的关闭会抛出异常DE序中止。开始百思不得其解,在仔l查看抛出的异常堆栈Ӟl于发现了问题所在。习惯!错误的习惯。我们所使用的持l层对象会承一个基c,该类“实现”了 hashCode ?/span> equals ҎQ代码如下:
public boolean equals(Object o) { return EqualsBuilder.reflectionEquals(this, o); } public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } |
hibernate 用h拥有的角色对象放q?/span> Set 中,实际 Set 会调?/span> hashCode ?/span> equals 来判断两个对象是否相{,q样问题来了, HashCodeBuilder.reflectionHashCode(this) Ҏ使用反射调用角色对象?/span> getUsers() ҎQ?/span> hibernate 又加载角色所包含的用P能没有问题吗Q而我们得反思一下持l层对象有没有通用?/span> hashCode ?/span> equals Ҏ?/span>
参考《深入浅?/span> Hibernate 》对 hashCode ?/span> equals Ҏ的处理有两大U:
1?span style="FONT: 7pt 'Times New Roman'"> 不覆?/span>
问题Q实体对象的?/span>
session
识别问题Q根本在?/span>
hashCode
默认调用
System.identityHashCode()
Ҏ?/span>
2?span style="FONT: 7pt 'Times New Roman'"> 覆盖
Ø 使用对象 pk
问题Q新增对象时Q没?/span> pk Q那么所有的对象都相{了Q也是只能加入的一条?/span>
Ø 值比对(对实体对象的所有属性D行比对,可以使用 Commonclipse 自动生成Q?/span>
问题Q过于严根{?/font>
Ø 业务关键信息判定
是值比对的一个子集,只做业务关键属性的比对?/font>
个h觉得业务关键信息判定的方法比较合理,使用 Commonclipse 自动生成值比对,注意两点Q?/span>
1?span style="FONT: 7pt 'Times New Roman'"> L实体兌集合属性的比对Q不然又会出现我上述的“递规加蝲”现象?/span>
2?span style="FONT: 7pt 'Times New Roman'">
自动生成?/span>
hashCode
ҎL
appendSuper(super.hashCode())
Q自动生成的
equals
ҎL
appendSuper(super.equals(object))
Q不然你的对象比较和加入
collection
都有问题的,《深入浅?/span>
Hibernate
》书中没有强调?/span>
管Hibernate 3.0 与Hibernate2.1的源代码是不兼容的,但是当Hibernate开发小l在设计Hibernate3.0Ӟ为简化升UHibernate版本作了周到的考虑。对于现有的ZHibernate2.1的Java目Q可以很方便的把它升U到Hibernate3.0?/font>
本文描述了Hibernate3.0版本的新变化QHibernate3.0版本的变化包括三个方面:
Q?QAPI的变化,它将影响到JavaE序代码?br />Q?Q元数据Q它媄响到对象-关系映射文g?br />Q?QHQL查询语句?/font>
值得注意的是Q?Hibernate3.0q不会完全取代Hibernate2.1。在同一个应用程序中Q允许Hibernate3.0和Hibernate2.1q存?/font>
1.1 Hibernate API 变化
1.1.1 包名
Hibernate3.0的包的根路径? “org.hibernate?Q而在Hibernate2.1中ؓ“net.sf.hibernate”。这一命名变化使得Hibernate2.1和Hibernate3.0能够同时在同一个应用程序中q行?/font>
如果希望把已有的应用升到Hibernate3.0Q那么升U的W一步是把Java源程序中的所有“net.sf.hibernate”替换ؓ“org.hibernate”?/font>
Hibernate2.1中的“net.sf.hibernate.expression”包被改名ؓ“org.hibernate.criterion”。假如应用程序用了Criteria APIQ那么在升的过E中Q必LJava源程序中的所有“net.sf.hibernate.expression”替换ؓ“org.hibernate.criterion”?/font>
如果应用使用了除Hibernate以外的其他外部YӞ而这个外部Y件又引用了Hibernate的接口,那么在升U时必须十分心。例如EHCache拥有自己的CacheProviderQ?net.sf.ehcache.hibernate.ProviderQ在q个cM引用了Hibernate2.1中的接口Q在升应用Ӟ可以采用以下办法之一来升UEHCache:
Q?Q手工修改net.sf.ehcache.hibernate.Providerc,使它引用Hibernate3.0中的接口?br />Q?Q等到EHCache软g本n升Z用Hibernate3.0后,使用新的EHCache软g?br />Q?Q用Hibernate3.0中内|的CacheProviderQorg.hibernate.cache.EhCacheProvider?/font>
1.1.2 org.hibernate.classic?/font>
Hibernate3.0把一些被废弃的接口都转移到org.hibernate.classic中?/font>
1.1.3 Hibernate所依赖的第三方软g?/font>
在Hibernate3.0的Y件包的lib目录下的README.txt文g中,描述了Hibernate3.0所依赖的第三方软g包的变化?/font>
1.1.4 异常模型
在Hibernate3.0中,HibernateException异常以及它的所有子c都l承了java.lang.RuntimeException。因此在~译Ӟ~译器不会再查HibernateException?/font>
1.1.5 Session接口
在Hibernate3.0中,原来Hibernate2.1的Session接口中的有些基本Ҏ也被废弃Q但Z化升U,q些Ҏ依然是可用的Q可以通过org.hibernate.classic.Session子接口来讉K它们Q例如:
org.hibernate.classic.Session session=sessionFactory.openSession();
session.delete("delete from Customer ");
在Hibernate3.0中,org.hibernate.classic.Session接口l承了org.hibernate.Session接口Q在org.hibernate.classic.Session接口中包含了一pd被废弃的ҎQ如find()、interate(){。SessionFactory接口的openSession()Ҏq回org.hibernate.classic.Sessioncd的实例。如果希望在E序中完全用Hibernate3.0Q可以采用以下方式创建Session实例Q?/font>
org.hibernate.Session session=sessionFactory.openSession();
如果是对已有的程序进行简单的升Qƈ且希望仍然调用Hibernate2.1中Session的一些接口,可以采用以下方式创徏Session实例Q?/font>
org.hibernate.classic.Session session=sessionFactory.openSession();
在Hibernate3.0中,Session接口中被废弃的方法包括:
* 执行查询的方法:find()、iterate()、filter()和delete(String hqlSelectQuery)
* saveOrUpdateCopy()
Hibernate3.0一律采用createQuery()Ҏ来执行所有的查询语句Q采用DELETE 查询语句来执行批量删除,采用merge()Ҏ来替?saveOrUpdateCopy()Ҏ?
提示Q在Hibernate2.1中,Session的delete()Ҏ有几U重载Ş式,其中参数为HQL查询语句的delete()Ҏ在Hibernate3.0中被废弃Q而参CؓOjbectcd的的delete()Ҏ依然被支持。delete(Object o)Ҏ用于删除参数指定的对象,该方法支持联删除?
Hibernate2.1没有Ҏ量更新和扚w删除提供很好的支持,参见<<_NHibernate>>一书的W?3章的13.1.1节(扚w更新和批量删除)Q而Hibernate3.0Ҏ量更新和扚w删除提供了支持,能够直接执行扚w更新或批量删除语句,无需把被更新或删除的对象先加载到内存中。以下是通过Hibernate3.0执行扚w更新的程序代码:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
以下是通过Hibernate3.0执行扚w删除的程序代码:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
1.1.6 createSQLQuery()
在Hibernate3.0中,Session接口的createSQLQuery()Ҏ被废弃,被移到org.hibernate.classic.Session接口中。Hibernate3.0采用新的SQLQuery接口来完成相同的功能?/font>
1.1.7 Lifecycle ?Validatable 接口
Lifecycle和Validatable 接口被废弃,q且被移到org.hibernate.classic包中?/font>
1.1.8 Interceptor接口
在Interceptor 接口中加入了两个新的Ҏ?用户创徏的Interceptor实现cd升的过E中Q需要ؓq两个新Ҏ提供Ҏ体ؓI的实现。此外,instantiate()Ҏ的参C了修改,isUnsaved()Ҏ被改名ؓisTransient()?/font>
1.1.9 UserType和CompositeUserType接口
在UserType和CompositeUserType接口中都加入了一些新的方法,q两个接口被Udorg.hibernate.usertype包中Q用户定义的UserType和CompositeUserType实现cdd现这些新Ҏ?
Hibernate3.0提供了ParameterizedType接口Q用于更好的重用用户自定义的cd?
1.1.10 FetchModec?/font>
FetchMode.LAZY ?FetchMode.EAGER被废弃。取而代之的分别为FetchMode.SELECT 和FetchMode.JOIN?/font>
1.1.11 PersistentEnumc?/font>
PersistentEnum被废弃ƈ删除。已l存在的应用应该采用UserType来处理枚丄型?/font>
1.1.12 对Blob 和Clob的支?/font>
Hibernate对Blob和Clob实例q行了包装,使得那些拥有Blob或Clobcd的属性的cȝ实例可以被游R序列化或反序列化,以及传递到merge()Ҏ中?/font>
1.1.13 Hibernate中供扩展的API的变?/font>
org.hibernate.criterion?org.hibernate.mapping?org.hibernate.persister和org.hibernate.collection 包的l构和实现发生了重大的变化。多数基于Hibernate
2.1 的应用不依赖于这些包Q因此不会被影响。如果你的应用扩展了q些包中的类Q那么必非常小心的对受影响的程序代码进行升U?/font>
1.2 元数据的变化
1.2.1 索策?/font>
在Hibernate2.1中,lazy属性的默认gؓ“false”,而在Hibernate3.0中,lazy属性的默认gؓ“true”。在升映射文gӞ如果原来的映文件中的有兛_素,?lt;set>?lt;class>{没有显式设|lazy属性,那么必须把它们都昑ּ的设|ؓlazy=“true”。如果觉得这U升U方式很ȝQ可以采取另一单的升方式Q在<hibernate-mapping>元素中设|? default-lazy=“false”?
1.2.2 对象标识W的映射
unsaved-value属性是可选的Q在多数情况下,Hibernate3.0把unsaved-value="0" 作ؓ默认倹{?/font>
在Hibernate3.0中,当用自然主键和游离对象Ӟ不再实现Interceptor.isUnsaved()Ҏ?如果没有讄q个ҎQ当Hibernate3.0无法区分对象的状态时Q会查询数据库,来判断这个对象到底是临时对象Q还是游d象。不q,昑ּ的用Interceptor.isUnsaved()Ҏ会获得更好的性能Q因可以减少Hibernate直接讉K数据库的ơ数?/font>
1.2.3 集合映射
<index>元素在某些情况下?lt;list-index>?lt;map-key>元素替代。此外,Hibernate3.0?lt;map-key-many-to-many> 元素来替代原来的<key-many-to-many>.元素Q用<composite-map-key>元素来替代原来的<composite-index>元素?/font>
1.2.4 DTD
对象-关系映射文g中的DTD文档Q由原来的:
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd
改ؓQ?br />
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd
1.3 查询语句的变?/font>
Hibernate3.0 采用新的ZANTLR的HQL/SQL查询译器,不过QHibernate2.1的查询翻译器也依然存在。在Hibernate的配|文件中Qhibernate.query.factory_class属性用来选择查询译器。例如:
Q?Q选择Hibernate3.0的查询翻译器Q?br />hibernate.query.factory_class= org.hibernate.hql.ast.ASTQueryTranslatorFactory
Q?Q选择Hibernate2.1的查询翻译器
hibernate.query.factory_class= org.hibernate.hql.classic.ClassicQueryTranslatorFactory
提示QANTLR是用UJava语言~写出来的一个编译工P它可生成Java语言或者是C++的词法和语法分析器,q可产生语法分析树ƈ对该树进行遍历。ANTLR׃是纯Java的,因此可以安装在Q意^CQ但是需要JDK的支持?
Hibernate开发小l尽力保证Hibernate3.0的查询翻译器能够支持Hibernate2.1的所有查询语句。不q,对于许多已经存在的应用,在升U过E中Q也不妨仍然使用Hibernate2.1的查询翻译器?br />值得注意的是Q?Hibernate3.0的查询翻译器存在一个BugQ不支持某些theta-styleq结查询方言Q如Oracle8i的OracleDialect方言、Sybase11Dialect。解册一问题的办法有两种Q(1Q改Z用支持ANSI-styleq结查询的方aQ如 Oracle9Dialect,Q?Q如果升U的时候遇到这一问题Q那么还是改Z用Hibernate2.1的查询翻译器?/font>
1.3.1 indices()和elements()函数
在HQL的select子句中废弃了indices()和elements()函数Q因两个函数的语法很让用戯解,可以用显式的q接查询语句来替?select elements(...) 。而在HQL的where子句中,仍然可以使用elements()函数?/font>
?span lang="EN-US">FCKeditor的官方站?a >http://www.fckeditor.net/download上下载FCKeditor 2.3.2和FCKeditor.JavaQ其中FCKeditor 2.3.2是源码,FCKeditor.Java是在jsp中用的例程?o:p>
?span lang="EN-US">FCKeditor 2.3.2解压到FCKeditor文g夹,然后删除如下文g或目录:
1?_samplesQ?_testcasesQ?o:p>
2、删?_documentation.html,_whatsnew.html,fckeditor.afp,fckeditor.asp,fckeditor.cfc,fckeditor.cfm,
fckeditor.lasso,fckeditor.php,fckeditor.pl,fckeditor.py
只剩下fckconfig.js,fckeditor.js,fckstyles.xml,fcktemplates.xml,htaccess.txt,license.txtQ?o:p>
3?editor/_sourceQ?o:p>
4?editor/filemanager/browser/default/connectorsQ?o:p>
5?editor/filemanager/uploadQ?o:p>
6、语a?editor/lang中只留下en.js、zh-cn.jsQ?o:p>
哈哈Q一下子?span lang="EN-US">2.5M瘦到832KQ爽呀Q还可以在皮肤包?editor/skins动动脑筋Q比如只留一个sliverQ这个好配色?o:p>
?span lang="EN-US">FCKeditor的jsp中用的例程和FCKeditor源码l合hQ解压FCKeditor.JavaQ将解压文g夹中web目录下的所有文件拷贝到FCKeditor目录中,q样在FCKeditor目录多了两个目录Q?o:p>
├─_samples
?span lang="EN-US"> ?span style="mso-spacerun: yes"> index.jsp
?span lang="EN-US"> ?span style="mso-spacerun: yes"> sample.css
?span lang="EN-US"> ?span style="mso-spacerun: yes"> sampleslist.jsp
?span lang="EN-US"> ?span style="mso-spacerun: yes">
?span lang="EN-US"> └─jsp
?span lang="EN-US"> sample01.jsp
?span lang="EN-US"> sample02.jsp
?span lang="EN-US"> sample03.jsp
?span lang="EN-US"> sample04.jsp
?span lang="EN-US"> sample05.jsp
?span lang="EN-US"> sample06.config.js
?span lang="EN-US"> sample06.jsp
?span lang="EN-US"> sample07.jsp
?span lang="EN-US"> sampleposteddata.jsp
?span lang="EN-US">
└─WEB-INF
?span style="mso-spacerun: yes"> web.xml
?span style="mso-spacerun: yes">
└─lib
FCKeditor-2.3.jar
commons-fileupload.jar |
看一?span lang="EN-US">web.xml文gQ里面增加了文g览和文件上传得servlet?o:p>
l
修改文gFCKeditor/fckconfig.js
1?span style="FONT: 7pt 'Times New Roman'">
修改属?span lang="EN-US">
中文Q?span lang="EN-US">FCKConfig.DefaultLanguage = 'zh-cn' ;
皮肤Q?span lang="EN-US">FCKConfig.SkinPath = FCKConfig.BasePath + 'skins/default/' ;
2?span style="FONT: 7pt 'Times New Roman'">
使用servlet做文件浏览和上传
参?span lang="EN-US">http://wiki.fckeditor.net/Developer%27s_Guide/Integration/Java
//browser
FCKConfig.LinkBrowserURL = FCKConfig.BasePath + "filemanager/browser/default/browser.html?Connector=connectors/jsp/connector" ;
FCKConfig.ImageBrowserURL = FCKConfig.BasePath + "filemanager/browser/default/browser.html?Type=Image&Connector=connectors/jsp/connector" ;
FCKConfig.FlashBrowserURL = FCKConfig.BasePath + "filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/jsp/connector" ;
//upload
FCKConfig.LinkUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=File' ;
FCKConfig.FlashUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=Flash' ;
FCKConfig.ImageUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=Image' ; |
l
修改web.xml增加FCKeditor的taglib
?span lang="EN-US">FCKeditor.Java解压目录/src目录下拷贝FCKeditor.tld文g到WEB-INF目录下,在web.xml文g中增加:
<taglib>
<taglib-uri>/WEB-INF/FCKeditor.tld</taglib-uri>
<taglib-location>/WEB-INF/FCKeditor.tld</taglib-location>
</taglib> |
?/span> jsp 面中作如下调用Q具体如何用可以看 FCKeditor.tld定义 Q:
...
<%@ taglib uri="/WEB-INF/FCKeditor.tld" prefix="FCK" %>
...
<FCK:editor id="EditorDefault" basePath="/FCKeditor/">
This is FCKeditor demo! </FCK:editor>
... |
我们修改了半天的整?/span>
FCKeditor目录拯到tomcat5.0.28Q或其他web服务器)的webapps目录下,启动tomcat讉Khttp://localhost:8080/FCKeditor/_samples/ 试用一下FCKeditor 2.3.2的强大功能,有了demo在项目中如何使用Q你自己琢磨吧!
例程下蝲Q?/span>
FCKeditor
l
多设计一个页面;
l
用户多增加一步操作(特别是在信息修改面Q;
l
带来面h的重复提交的问题Q?/span>
我们改变一下思\会带来意想不到的效果Q同时也规避了以上问题?/span>
Ҏ一
1?/span>
修改你的信息录入面Q加入如下脚?/span>
…?/span>
<iframe name="tagFrame" style="display:none" frameborder="0"></iframe>
…?/span>
<script language="JavaScript">
<!--
//
保存信息
function saveInfo() {
testForm.action="test.do?method=save";
testForm.target="tagFrame";
testForm.submit();
}
//-->
</script>
|
2?/span>
修改你的提示面
<%@ page contentType="text/html; charset=GBK" %>
<script language="javascript">
alert("
保存成功Q?/span>
");
//
q里是等待你点击
alert
的确定按钮后跌{Q神奇的
js
parent.window.location.href='test.do?method=test';
</script>
|
单改一下就
jsp
收获q不?/span>
Ҏ?/span>
让actionq回javascript完成提示和蟩转:
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
。。?/span>
//
发送的信息
String msg = "alert('
操作成功Q?/span>
');location.href='test.do?method=test';";
writeJsToFrontPage(response, msg);
return null;
}
protected void writeJsToFrontPage(HttpServletResponse response,
String msg) throws IOException {
response.setContentType("text/html; charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter pw = response.getWriter();
pw.write("<SCRIPT TYPE='text/javascript'>" + msg + "</SCRIPT>");
pw.close();
} |
基本思\Q?/STRONG>
在信息新增时对信息进行两个方面的保存Q?、保存到数据库,目的便于信息的修改;2、用velocity生成静态的html文gQ以便浏览。用户可以下载velocity的?vm”模板,q序{变成?html”文件给用户Q用户修改(可以加上css修饰Q后?html”文件上传,q序{变成?vm”文件放到velocity调用的模板目录中?BR>
遇到问题Q?BR>1、log文g生成问题Q?BR>2、编码问题;
3、模板\径问题;
解决ҎQ?BR>在velocity初始化时Q添加以下属性配|:
// 讄velocity的log
Velocity.setProperty(Velocity.RUNTIME_LOG, mainPath + File.separator + "velocity.log");
// 讄velocity的输入输出编?BR> Velocity.setProperty(Velocity.INPUT_ENCODING, "GBK");
Velocity.setProperty(Velocity.OUTPUT_ENCODING, "GBK");
// / 讄velocity的模板\径(必要Q?BR> Velocity.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, mainPath + File.separator + "template";
// 初始化velocity引擎
try {
Velocity.init();
} catch (Exception e) {
e.printStackTrace();
}
其中mainPath 指你的应用发布目录或其他M一个有权限使用目录Q可由配|文件定义?BR>
问题分析Q?/STRONG>
1?点不用说了,说说W?炏V一开始我在调用模板时使用l对路径和相对\径,可是怎么试是不行QL报:Unable to find resource。查看了一下源码,velocity调用的是FileResourceLoaderQ部分源码如下:
public void init( ExtendedProperties configuration)
{
rsvc.info("FileResourceLoader : initialization starting.");
paths = configuration.getVector("path");
/*
* lets tell people what paths we will be using
*/
int sz = paths.size();
for( int i=0; i < sz; i++)
{
rsvc.info("FileResourceLoader : adding path '" + (String) paths.get(i) + "'");
}
rsvc.info("FileResourceLoader : initialization complete.");
}
/**
* Get an InputStream so that the Runtime can build a
* template with it.
*
* @param name name of template to get
* @return InputStream containing the template
* @throws ResourceNotFoundException if template not found
* in the file template path.
*/
public synchronized InputStream getResourceStream(String templateName)
throws ResourceNotFoundException
{
/*
* Make sure we have a valid templateName.
*/
if (templateName == null || templateName.length() == 0)
{
/*
* If we don't get a properly formed templateName then
* there's not much we can do. So we'll forget about
* trying to search any more paths for the template.
*/
throw new ResourceNotFoundException(
"Need to specify a file name or file path!");
}
String template = StringUtils.normalizePath(templateName);
if ( template == null || template.length() == 0 )
{
String msg = "File resource error : argument " + template +
" contains .. and may be trying to access " +
"content outside of template root. Rejected.";
rsvc.error( "FileResourceLoader : " + msg );
throw new ResourceNotFoundException ( msg );
}
/*
* if a / leads off, then just nip that :)
*/
if (template.startsWith("/"))
{
template = template.substring(1);
}
int size = paths.size();
for (int i = 0; i < size; i++)
{
String path = (String) paths.get(i);
InputStream inputStream = findTemplate(path, template);
if (inputStream != null)
{
/*
* Store the path that this template came
* from so that we can check its modification
* time.
*/
templatePaths.put(templateName, path);
return inputStream;
}
}
/*
* We have now searched all the paths for
* templates and we didn't find anything so
* throw an exception.
*/
String msg = "FileResourceLoader Error: cannot find resource " +
template;
throw new ResourceNotFoundException( msg );
}
可见?STRONG>一定要讄
// / 讄velocity的模板\径(必要Q?BR> Velocity.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, mainPath + File.separator + "template";
1?SPAN style="FONT: 7pt 'Times New Roman'"> 假如你要提交的页面ؓtoSubmit.jspQ?o:p>
2?SPAN style="FONT: 7pt 'Times New Roman'"> 在打开toSubmit.jsp的Action1中加入:saveToken(request)Q例?o:p>
public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { //生成同步令牌 saveToken(request); return mapping.findForward("toSubmit"); } |
3?SPAN style="FONT: 7pt 'Times New Roman'"> 在提?SPAN lang=EN-US>toSubmit.jsp的Action2中加入:isTokenValid(request, true)Q例如:
public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // 验证同步令牌 if (isTokenValid(request, true)) { //执行提交操作 }else { // 重复提交 return mapping.findForward("Error"); } } |
4?SPAN style="FONT: 7pt 'Times New Roman'"> 使用注意Q?SPAN lang=EN-US>toSubmit.jsp中的form必须使用struts的标{?lt;html:form>?o:p>
二、基本原?SPAN lang=EN-US>
W一步、在session中放入同步o?o:p>
?SPAN lang=EN-US>Action1中加入了saveToken(request)的方法后Q调用TokenProcessorcȝsaveTokenҎ如下Q?o:p>
public synchronized void saveToken(HttpServletRequest request) { HttpSession session = request.getSession(); String token = generateToken(request); if (token != null) { session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token); } } |
很明昑֜session中放入了同步令牌Q名UCؓGlobals.TRANSACTION_TOKEN_KEY?o:p>
W二步、在面创徏hidden元素
当应用服务器初始?SPAN lang=EN-US>toSubmit.jsp面遇到标签<html:form>Ӟ便会调用struts的FormTagc,其中有一个方法:
protected String renderToken() { StringBuffer results = new StringBuffer(); HttpSession session = pageContext.getSession(); if (session != null) { String token = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY); if (token != null) { results.append("<input type=\"hidden\" name=\""); results.append(Constants.TOKEN_KEY); results.append("\" value=\""); results.append(token); if (this.isXhtml()) { results.append("\" />"); } else { results.append("\">"); } } } return results.toString(); } |
其意为:当检到session中的Globals.TRANSACTION_TOKEN_KEY不ؓI时Q在toSubmit.jsp面创徏元素Q?o:p>
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value=""> |
名称为:org.apache.struts.taglib.html.TOKEN是Constants.TOKEN_KEYQ?o:p>
gؓQ?SPAN lang=EN-US>session中的Globals.TRANSACTION_TOKEN_KEY的|即ؓ同步令牌倹{?o:p>
W三步、验证同步o?SPAN lang=EN-US>
?SPAN lang=EN-US>Action2中加入isTokenValidҎQ实际上是调用TokenProcessorcȝisTokenValidҎ如下Q?o:p>
public synchronized boolean isTokenValid( HttpServletRequest request, boolean reset) { // Retrieve the current session for this request HttpSession session = request.getSession(false); if (session == null) { return false; } // Retrieve the transaction token from this session, and // reset it if requested String saved = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY); if (saved == null) { return false; } if (reset) { this.resetToken(request); } // Retrieve the transaction token included in this request String token = request.getParameter(Constants.TOKEN_KEY); if (token == null) { return false; } return saved.equals(token); } |
它首先取?SPAN lang=EN-US>session中的令牌|然后resetTokenQ再从页面hidden元素取来令牌|q行比较Q如果相{则为第一ơ,不等则ؓ重复提交?o:p>
其中resetTokenҎ如下Q?o:p>
public synchronized void resetToken(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return; } session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY); } |
在信息发布系l中Q如果需要将用户发布的信息保存到Oralcle9i数据库中Q?/SPAN>varchar2(4000)有时是不够用的,因ؓ除了用户录入的内容还要加?/SPAN>html~辑器附加的html标识Q所以我使用clob?/SPAN>
动手
其实也挺单的Q两个配|、两个注?/FONT>
1?/SPAN>hbm配置
<hibernate-mapping> <class name="com.wonders.pubinfo.bean.LawInfo" table="JZ_LAW_INFO"> <id column="ID" name="id" type="java.lang.String"> <generator class="uuid.hex"/> </id> <property column="TYPE" length="4" name="type" type="java.lang.String"/> <property column="TITLE" length="200" name="title" type="java.lang.String"/> <property column="KEYWORD" length="200" name="keyword" type="java.lang.String"/> <property column="CONTENT" name="content" type="org.springframework.orm.hibernate.support.ClobStringType"/> <property column="PUBLISH_TIME" length="23" name="publishTime" type="java.sql.Timestamp"/> <property column="PRACTICE_TIME" length="23" name="practiceTime" type="java.sql.Timestamp"/> <property column="END_TIME" length="23" name="endTime" type="java.sql.Timestamp"/> <property column="FILE_NUM" length="50" name="fileNum" type="java.lang.String"/> <property column="PUB_ORGAN" length="50" name="pubOrgan" type="java.lang.String"/> <property column="TEMPLATE" length="20" name="template" type="java.lang.String"/> <property column="VALIDITY" length="1" name="validity" type="java.lang.String"/> <property column="HTML_FILE" length="50" name="htmlFile" type="java.lang.String"/> </class> </hibernate-mapping> |
2?/SPAN>application配置
<beans> <!--**** Start of PERSISTENCE DEFINITIONS ****--> <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"> <value>init.properties</value> </property> </bean>
<!-- JDBC Connection --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>${datasource.driverClassName}</value> </property> <property name="url"> <value>${datasource.url}</value> </property> <property name="username"> <value>${datasource.username}</value> </property> <property name="password"> <value>${datasource.password}</value> </property> <property name="maxActive"> <value>${datasource.maxActive}</value> </property> <property name="maxIdle"> <value>${datasource.maxIdle}</value> </property> <property name="maxWait"> <value>${datasource.maxWait}</value> </property> <property name="defaultAutoCommit"> <value>${datasource.defaultAutoCommit}</value> </property> </bean>
<!--**** OracleLobHandler ****--> <bean id="oracleLobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler"> <property name="nativeJdbcExtractor"> <ref local="nativeJdbcExtractor"/> </property> </bean> <bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor"></bean>
<!--**** SessionFactory Definition ****--> <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource"> <ref local="dataSource" /> </property> <property name="lobHandler"> <ref bean="oracleLobHandler" /> </property> <property name="mappingResources"> <list> <value>com\wonders\pubinfo\bean\LawInfo.hbm.xml</value> <value>com\wonders\pubinfo\bean\LawType.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.jdbc.fetch_size">${hibernate.jdbc.fetch_size}</prop> <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop> </props> </property> </bean> </beans> |
3、注?/SPAN>
Q?/SPAN>1Q、注意红色字体部分,是你要配置的;在引?/SPAN>springcȝ地方Q注意查看一下你使用?/SPAN>spring包中有的做相应修攏V?/SPAN>
Q?/SPAN>2Q、注意用的datasource一定?/SPAN>NativeJdbcExtractorQ就是适用jdbcq接Q不要用容器提供q接Q不然会报如下错误:
OracleLobCreator needs to work on [oracle.jdbc.OracleConnection], not on [class com.ibm.ws.rsadapter.jdbc.WSJdbcConnection] - specify a corresponding NativeJdbcExtractor |
4?/SPAN>bean中的clob字段使用Stringcd
public class LawInfo implements Serializable { ... private String content; ... public String getContent() { return content; } public void setContent(String content) { this.content = content; } ... } |