MIS系统中的数据查询时必不可少的。而且大部分都是多表查询,这时候SQL语句中的Join就显得尤为重要。再加上现在的O-R mapping,更是给查询带来了一个不大不小的麻烦(我不是说O-R mapping的坏话)。以下的部分将用一个本人认为很典型的例子说明一下。
数据库结构如下:

用户界面如下:

由数据库结构和界面的grid可以很容易想到,直接写上
select FACILITY.*, PROJECT.CODE as PROJECT_CODE, PROJECT.CN_NAME as PROJECT_CN_NAME, SITE.NAME as SITE_NAME, SITE.CID as SITE_CID,
FACILITY_CATALOG.CN_NAME as FACILITY_CATALOG_NAME from FACILITY left outer join PROJECT on FACILITY.MASTER_ID = PROJECT.ID left outer join SITE on PROJECT.SITE_GUID = SITE.ID left outer FACILITY_CATALOG on FACILITY.CATALOG_GUID = FACILITY_CATALOG.ID
。然后在后头写上where搜索里选择的条件。这似乎没什么难度吧。
这个项目典型的地方是使用了一个可爱的框架,绑定在grid上的不是DataTable而是一个EntityCollection,而且这里还用了延迟加载,即使在SQL语句中写上了PROJECT.CODE, PROJECT.CN_NAME,由于延迟加载的关系都被忽略了(Facility这个实体对象里包含MASTER_ID,利用延迟加载来获取实体对象Project)。所以SQL语句就得写成了
select FACILITY.* from FACILITY left outer join PROJECT on FACILITY.MASTER_ID = PROJECT.ID left outer join SITE on PROJECT.SITE_GUID = SITE.ID left outer FACILITY_CATALOG on FACILITY.CATALOG_GUID = FACILITY_CATALOG.ID
至于后面的条件还是和前面一样加。我们可以想象一下这么做的问题:1、虽然用户只选择了站点这个条件,但是FACILITY和FACILITY_CATALOG进行了Join。2、如果用户只选择了设备编目,SQL语句就多了FACILITY和PROJECT、SITE的Join。当然啦,看到这里有人可能会说,我们用的框架没有这个问题或者你不会自己用if做判断啊。换框架没有必要,这里只是拿出一个典型的例子,相信就算换了框架,在开发过程中还是会遇到类似问题的。至于用if进行判断,同样也有麻烦,这个例子里的表比较少,写起判断没什么太大问题。关键是用户选择工程名称时你得加入FACILITY和PROJECT的Join,用户选择站点名称时你除了加入FACILITY和SITE的Join外,还必须先添加FACILITY和PROJECT的Join,否则就出错没商量。
不知大家看到这个例子的特殊性没?本人见过一个项目组用这个框架的时候,一个SQL语句里一次性写了28个Join(自己汗一下先,怎么设计数据库的,虽说是该有不该有的都写了,也没有这么多吧)。当然这不排除数据库设计和框架的问题,其中还有一部分原因就是懒人多,表多条件多的时候谁都不喜欢一个if一个if拼SQL语句。说了这么多,总结起来就是“按需Join的问题”。如果能有一个工具能实现,一次性添加所需的所有东西,然后自动根据需要生成SQL语句,该多好(或者说方便多了,至少本人是这么认为的)?
如果SQL语句进行分解。可以划分为显示的字段定义、表关联定义、条件定义、排序、分组等几个部分。因此,本人对SQL语句的各部分进行如下划分,并以以下术语命名:
1、 Fields:输出字段的定义。
2、 Joins:表关联定义。
3、 Filters:查询条件定义。
4、 GroupBys:分组定义。
5、 Having:Having子句
6、 Existis:Exists子句。
7、 OrderBys:排序定义。
8、 SubQuerys:子查询定义
9、 UsedTables:查询中所使用的表。
10、UsedJoins:查询中所使用的联结
11、 QueryInfo:一条完整的SQL查询语句。
12、 QueryBuilder:查询生成器。
因此SQL语句可以用如下形式表示:
select (Fileds) from (UsedTables | SubQuerys| UsedJoins] [where [Exists] Filters] [group by GroupBys] [having Having] [order by OrderBys]
注:黑体字表示SQL保留字。“()”表示必填项。“|”表示可在其中选择一项。“[]”表示可选项。
经过分解后可以得出这样的结论,UsedTables中的表由所有的Fields、Exists、Filters、GroupBys、Having、OrderBys中所使用的表组成,SubQuerys也可以根据Alias从UsedTables判断是否使用。UsedJoins也可以用同样的方法来实现按需选取,复杂的地方是一个Join可能依赖另一个Join,就像例子里的FACILITY和SITE的那样。另外,Fields为空的时候必须转换为“*”,UsedJoins不为空时From部分用Join,UsedJoins为空时from部分则必须用UsedTables或SubQuerys来代替。
暂时先到这,眼花脖子酸,下回给出对象图和实现思路。
JSCFO当前版本号2.8。实现功能如下:
1、可同时对多个文件进行操作。
2、格式化代码。
3、合并文件。
4、变量名混淆(选择变量名混淆后会随机在if、for、while、dowhile中插入混淆用的判断)。
5、流程混淆。可选择打乱流程时进行分块的行数。(由于Javascript中没有goto,所以打乱代码后用for或while配合if或switch保证代码执行顺序。for或while和if或switch的配合有一定随机性,较难被找到规律。此功能会造成代码增大,不过没有办法)
6、字符串混淆。(目前方法较简单,后期考虑用压缩替代混淆)
7、数字混淆。(用数学表达式替代常数)
8、压缩。(简单的方法,Pack2的那种)
9、格式化和混淆时自动修正结尾没有加分号的情况。
10、修改正则表达式的写法,避免所有代码在同一行时发生错误。
界面如下:

试用版中ObfuscateOptions(混淆选项)不可设置,默认使用了变量混淆、流程混淆、字符串混淆、数字混淆;流程混淆时Lines(方块的行数)默认为1,压缩也不启用。Format Only(仅格式化)和Merge(合并)选项可自由设置。
使用方法:1、用File菜单下的Add和Remove菜单项增加删除文件。2、混淆选项可以不用选择,如果需要合并多个文件则钩选Merge,如果仅需要格式化代码请钩选Format Only。3、点击Obfuscate菜单下的Go进行混淆。如果Output Folder(输出目录)中有与源文件同名的文件,则在混淆后的文件名前加“Obfuscated_”。4、双击Files(文件列表)中的项可以在新窗口中显示所选择的JS文件格式化后的代码。
混淆前的代码:
var x = 0;
var z = false;
function Test()
{
for(var i=0; i<x; i++)
{
if(z)
{
var s = "Today's date is: ";
var d = new Date();
s += (d.getMonth() + 1) + "/"; //Get month
s += d.getDate() + "/"; //Get day
s += d.getYear();
alert(i+ ":" + s);
}
}
}
x = 10;
z = true;
Test();
混淆后的代码(未压缩):
var $_A, x, z, $_E;
function Test()
{
for(var i = ((0x36 * 0x0) + 0x0); i < x; i++)
{
if(z)
{
var s, d, $_D;
$_D = 0;
for(var $_D = 5; $_D >= 0; $_D--)
{
if($_D == ((0x22 * 0x3) - 0x66))
{
alert((i + $_A("%3B")) + s);
}
if($_D == ((0x4f * 0x3) - 0xec))
{
s += d.getYear();
}
if($_D == ((0x3 * 0x3) - 0x4))
{
s = $_A("e%C3%D3%C5%DA%A0%9A%93%84%C5%D5%D9%85%89%DC%ADZ");
}
if($_D == ((0x42 * 0x7) - 0x1cc))
{
s += (d.getDate() + $_A("0"));
}
if($_D == ((0x26 * 0x2) - 0x49))
{
s += ((d.getMonth() + ((0x8 * 0x2) - 0xf)) + $_A("0"));
}
if($_D == ((0x1d * 0x7) - 0xc7))
{
d = new Date();
}
}
}
}
}
$_A = function ($_B)
{
var $_C;
$_B = unescape($_B);
$_C = String.fromCharCode($_B.charCodeAt(0) - $_B.length);
for(var i = 1; i < $_B.length; i++)
{
$_C += String.fromCharCode($_B.charCodeAt(i) - $_C.charCodeAt(i - 1));
}
return $_C;
};
$_E = 5;
while($_E > 0)
{
$_E--;
if($_E == ((0x3a * 0x7) - 0x193))
{
z = false;
}
if($_E == ((0x30 * 0x0) + 0x1))
{
z = true;
}
if($_E == ((0x0 * 0x2) + 0x0))
{
Test();
}
if($_E == ((0x1e * 0x3) - 0x56))
{
x = ((0x27 * 0x5) - 0xc3);
}
if($_E == ((0x26 * 0x2) - 0x4a))
{
x = ((0x55 * 0x1) - 0x4b);
}
}
压缩后的代码如下:
eval(function(e,d){e=e.replace(new RegExp('\\b\\w+\\b','g'), function($0){return d[$0]});return e;}('0 $1=2($3){$3=4($3);0 $5=6.7($3.8(9)-$3.10);11(0 12=13;12<$3.10;12++){$5+= 6.7($3.8(12)-$5.8(12-13));}14 $5;};0 $15,$16;2 17(){11(0 12 = (18*19-20);(12 < $15) && !(21.$22>23);12++){24($16){0 $25,$26;$25 = $1(\"27%28%29%30%31%32%33%34%35%30%36%37%38%39%40%41\");$26 = 42 43();$25 += (($26.44() + (45*18-46)) + $1(\"9\"));$25 += ($26.47() + $1(\"9\"));$25 += $26.48();49(((12 + $1(\"%50\")) + $25));}}}$15 = (51*52-53);$16 = 54;$15=(55*18-56);$16=57;17();', 'var|_A|function|_B|unescape|_C|String|fromCharCode|charCodeAt|0|length|for|i|1|return|_D|_E|Test|0x2|0x4|0x8|this|_F|2|if|_G|_H|e|C3|D3|C5|DA|A0|9A|93|84|D5|D9|85|89|DC|ADZ|new|Date|getMonth|0x4b|0x95|getDate|getYear|alert|3B|0x52|0x3|0xf6|false|0x12|0x1a|true'.split('|')));
目前的流程混淆利用while或for配合if或switch控制打乱后代码的正常执行顺序,希望有朋友能提出更好的建议。字符串的加密也让人感到头疼。
混淆后的代码较原有代码增大不少,主要原因是针对每一行代码都进行了打乱并且没有进行压缩处理。进行压缩处理之后,还是可以接受的。
后期目标如下:
1、支持包含<Script language="javascript"></script>标记的文件。(暂定为此标记)
2、用压缩算法替代现在那个简单的字符串混淆方法。
3、尝试寻找新的流程混淆方法。
4、考虑是否加入函数名混淆。(个人目前认为函数名混淆的意义不大)
5、加入混淆排除选项。
6、考察是否有更高效的压缩算法,以替代Pack2目前的方法。
PS:
之前版本有用Themida加壳,发现很多朋友用ESET,故不用Themida,已更新。请想试用的朋友放心,绝无病毒。
至于有人说我不放源码的问题,这个似乎没有规定吧。我这个还只是给大家试用,并没有说要收钱干啥的。所以对源码有意见的请自己保留吧
准备开始新版本的工作,旧版本暂停下载。