吕克让的资料库 - lhelper's repository

克己服人,礼智谦让!
Weblcome to lhelper's repository!

Tuesday, July 04, 2006

 

TrackChanges.js:用JavaScript 记录用户在表单中所作的修改

原理:
1.为form 中的每个field (submit/reset/button/image除外)生成一个虚假的INPUT element,用于保存页面装载时form 中每个element的初始值。

2.在form 提交之前,比较form 中的每个field 与上面一步生成的虚假的INPUT element,记录二者的差别(diff)。然后合并所有的差别并赋值到另一个新的INPUT element中。

这样,用户在前台操作过程中所修改的内容就可以随form 一起提交到服务器端进行处理了。

1. trackChange.js



/**********************************************************
TrackChanges v0.1 By lhelper <lhelper at gmail dot com>



track changes of client end form fields
1. append dummy input fields onto the raw form, which has
the origianl value, checked status, etc. as the real one

2. compare the dummy input fields with the originals, record
the changes (unix/linux diff format) with one more real
field, whose name is '_tc_c' (default)
**********************************************************/
var TrackChanges={
initialize : function _init(frm) {
if(!frm) { // smart check
return;
}
var eles=frm.elements;
for(var i=0, n=eles.length; i<n; i++) {
var ele=eles[i];
// alert(ele.tagName + ":" + ele.type + ":" + ele.name + ":" + ele.value);
// 1. omit the submit button (submit, reset, button, image, etc.)
if(ele.tagName=="INPUT") {
if(ele.type=="submit" ele.type=="reset" ele.type=="button" ele.type=="image") {
continue;
}
if(ele.type!="checkbox" && ele.type!="radio" && document.getElementsByName(ele.name).length > 1) {
alert("There are " + document.getElementsByName(ele.name).length + " field elements with the same name (" + ele.name + "), which can result in unbelievable output!");
return false;
}
}



// 2. append dummy field _tc_f[ele.name][ele.id][0] onto the form
var obj=document.createElement("INPUT");
obj.type="hidden";
//obj.type="text";
if(ele.tagName=="INPUT" && (ele.type=="checkbox" ele.type=="radio")) {
if(!ele.id) {
ele.id=ele.name + "_" + ele.value;
}
obj.name="_tc_f[" + ((ele.type=="checkbox") ? "checkbox" : "radio") + "][" + ele.name + "][" + ele.id + "][0]";
obj.checked=ele.checked;
} else {
if(!ele.id) {
ele.id=ele.name;
}
obj.name="_tc_f[input][" + ele.name + "][" + ele.id + "][0]";
}
obj.value=ele.value;
obj.disabled=true;
frm.appendChild(obj);
}
},



recordChanges : function _diff(frm, fn) {
if(!frm) { // smart check
return;
}
var cmsDiff="";
for(var eles=frm.elements, i=0; i<eles.length; i++) {
var re=new RegExp("_tc_f\\[(.*?)\\]\\[(.*?)\\]\\[(.*?)\\]\\[0\\]");
var m=re.exec(eles[i].name);
if(m == null) {
continue;
}
if(m[1] == "checkbox" m[1] == "radio") {
if(document.getElementById(m[3]).checked != eles[i].checked) {
cmsDiff+="< " + m[3] + ":" + eles[i].checked + "\n----------\n" + "> " + m[3] + ":" + !eles[i].checked + "\n\n";;
}
} else {
if(document.getElementById(m[3]).value != eles[i].value) {
cmsDiff+="< " + m[3] + ":" + eles[i].value + "\n----------\n" + "> " + m[3] + ":" + document.getElementById(m[3]).value + "\n\n";;
}
}
}
//alert(cmsDiff);
var obj=document.createElement("INPUT");
obj.type="hidden";
//obj.type="text";
obj.name=(fn) ? fn : "_tc_c";
obj.value=cmsDiff;
frm.appendChild(obj);
}
};




2. 使用方法:



<script language=JavaScript>
<!--
function _submit(frm) {
// 记录用户修改的内容
TrackChanges.recordChanges(frm);


frm.method="post";
frm.action="news_edit_save.jsp";
return true;
}
// -->
</script>
......



<form method="POST" action="/dev/null" name="news_info" onSubmit="return _submit(this)">
<input type="text" name="news_title" maxlength="50" value="">
......
<input type="submit" value="确认录入" name="btnSave" class="submit">
</form>


<script language="javascript">
<!-- //
// 初始化 TackChanges
TrackChanges.initialize(document.news_info);
// -->
</script>


 

Hibernate 的 "SQL insert, update or delete failed (row not found)" 异常

关键字:hibernate, trigger(触发器), store procedure(存储过程), NonBatchingBatcher,rowCounts, updateCount


在借助hibernate[1] 执行更新操作,包括插入(insert)/修改(update)/删除(delete)操作的过程中,如果数据库一方返回的update count 与 hibernate 所预期的 count[2] 不同,那么hibernate 的Batcher 就会抛出异常:



HibernateException("SQL insert, update or delete failed (row not found)");

下面是一段完整的错误信息:


[ERROR][tcpConnection-8080-3] - Could not synchronize database state with session
org.springframework.orm.hibernate.HibernateSystemException: SQL insert, update or delete failed (row not found); nested exception is net.sf.hibernate.HibernateException: SQL insert, update or delete failed (row not found)
net.sf.hibernate.HibernateException: SQL insert, update or delete failed (row not found)
at net.sf.hibernate.impl.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:25)

该异常通常是由于更新过程中所涉及的触发器(trigger) 或存储过程(store procedure) 中又包含有更新操作,使得数据库返回的update count 为整个过程中所涉及的所有count 的总和。


一般情况下可以通过在触发器或存储过程中加入 'SET NOCOUNT ON' 声明的方式来解决问题,如:
CREATE TRIGGER tri_t1_delete
ON t1
FOR DELETE
AS
SET NOCOUNT ON -- set 'NOCOUNT' to ON, so the count (indicating the number of rows affected by a Transact-SQL statement) is not returned.
DELETE t2
FROM t2 AS a INNER JOIN deleted AS b
ON a.id=b.id



但似乎也有例外,如:
http://sourceforge.net/forum/message.php?msg_id=3074706



下面是hibernate(2.1)的NonBatchingBatcher (Batcher 的具体实现)比较数据库一方返回的update count 与程序预期的 count 是否相同并伺机抛出异常的程序片断:


public void addToBatch(int expectedRowCount) throws SQLException, HibernateException {
int rowCount = getStatement().executeUpdate();
//negative expected row count means we don't know how many rows to expect
if ( expectedRowCount>0 && expectedRowCount!=rowCount )
throw new HibernateException("SQL insert, update or delete failed (row not found)");
}



注:
[1]: 笔者使用的是2.1 版本的hibernate 发行包
[2]: 通常是 1,


参考:
About 'SET NOCOUNT ON'
http://msdn.microsoft.com/library/en-us/tsqlref/ts_set-set_3ed0.asp?frame=true


 

JavaCC based Simple-QueryParser For Lucene

修改了Lucene 自带的QueryParser,
屏蔽了Token Definitions 中的部分内容,去掉了对:
RangeQuery、
WildcardQuery、
PrefixQuery、
( Query )、
boost、
slop
等的直接支持——以上几项可以改为用Filter 来实现

加入了对不完整表达式的容错处理,减少ParserException 的发生:
Java AND AND Lucene
Java AND "Lucene
Java AND Lucene"
Java AND
AND Lucene
title:
:Java

加入validFieldSet,用于限制可以出现在":" 左侧的有效field,防止:
tom said: "hi everyboy"
主持人:"大家好"
等被解析成错误的Query



如果Analyzer 过滤了所有term 的话,则让parser() 方法返回null,如:
+AND a
+OR NOT the was will
parser() 方法返回null 的情况下,可以仿照google 将请求转向到另外一个页面。


当整个Query 中的所有BooleanClause 都是 prohibited (Pure NOT Query) 的情况下,可以选择:
将第一个clause 的prohibited 属性至为false
在当前clauses 的基础上再加一个MatchAllDocsQuery (http://lucene.apache.org/java/docs/api/org/apache/lucene/search/MatchAllDocsQuery.html)
直接返回pure-NOT-query



屏蔽了部分内容之后的 Token Definitions (/// 为注释部分):


/* ***************** */
/* Token Definitions */
/* ***************** */


<*> TOKEN : {
<#_NUM_CHAR: ["0"-"9"] >
<#_ESCAPED_CHAR: "\\" [ "\\", "+", "-", "!", "(", ")", ":", "^",
"[", "]", "\"", "{", "}", "~", "*", "?" ] >
<#_TERM_START_CHAR: ( ~[ " ", "\t", "\n", "\r", "+", "-", "!", "(", ")", ":", "^",
"[", "]", "\"", "{", "}", "~", "*", "?" ]
<_ESCAPED_CHAR> ) >
<#_TERM_CHAR: ( <_TERM_START_CHAR> <_ESCAPED_CHAR> "-" "+" ) >
/// <#_WHITESPACE: ( " " "\t" "\n" "\r") >
<#_SKIP_CHAR: (" " "\t" "\n" "\r" "(" ")" "{" "}" "[" "]" "^" "~" "*" "?") >
}



/// <DEFAULT, RangeIn, RangeEx> SKIP : {
/// <<_WHITESPACE>>
/// }
<DEFAULT> SKIP : {
<<_SKIP_CHAR>>
}


// OG: to support prefix queries:
// http://nagoya.apache.org/bugzilla/show_bug.cgi?id=12137
// Change from:
// <WILDTERM: <_TERM_START_CHAR>
// (<_TERM_CHAR> ( [ "*", "?" ] ))* >
// To:
//
// <WILDTERM: (<_TERM_CHAR> ( [ "*", "?" ] ))* >



<DEFAULT> TOKEN : {
<AND: ("AND" "&&") >
<OR: ("OR" "") >
<NOT: ("NOT" "!") >
<PLUS: "+" >
<MINUS: "-" >
/// <LPAREN: "(" >
/// <RPAREN: ")" >
<COLON: ":" >
/// <CARAT: "^" > : Boost
<BILATERAL_QUOTED: "\"" (~["\""])+ "\"">
<UNILATERAL_QUOTED: "\"" (~["\""])+>
<QUOTE_AT_THE_END: "\"">
<NUMBER: (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? >
<TERM: <_TERM_START_CHAR> (<_TERM_CHAR>)* >
/// <FUZZY: "~" >
/// <SLOP: "~" (<_NUM_CHAR>)+ >
/// <PREFIXTERM: <_TERM_START_CHAR> (<_TERM_CHAR>)* "*" >
/// <WILDTERM: <_TERM_START_CHAR>
/// (<_TERM_CHAR> ( [ "*", "?" ] ))* >
/// <RANGEIN_START: "[" > : RangeIn
/// <RANGEEX_START: "{" > : RangeEx
}



/// <Boost> TOKEN : {
/// <NUMBER: (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? > : DEFAULT
/// }


/// <RangeIn> TOKEN : {
/// <RANGEIN_TO: "TO">
/// <RANGEIN_END: "]"> : DEFAULT
/// <RANGEIN_QUOTED: "\"" (~["\""])+ "\"">
/// <RANGEIN_GOOP: (~[ " ", "]" ])+ >
/// }



/// <RangeEx> TOKEN : {
/// <RANGEEX_TO: "TO">
/// <RANGEEX_END: "}"> : DEFAULT
/// <RANGEEX_QUOTED: "\"" (~["\""])+ "\"">
/// <RANGEEX_GOOP: (~[ " ", "}" ])+ >
/// }


Archives

June 2004   November 2005   July 2006   August 2006  

This page is powered by Blogger. Isn't yours?