Javascript浮点数比较大小

今天在修改bug的时候发现个关于2个浮点数之和和第3?数比较大小的问题,2个数之和通过人工计算的确和第3个数相等,但是js在判断的时候就变成不相等了,最后发现是2个浮点数相加后出现了精度缺失,正确的比较方式应该如下所示:

return?Math.abs((1*num3)?-?((1*num1)?+?(1*num2)))?<?0.01?true:false;

利用差值的绝对值的精度来判断:

具体就是:f1和f2是两个浮点数,precision是我们自己设置的精度,比如1e-6。

则可以用?fabs(f1-f2)<=precision?来判断f1和f2是否相等。

如果要求更高的精度,则把precision定得更小就行了。

 

UML的9种图及4+1视图简介(整理)

UML中包括九种图:?用例图、类图、对象图、状态图、时序图、协作图、活动图、组件图、配置图
1)用例图(Use?Case?Diagram)
它是UML中最简单也是最复杂的一种图。说它简单是因为它采用了面向对象的思想,又是基于用户视角的,绘制非常容易,简单的图形表示让人一看就懂。说它复杂是因为用例图往往不容易控制,要么过于复杂,要么过于简单。
用例图表示了角色和用例以及它们之间的关系。
2)类图(Class?Diagram)
???????是最常用的一种图,类图可以帮助我们更直观的了解一个系统的体系结构。通过关系和类表示的类图,可以图形化的方式描述一个系统的设计部分。
3)对象图(Object?Diagram)
对象图是类图的实例,几乎使用与类图完全相同的标识。它们的不同点在于对象图显示类的多个对象实例,而不是实例的类。一个对象图是类图的一个实例。由于对象存在生命周期,因此对象图只能在系统某一时间段存在。
4)状态图
描述一个实体基于事件反应的动态行为,显示了该实体如何根据当前所处的状态对不同的时间做出反应的。通常创建一个UML状态图是为了以下的研究目的:研究类、角色、子系统、或组件的复杂行为。
5)时序图
又称顺序图,描述了对象之间动态的交互关系,着重体现对象间消息传递的时间顺序。
顺序图由一组对象构成,每个对象分别带有一条竖线,称作对象的生命线,它代表时间轴,时间沿竖线向下延伸。顺序图描述了这些对象随着时间的推移相互之间交换消息的过程。消息用从一务垂直的对象生命线指向另一个对象的生命线的水平箭头表示。图中还可以根据需要增加有关时间的说明和其他注释。
6)协作图
协作图用于显示组件及其交互关系的空间组织结构,它并不侧重于交互的顺序。协作图显示了交互中各个对象之间的组织交互关系以及对象彼此之间的链接。与序列图不同,协作图显示的是对象之间的关系。另一方面,协作图没有将时间作为一个单独的维度,因此序列号就决定了消息及并发线程的顺序。协作图是一个介于符号图和序列图之间的交叉产物,它用带有编号的箭头来描述特定的方案,以显示在整个方案过程中消息的移动情况。
协作图用途:
通过描绘对象之间消息的移动情况来反映具体的方案。
显示对象及其交互关系的空间组织结构,而非交互的顺序。
7)活动图(Activity?Diagram)
UML活动图记录了单个操作或方法的逻辑,单个用户案例,或者单个业务流程的逻辑。描述系统中各种活动的执行顺序,通常用于描述一个操作中所要进行的各项活动的执行流程。同时,它也常被用来描述一个用例的处理流程,或者某种交互流程。
活动图由一些活动组成,图中同时包括了对这些活动的说明。当一个活动执行完毕之后,控制将沿着控制转移箭头转向下一个活动。活动图中还可以方便地描述控制转移的条件以及并行执行等要求。
8)组件图(Component?Diagram)
组件图是用来反映代码的物理结构。从组件图中,可以了解各软件组件(如源代码文件或动态链接库)之间的编译器和运行时依赖关系。使用组件图可以将系统划分为内聚组件并显示代码自身的结构。
组件图的主要目的是显示系统组件间的结构关系。
9)配置图
配置图描述系统中硬件和软件的物理配置情况和系统体系结构。
在配置图中,用结点表示实际的物理设备,如计算机和各种外部设备等,并根据它们之间的连接关系,将相应的结点连接起来,并说明其连接方式。在结点里面,说明分配给该结点上运行的可执行构件或对象,从而说明哪些软件单元被分配在哪些结点上运行

4+1视图模型概况??

Kruchten?提出了一个”4+1″视图模型,从5个不同的视角包括包括逻辑试图、进程视图、物理视图、开发视图、场景视图来描述软件体系结构。每一个视图只关心系统的一个侧面,5个试图结合在一起才能反映系统的软件体系结构的全部内容。如下图:

通常我们选择UML来表现各种视图,以下列出了UML和各视图的对应关系
4+1视图???????????????????????????????????UML
场景视图????????????????????????????use?case
逻辑视图????????????????????????????类图
开发视图????????????????????????????类图,组件图
进程视图????????????????????????????无完全对应
部署视图????????????????????????????部署图
在架构设计稳定中通常不会给出较多的用例描述,这些是在需求稳定中定义。但是往往架构文档会选择一些用例,列入文档中,这些用例和一些非功能性需求一起用以证明架构的有效和正确性。在逻辑视图中用例的实现是必不可少的一节,尽管架构设计更关注非功能性需求。

 

修改WAMP默认端口

?前几日自己搭建WAMP的环境,在搭建的过程中发现WAMP使用的端口和以前安装的tomcat,apache等软件使用的端口冲突了,在网上查找了一番终于找到了修改的方式。

安装文件下载地址:http://www.wampserver.com/en/?

一、修改APACHE的监听端口,

1、在界面中选apache,弹出隐藏菜单选项,打开配置文件httpd.conf;

2、找到Listen?80?和?ServerName?localhost:80;

3、将80改成801(当然自己也可以设定别的不使用的端口,例如8000等);

4、保存,待重启WAMP服务后即可。

二、修改WAMP打开默认页?Localhost和phpMyadmin的端口:

1、打开wamp目录下wampmanager.tpl文件

2、找到

Parameters:?”http://localhost/”;?Glyph:?5

Parameters:?”http://localhost/phpmyadmin/”;?Glyph:?5

3、改成

Parameters:?http://localhost:801/;?Glyph:?5

Parameters:?”http://localhost:801/phpmyadmin/”;?Glyph:?5

4、保存,重启wamp所有服务即可。

phpmyadmin使用wamp5启动提示密码错误,一般发生在曾经配置过php+apache的机器上,如果记得密码,可以通过以下的方式修改wamp5中phpmyadmin的默认设置,使服务正常。

修改phpmyadmin的默认密码:

1、进入安装盘符下wampapps下的phpmyadmin文件夹

2、打开config.inc文件

3、找到

$cfg[‘Servers’][$i][‘user’]?=?’root’;

$cfg[‘Servers’][$i][‘password’]?=?’这里填写你的密码’;

4、修改好你的密码,保存即可。

 

将javabean转换成xml

使用到的第三方包:org.apache.commons.betwixt

代码片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import org.apache.commons.betwixt.io.BeanReader;
import org.apache.commons.betwixt.io.BeanWriter;
/**
?????* 将javaBean 转成XML文件
?????*
?????* @param bean
?????* javaBean对象
?????* @return XML文件
?????*/
????public static File convertBean2XML(Object bean)
????{
????????File file = new File(FjhlUtils.generateFileName(Consts.XML_FILE_SUFFIX));
????????// FileWriter fw;
????????BeanWriter beanWriter = null;
????????OutputStreamWriter fw;
????????try
????????{
????????????fw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
????????????beanWriter = new BeanWriter(fw);
????????????beanWriter.getXMLIntrospector().getConfiguration()
????????????????????.setAttributesForPrimitives(false);
????????????beanWriter.getBindingConfiguration().setMapIDs(false);
????????????beanWriter.setEndOfLine("\n");
????????????beanWriter.enablePrettyPrint();
????????????beanWriter.setWriteEmptyElements(true);
????????????beanWriter.setEndTagForEmptyElement(true);
????????????beanWriter.writeXmlDeclaration(Consts.XML_TITLE);
????????????beanWriter.write(bean);
????????????beanWriter.flush();
????????}
????????catch (IOException e)
????????{
????????????LOG.error("将javaBean 转成XML文件时发生IO异常", e);
????????}
????????catch (SAXException e)
????????{
????????????LOG.error("将javaBean 转成XML文件时发生SAX解析异常", e);
????????}
????????catch (IntrospectionException e)
????????{
????????????LOG.error("将javaBean 转成XML文件时发生内省异常", e);
????????}
????????finally
????????{
????????????try
????????????{
????????????????if (beanWriter != null)
????????????????{
????????????????????beanWriter.close();
????????????????????beanWriter = null;
????????????????}
????????????}
????????????catch (IOException e)
????????????{
????????????????LOG.error("转成XML文件时,关闭beanWriter失败",e);
????????????}
????????}
????????return file;
????}
??
??
?/**
?????* 将XML文件转成javaBean
?????*
?????* @param xmlFile
?????* XML文件
?????* @param clazz
?????* 生成javaBean Class对象
?????* @return Object javaBean对象
?????*/
????public static Object convertXML2Bean(File xmlFile, Class<?> clazz)
????{
????????Object bean = null;
????????BeanReader beanReader = new BeanReader();
????????beanReader.getXMLIntrospector().getConfiguration()
????????????????.setAttributesForPrimitives(false);
????????beanReader.getBindingConfiguration().setMapIDs(false);
????????try
????????{
????????????beanReader.registerBeanClass(clazz);
????????????bean = beanReader.parse(xmlFile);
????????}
????????catch (IntrospectionException e)
????????{
????????????LOG.error("将XML文件转成javaBean时发生内省异常", e);
????????}
????????catch (IOException e)
????????{
????????????LOG.error("将XML文件转成javaBean时发生IO异常", e);
????????}
????????catch (SAXException e)
????????{
????????????LOG.error("将XML文件转成javaBean时发生SAX解析异常", e);
????????}
????????return bean;
????}

将一组文件压缩成压缩文件

本文是用Java语言实现,使用的是java的类库。

实现的代码片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
?????* 把一组文件压缩到指定的文件
?????*
?????* @param files
?????* 文件组
?????* @param zipFileName
?????* 要压缩成的文件名(包括路径)
?????* @param sPath
?????* ZIP中的路径
?????* @return 压缩文件
?????*/
????public static File compressFiles(File[] files,
String zipFileName,String sPath)
????{
????????String sUploadPath = "test";
????????File zipFile = new File(zipFileName);
????????FileOutputStream fout = null;
????????ZipOutputStream zipOut = null;
????????ZipInputStream zipIn = null;
????????FileInputStream fin = null;
????????byte[] buff = new byte[1024];
????????int rb;
????????try
????????{
????????????if (zipFile.exists())
????????????{
????????????????File fileBak =
?????????????????new File(zipFile.getAbsolutePath() + ".bak");
????????????????if (zipFile.renameTo(fileBak))
????????????????{
????????????????????logger.debug("重命名文件成功:" + fileBak.getName());
????????????????}
????????????????fin = new FileInputStream(fileBak);
????????????????zipIn = new ZipInputStream(fin);
????????????????fout = new FileOutputStream(zipFile);
????????????????zipOut = new ZipOutputStream(fout);
????????????????ZipEntry ze = null;
????????????????try
????????????????{
????????????????????while ((ze = zipIn.getNextEntry()) != null)
????????????????????{
????????????????????????zipOut.putNextEntry(ze);
????????????????????????while ((rb = zipIn.read(buff)) != -1)
????????????????????????{
????????????????????????????zipOut.write(buff, 0, rb);
????????????????????????}
????????????????????}
????????????????}
????????????????catch (Exception e)
????????????????{
????????????????????logger.error("aa", e);
????????????????}
????????????????finally
????????????????{
????????????????????safeCloseInputStream(fin);
????????????????????safeCloseInputStream(zipIn);
????????????????????safeDeleteFile(fileBak);
????????????????}
????????????}
????????????else
????????????{
????????????????fout = new FileOutputStream(zipFile);
????????????????zipOut = new ZipOutputStream(fout);
????????????}
????????????ZipEntry ze = null;
????????????for (int i = 0; i < files.length; i++)
????????????{
????????????????if (files[i] != null)
????????????????{
????????????????????try
????????????????????{
????????????????????????if (!files[i].exists())
????????????????????????{
????????????????????????????logger.error(
?????????????????????????????files[i].getAbsolutePath() + "不存在");
????????????????????????????continue;
????????????????????????}
????????????????????????fin = new FileInputStream(files[i]);
????????????????????????if (StringUtils.isBlank(sPath))
????????????????????????{
????????????????????????????ze = new ZipEntry(files[i].getName());
????????????????????????}
????????????????????????else
????????????????????????{
?????????????????????????StringBuffer sbZipEntry = new StringBuffer();
?????????????????????????sbZipEntry
?????????????????????????????.append(sPath)
?????????????????????????????.append(File.separator)
?????????????????????????????.append(files[i].getAbsolutePath()
?????????????????????????????.substring(sUploadPath.length()));
?????????????????????????ze = new ZipEntry(sbZipEntry.toString());
????????????????????????}
????????????????????????zipOut.putNextEntry(ze);
????????????????????????while ((rb = fin.read(buff)) != -1)
????????????????????????{
????????????????????????????zipOut.write(buff, 0, rb);
????????????????????????}
????????????????????}
????????????????????finally
????????????????????{
????????????????????????safeCloseInputStream(fin);
????????????????????}
????????????????}
????????????}
????????????logger.info("文件压缩成功:" + zipFile.getName());
????????????return zipFile;
????????}
????????catch (IOException e)
????????{
????????????logger.error(e);
????????????safeDeleteFile(zipFile);
????????????return null;
????????}
????????finally
????????{
????????????safeCloseOutStream(zipOut);
????????????safeCloseOutStream(fout);
????????}
????}

 

十步完全理解SQL(转载)

昨天晚上在CSDN上看到极客头条上有个关于SQL的帖子,然后进去看了一下,发现写的非常好,对于SQL的理解非常透彻,看完后感觉又加深了对于SQL的理解,所以在这里转载过来希望大家也学习学习,说不定会对SQL理解地更透彻。如果想看原文,请点击这里

很多程序员视?SQL?为洪水猛兽。SQL?是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言、面向对象的程序语言、甚至是函数语言(尽管有些人认为?SQL?语言也是一种函数式语言)。

我们每天都在写?SQL?并且应用在开源软件?jOOQ?中。于是我想把?SQL?之美介绍给那些仍然对它头疼不已的朋友,所以本文是为了以下读者而特地编写的:

1、?在工作中会用到?SQL?但是对它并不完全了解的人。

2、?能够熟练使用?SQL?但是并不了解其语法逻辑的人。

3、?想要教别人?SQL?的人。

本文着重介绍?SELECT?句式,其他的?DML?(Data?Manipulation?Language?数据操纵语言命令)将会在别的文章中进行介绍。

10个简单步骤,完全理解SQL

1?SQL?是一种声明式语言

首先要把这个概念记在脑中:“声明”。?SQL?语言是为计算机声明了一个你想从原始数据中获得什么样的结果的一个范例,而不是告诉计算机如何能够得到结果。这是不是很棒?

(译者注:简单地说,SQL?语言声明的是结果集的属性,计算机会根据?SQL?所声明的内容来从数据库中挑选出符合声明的数据,而不是像传统编程思维去指示计算机如何操作。)

1 SELECT?first_name,?last_name?FROM?employees?WHERE?salary?>?100000

上面的例子很容易理解,我们不关心这些雇员记录从哪里来,我们所需要的只是那些高薪者的数据(译者注:?salary>100000?)。

我们从哪儿学习到这些?

如果?SQL?语言这么简单,那么是什么让人们“闻?SQL?色变”?主要的原因是:我们潜意识中的是按照命令式编程的思维方式思考问题的。就好像这样:“电脑,先执行这一步,再执行那一步,但是在那之前先检查一下是否满足条件?A?和条件?B?”。例如,用变量传参、使用循环语句、迭代、调用函数等等,都是这种命令式编程的思维惯式。

2?SQL?的语法并不按照语法顺序执行

SQL?语句有一个让大部分人都感到困惑的特性,就是:SQL?语句的执行顺序跟其语句的语法顺序并不一致。SQL?语句的语法顺序是:

  • SELECT[DISTINCT]
  • FROM
  • WHERE
  • GROUP?BY
  • HAVING
  • UNION
  • ORDER?BY

为了方便理解,上面并没有把所有的?SQL?语法结构都列出来,但是已经足以说明?SQL?语句的语法顺序和其执行顺序完全不一样,就以上述语句为例,其执行顺序为:

  • FROM
  • WHERE
  • GROUP?BY
  • HAVING
  • SELECT
  • DISTINCT
  • UNION
  • ORDER?BY

关于?SQL?语句的执行顺序,有三个值得我们注意的地方:

1、?FROM?才是?SQL?语句执行的第一步,并非?SELECT?。数据库在执行?SQL?语句的第一步是将数据从硬盘加载到数据缓冲区中,以便对这些数据进行操作。(译者注:原文为“The?first?thing?that?happens?is?loading?data?from?the?disk?into?memory,?in?order?to?operate?on?such?data.”,但是并非如此,以?Oracle?等常用数据库为例,数据是从硬盘中抽取到数据缓冲区中进行操作。)

2、?SELECT?是在大部分语句执行了之后才执行的,严格的说是在?FROM?和?GROUP?BY?之后执行的。理解这一点是非常重要的,这就是你不能在?WHERE?中使用在?SELECT?中设定别名的字段作为判断条件的原因。

1

2

3

SELECT?A.x?+?A.y?AS?z

FROM?A

WHERE?z?=?10?–?z?在此处不可用,因为SELECT是最后执行的语句!

如果你想重用别名z,你有两个选择。要么就重新写一遍?z?所代表的表达式:

1

2

3

SELECT?A.x?+?A.y?AS?z

FROM?A

WHERE?(A.x?+?A.y)?=?10

…或者求助于衍生表、通用数据表达式或者视图,以避免别名重用。请看下文中的例子。

3、?无论在语法上还是在执行顺序上,?UNION?总是排在在?ORDER?BY?之前。很多人认为每个?UNION?段都能使用?ORDER?BY?排序,但是根据?SQL?语言标准和各个数据库?SQL?的执行差异来看,这并不是真的。尽管某些数据库允许?SQL?语句对子查询(subqueries)或者派生表(derived?tables)进行排序,但是这并不说明这个排序在?UNION?操作过后仍保持排序后的顺序。

注意:并非所有的数据库对?SQL?语句使用相同的解析方式。如?MySQL、PostgreSQL和?SQLite?中就不会按照上面第二点中所说的方式执行。

我们学到了什么?

既然并不是所有的数据库都按照上述方式执行?SQL?预计,那我们的收获是什么?我们的收获是永远要记得:?SQL?语句的语法顺序和其执行顺序并不一致,这样我们就能避免一般性的错误。如果你能记住?SQL?语句语法顺序和执行顺序的差异,你就能很容易的理解一些很常见的?SQL?问题。

当然,如果一种语言被设计成语法顺序直接反应其语句的执行顺序,那么这种语言对程序员是十分友好的,这种编程语言层面的设计理念已经被微软应用到了?LINQ?语言中。

3?SQL?语言的核心是对表的引用(table?references

由于?SQL?语句语法顺序和执行顺序的不同,很多同学会认为SELECT?中的字段信息是?SQL?语句的核心。其实真正的核心在于对表的引用。

根据?SQL?标准,FROM?语句被定义为:

1 <from?clause>?::=?FROM?<table?reference>?[?{?<comma>?<table?reference>???}…?]

FROM?语句的“输出”是一张联合表,来自于所有引用的表在某一维度上的联合。我们们慢慢来分析:

1 FROM?a,?b

上面这句?FROM?语句的输出是一张联合表,联合了表?a?和表?b?。如果?a?表有三个字段,?b?表有?5?个字段,那么这个“输出表”就有?8?(?=5+3)个字段。

这个联合表里的数据是?a*b,即?a?和?b?的笛卡尔积。换句话说,也就是?a?表中的每一条数据都要跟?b?表中的每一条数据配对。如果?a?表有3?条数据,?b?表有?5?条数据,那么联合表就会有?15?(?=5*3)条数据。

FROM?输出的结果被?WHERE?语句筛选后要经过?GROUP?BY?语句处理,从而形成新的输出结果。我们后面还会再讨论这方面问题。

如果我们从集合论(关系代数)的角度来看,一张数据库的表就是一组数据元的关系,而每个?SQL?语句会改变一种或数种关系,从而产生出新的数据元的关系(即产生新的表)。

我们学到了什么?

思考问题的时候从表的角度来思考问题提,这样很容易理解数据如何在?SQL?语句的“流水线”上进行了什么样的变动。

4灵活引用表能使?SQL?语句变得更强大

灵活引用表能使?SQL?语句变得更强大。一个简单的例子就是?JOIN?的使用。严格的说?JOIN?语句并非是?SELECT?中的一部分,而是一种特殊的表引用语句。?SQL?语言标准中表的连接定义如下:

1

2

3

4

<table?reference>?::=

<table?name>

|?<derived???table>

|?<joined???table>

就拿之前的例子来说:

1 FROM?a,?b

a?可能输如下表的连接:

1 a1?JOIN?a2?ON?a1.id?=?a2.id

将它放到之前的例子中就变成了:

1 FROM?a1?JOIN?a2?ON?a1.id?=?a2.id,?b

尽管将一个连接表用逗号跟另一张表联合在一起并不是常用作法,但是你的确可以这么做。结果就是,最终输出的表就有了?a1+a2+b?个字段了。

(译者注:原文这里用词为?degree?,译为维度。如果把一张表视图化,我们可以想象每一张表都是由横纵两个维度组成的,横向维度即我们所说的字段或者列,英文为columns;纵向维度即代表了每条数据,英文为?record?,根据上下文,作者这里所指的应该是字段数。)

在?SQL?语句中派生表的应用甚至比表连接更加强大,下面我们就要讲到表连接。

我们学到了什么?

思考问题时,要从表引用的角度出发,这样就很容易理解数据是怎样被?SQL?语句处理的,并且能够帮助你理解那些复杂的表引用是做什么的。

更重要的是,要理解?JOIN?是构建连接表的关键词,并不是?SELECT?语句的一部分。有一些数据库允许在?INSERT?、?UPDATE?、?DELETE?中使用?JOIN?。

5?SQL?语句中推荐使用表连接

我们先看看刚刚这句话:

1 FROM?a,?b

高级?SQL?程序员也许学会给你忠告:尽量不要使用逗号来代替?JOIN?进行表的连接,这样会提高你的?SQL?语句的可读性,并且可以避免一些错误。

利用逗号来简化?SQL?语句有时候会造成思维上的混乱,想一下下面的语句:

1

2

3

4

5

FROM?a,?b,?c,???d,?e,?f,?g,?h

WHERE?a.a1?=?b.bx

AND?a.a2?=???c.c1

AND?d.d1?=???b.bc

–?etc…

我们不难看出使用?JOIN?语句的好处在于:

  • 安全。?JOIN?和要连接的表离得非常近,这样就能避免错误。
  • 更多连接的方式,JOIN?语句能去区分出来外连接和内连接等。

我们学到了什么?

记着要尽量使用?JOIN?进行表的连接,永远不要在?FROM?后面使用逗号连接表。

6?SQL?语句中不同的连接操作

SQL?语句中,表连接的方式从根本上分为五种:

  • EQUI?JOIN
  • SEMI?JOIN
  • ANTI?JOIN
  • CROSS?JOIN
  • DIVISION

EQUI?JOIN

这是一种最普通的?JOIN?操作,它包含两种连接方式:

  • INNER?JOIN(或者是?JOIN?)
  • OUTER?JOIN(包括:?LEFT?、?RIGHT、?FULL?OUTER?JOIN)

用例子最容易说明其中区别:

1

2

3

4

5

6

7

8

9

10

–?This?table?reference???contains?authors?and?their?books.

–?There?is?one?record?for???each?book?and?its?author.

–?authors?without?books?are???NOT?included

author?JOIN?book?ON?author.id?=?book.author_id

 

–?This?table?reference???contains?authors?and?their?books

–?There?is?one?record?for???each?book?and?its?author.

–?…?OR?there?is?an???”empty”?record?for?authors?without?books

–?(“empty”???meaning?that?all?book?columns?are?NULL)

author?LEFT?OUTER?JOIN?book?ON?author.id?=?book.author_id

SEMI?JOIN

这种连接关系在?SQL?中有两种表现方式:使用?IN,或者使用?EXISTS。“?SEMI?”在拉丁文中是“半”的意思。这种连接方式是只连接目标表的一部分。这是什么意思呢?再想一下上面关于作者和书名的连接。我们想象一下这样的情况:我们不需要作者?/?书名这样的组合,只是需要那些在书名表中的书的作者信息。那我们就能这么写:

1

2

3

4

5

6

7

–?Using?IN

FROM?author

WHERE?author.id?IN?(SELECT?book.author_id?FROM?book)

 

–?Using?EXISTS

FROM?author

WHERE?EXISTS?(SELECT?1?FROM?book?WHERE?book.author_id?=?author.id)

尽管没有严格的规定说明你何时应该使用?IN?,何时应该使用?EXISTS?,但是这些事情你还是应该知道的:

  • IN比?EXISTS?的可读性更好
  • EXISTS?比IN?的表达性更好(更适合复杂的语句)
  • 二者之间性能没有差异(但对于某些数据库来说性能差异会非常大)

因为使用?INNER?JOIN?也能得到书名表中书所对应的作者信息,所以很多初学者机会认为可以通过?DISTINCT?进行去重,然后将?SEMI?JOIN?语句写成这样:

1

2

3

4

–?Find?only?those?authors???who?also?have?books

SELECT?DISTINCT?first_name,?last_name

FROM?author

JOIN?book?ON?author.id?=?book.author_id

这是一种很糟糕的写法,原因如下:

  • SQL?语句性能低下:因为去重操作(?DISTINCT?)需要数据库重复从硬盘中读取数据到内存中。(译者注:?DISTINCT?的确是一种很耗费资源的操作,但是每种数据库对于?DISTINCT?的操作方式可能不同)。
  • 这么写并非完全正确:尽管也许现在这么写不会出现问题,但是随着?SQL?语句变得越来越复杂,你想要去重得到正确的结果就变得十分困难。

更多的关于滥用?DISTINCT?的危害可以参考这篇博文

http://blog.jooq.org/2013/07/30/10-common-mistakes-java-developers-make-when-writing-sql/)。

ANTI?JOIN

这种连接的关系跟?SEMI?JOIN?刚好相反。在?IN?或者?EXISTS?前加一个?NOT?关键字就能使用这种连接。举个例子来说,我们列出书名表里没有书的作者:

1

2

3

4

5

6

7

–?Using?IN

FROM?author

WHERE?author.id?NOT?IN?(SELECT?book.author_id?FROM?book)

 

–?Using?EXISTS

FROM?author

WHERE?NOT?EXISTS?(SELECT?1?FROM?book?WHERE?book.author_id?=?author.id)

关于性能、可读性、表达性等特性也完全可以参考?SEMI?JOIN。

这篇博文介绍了在使用?NOT?IN?时遇到?NULL?应该怎么办,因为有一点背离本篇主题,就不详细介绍,有兴趣的同学可以读一下

http://blog.jooq.org/2012/01/27/sql-incompatibilities-not-in-and-null-values/)。

CROSS?JOIN

这个连接过程就是两个连接的表的乘积:即将第一张表的每一条数据分别对应第二张表的每条数据。我们之前见过,这就是逗号在?FROM?语句中的用法。在实际的应用中,很少有地方能用到?CROSS?JOIN,但是一旦用上了,你就可以用这样的?SQL语句表达:

1

2

–?Combine?every?author?with???every?book

author?CROSS?JOIN?book

DIVISION

DIVISION?的确是一个怪胎。简而言之,如果?JOIN?是一个乘法运算,那么?DIVISION?就是?JOIN?的逆过程。DIVISION?的关系很难用?SQL?表达出来,介于这是一个新手指南,解释?DIVISION?已经超出了我们的目的。但是有兴趣的同学还是可以来看看这三篇文章

http://blog.jooq.org/2012/03/30/advanced-sql-relational-division-in-jooq/

http://en.wikipedia.org/wiki/Relational_algebra#Division

https://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/)。

推荐阅读?→_→?画图解释SQL联合语句

我们学到了什么?

学到了很多!让我们在脑海中再回想一下。?SQL?是对表的引用,?JOIN?则是一种引用表的复杂方式。但是?SQL?语言的表达方式和实际我们所需要的逻辑关系之间是有区别的,并非所有的逻辑关系都能找到对应的?JOIN?操作,所以这就要我们在平时多积累和学习关系逻辑,这样你就能在以后编写?SQL?语句中选择适当的?JOIN?操作了。

7?SQL?中如同变量的派生表

在这之前,我们学习到过?SQL?是一种声明性的语言,并且?SQL?语句中不能包含变量。但是你能写出类似于变量的语句,这些就叫做派生表:

说白了,所谓的派生表就是在括号之中的子查询:

1

2

–?A?derived?table

FROM?(SELECT?*?FROM?author)

需要注意的是有些时候我们可以给派生表定义一个相关名(即我们所说的别名)。

1

2

–?A?derived?table?with?an???alias

FROM?(SELECT?*?FROM?author)?a

派生表可以有效的避免由于?SQL?逻辑而产生的问题。举例来说:如果你想重用一个用?SELECT?和?WHERE?语句查询出的结果,这样写就可以(以?Oracle?为例):

1

2

3

4

5

6

7

8

–?Get?authors’?first?and???last?names,?and?their?age?in?days

SELECT?first_name,?last_name,?age

FROM?(

SELECT?first_name,?last_name,?current_date?-?date_of_birth???age

FROM?author

)

–?If?the?age?is?greater???than?10000?days

WHERE?age?>?10000

需要我们注意的是:在有些数据库,以及?SQL?:?1990?标准中,派生表被归为下一级——通用表语句(?common?table?experssion)。这就允许你在一个?SELECT?语句中对派生表多次重用。上面的例子就(几乎)等价于下面的语句:

1

2

3

4

5

6

7

WITH?a?AS?(

SELECT?first_name,?last_name,?current_date?-???date_of_birth?age

FROM?author

)

SELECT?*

FROM?a

WHERE?age?>?10000

当然了,你也可以给“?a?”创建一个单独的视图,这样你就可以在更广泛的范围内重用这个派生表了。更多信息可以阅读下面的文章(http://en.wikipedia.org/wiki/View_%28SQL%29)。

我们学到了什么?

我们反复强调,大体上来说?SQL?语句就是对表的引用,而并非对字段的引用。要好好利用这一点,不要害怕使用派生表或者其他更复杂的语句。

8?SQL?语句中?GROUP?BY?是对表的引用进行的操作

让我们再回想一下之前的?FROM?语句:

1 FROM?a,?b

现在,我们将?GROUP?BY?应用到上面的语句中:

1 GROUP?BY?A.x,?A.y,?B.z

上面语句的结果就是产生出了一个包含三个字段的新的表的引用。我们来仔细理解一下这句话:当你应用?GROUP?BY?的时候,?SELECT?后没有使用聚合函数的列,都要出现在?GROUP?BY?后面。(译者注:原文大意为“当你是用?GROUP?BY?的时候,你能够对其进行下一级逻辑操作的列会减少,包括在?SELECT?中的列”)。

  • 需要注意的是:其他字段能够使用聚合函数:
1

2

3

SELECT?A.x,?A.y,?SUM(A.z)

FROM?A

GROUP?BY?A.x,?A.y

  • 还有一点值得留意的是:?MySQL?并不坚持这个标准,这的确是令人很困惑的地方。(译者注:这并不是说?MySQL?没有?GROUP?BY?的功能)但是不要被?MySQL?所迷惑。?GROUP?BY?改变了对表引用的方式。你可以像这样既在?SELECT?中引用某一字段,也在?GROUP?BY?中对其进行分组。

我们学到了什么?

GROUP?BY,再次强调一次,是在表的引用上进行了操作,将其转换为一种新的引用方式。

9?SQL?语句中的?SELECT?实质上是对关系的映射

我个人比较喜欢“映射”这个词,尤其是把它用在关系代数上。(译者注:原文用词为?projection?,该词有两层含义,第一种含义是预测、规划、设计,第二种意思是投射、映射,经过反复推敲,我觉得这里用映射能够更直观的表达出?SELECT?的作用)。一旦你建立起来了表的引用,经过修改、变形,你能够一步一步的将其映射到另一个模型中。?SELECT?语句就像一个“投影仪”,我们可以将其理解成一个将源表中的数据按照一定的逻辑转换成目标表数据的函数。

通过?SELECT语句,你能对每一个字段进行操作,通过复杂的表达式生成所需要的数据。

SELECT?语句有很多特殊的规则,至少你应该熟悉以下几条:

  1. 你仅能够使用那些能通过表引用而得来的字段;
  2. 如果你有?GROUP?BY?语句,你只能够使用?GROUP?BY?语句后面的字段或者聚合函数;
  3. 当你的语句中没有?GROUP?BY?的时候,可以使用开窗函数代替聚合函数;
  4. 当你的语句中没有?GROUP?BY?的时候,你不能同时使用聚合函数和其它函数;
  5. 有一些方法可以将普通函数封装在聚合函数中;

一些更复杂的规则多到足够写出另一篇文章了。比如:为何你不能在一个没有?GROUP?BY?的?SELECT?语句中同时使用普通函数和聚合函数?(上面的第?4?条)

原因如下:

  1. 凭直觉,这种做法从逻辑上就讲不通。
  2. 如果直觉不能够说服你,那么语法肯定能。?SQL?:?1999?标准引入了?GROUPING?SETS,SQL:?2003?标准引入了?group?sets?:?GROUP?BY()?。无论什么时候,只要你的语句中出现了聚合函数,而且并没有明确的?GROUP?BY?语句,这时一个不明确的、空的?GROUPING?SET?就会被应用到这段?SQL?中。因此,原始的逻辑顺序的规则就被打破了,映射(即?SELECT?)关系首先会影响到逻辑关系,其次就是语法关系。(译者注:这段话原文就比较艰涩,可以简单理解如下:在既有聚合函数又有普通函数的?SQL?语句中,如果没有?GROUP?BY?进行分组,SQL?语句默认视整张表为一个分组,当聚合函数对某一字段进行聚合统计的时候,引用的表中的每一条?record?就失去了意义,全部的数据都聚合为一个统计值,你此时对每一条?record?使用其它函数是没有意义的)。

糊涂了?是的,我也是。我们再回过头来看点浅显的东西吧。

我们学到了什么?

SELECT?语句可能是?SQL?语句中最难的部分了,尽管他看上去很简单。其他语句的作用其实就是对表的不同形式的引用。而?SELECT?语句则把这些引用整合在了一起,通过逻辑规则将源表映射到目标表,而且这个过程是可逆的,我们可以清楚的知道目标表的数据是怎么来的。

想要学习好?SQL?语言,就要在使用?SELECT?语句之前弄懂其他的语句,虽然?SELECT?是语法结构中的第一个关键词,但它应该是我们最后一个掌握的。

10?SQL?语句中的几个简单的关键词:?DISTINCT??UNION??ORDER?BY??OFFSET

在学习完复杂的?SELECT?豫剧之后,我们再来看点简单的东西:

  • 集合运算(?DISTINCT?和?UNION?)
  • 排序运算(?ORDER?BY,OFFSET…FETCH)

集合运算(?set?operation):

集合运算主要操作在于集合上,事实上指的就是对表的一种操作。从概念上来说,他们很好理解:

  • DISTINCT?在映射之后对数据进行去重
  • UNION?将两个子查询拼接起来并去重
  • UNION?ALL?将两个子查询拼接起来但不去重
  • EXCEPT?将第二个字查询中的结果从第一个子查询中去掉
  • INTERSECT?保留两个子查询中都有的结果并去重

排序运算(?ordering?operation):

排序运算跟逻辑关系无关。这是一个?SQL?特有的功能。排序运算不仅在?SQL?语句的最后,而且在?SQL?语句运行的过程中也是最后执行的。使用?ORDER?BY?和?OFFSET…FETCH?是保证数据能够按照顺序排列的最有效的方式。其他所有的排序方式都有一定随机性,尽管它们得到的排序结果是可重现的。

OFFSET…SET是一个没有统一确定语法的语句,不同的数据库有不同的表达方式,如?MySQL?和?PostgreSQL?的?LIMIT…OFFSET、SQL?Server?和?Sybase?的?TOP…START?AT?等。具体关于?OFFSET..FETCH?的不同语法可以参考这篇文章

http://www.jooq.org/doc/3.1/manual/sql-building/sql-statements/select-statement/limit-clause/)。

让我们在工作中尽情的使用?SQL

正如其他语言一样,想要学好?SQL?语言就要大量的练习。上面的?10?个简单的步骤能够帮助你对你每天所写的?SQL?语句有更好的理解。另一方面来讲,从平时常见的错误中也能积累到很多经验。下面的两篇文章就是介绍一些?JAVA?和其他开发者所犯的一些常见的?SQL?错误:

用JAVA基本类库去解析HTML

这几天参加公司的定级考试,有个上机题是

访问URL:?http://www.weather.com.cn/weather/101010100.shtml?页面,提取出页面中的天气信息,然后把信息按照要求输出到控制台。开始想到的是先把html文件存到本地,然后在逐行用正则表达式去解析,后来想想这种方法太土,而且解析起来会很复杂,所以就想用SAX去解析,试了一下,程序执行起来太慢,半天出不了结果,而且网络中的html标签不规则,标签不一定都有结尾,所以解析时会报错。后来在网上搜了一下,原来javax.swing.text.html包中已经提供了解析html标签的类库,在网上参考的别人的博客,地址如下:

http://blog.csdn.net/thamsyangsw/article/details/4389900

程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
package com.thunisoft.kms.java.lvl2.exam;
????
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
????
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLEditorKit.ParserCallback;
import javax.swing.text.html.parser.ParserDelegator;
????
/**
?* Title: <br>
?* Description: <br>
?* Copyright: Copyright (c) 2007<br>
?* Company:<br>
?*
?* @author keep at it
?* @version 1.0
?* @date 2013-12-4
?*/
public class GrapWeatherInfo extends ParserCallback
{
????/** 是否是table标签 */
????protected boolean isTable = false;
????/** 是否是a标签 */
????protected boolean isAlink = false;
????/** 是否是div标签 */
????protected boolean isDiv = false;
????/** 是否是td标签 */
????protected boolean isTd = false;
????/** 放符合条件的元素 */
????protected static Vector<String> element = new Vector<String>();
????protected static String paragraphText = new String();
????/** 要获取文件在网络中的URL */
????private static final String FILE_URL =
????/** 文件在本地磁盘的存储位置 */
????private static final String FILE_LOCATION = "E:/url.html";
????
????/** 构造方法 */
????public GrapWeatherInfo()
????{
????
????}
????
????/**
?????* 开始解析
?????*
?????* @param r
?????*/
????private static void startParse(Reader r)
????{
????????try
????????{
?????????ParserDelegator ps = new ParserDelegator();
?????????????// 负责每次在调用其 parse
?????????????// 方法时启动一个新的
?????????????// DocumentParser
????????????HTMLEditorKit.ParserCallback parser =
new GrapWeatherInfo();// 解析结果驱动这些回调方法。
????????????ps.parse(r, parser, true);
// 解析给定的流并通过解析的结果驱动给定的回调。
????????????Vector<String> link = element;
????????????String temp = "";
????????????for (int i = 1; i < link.size(); i++)
????????????{
????????????????if (link.get(i).contains("星期"))
????????????????{
????????????????????temp = link.get(i);
????????????????}
????????????????if (link.get(i).equals(";"))
????????????????{
????????????????????System.out.println();
????????????????}
????????????????else if (!link.get(i).equals(">"))
????????????????{
????????????????????// Pattern p = Pattern.compile("\\s*|\t|\r|\n");
????????????????????// Matcher m = p.matcher(link.get(i));
????????????????????if (link.get(i).endsWith("夜间")
??????????????????????&& !link.get(i - 1).contains("星期"))
????????????????????{
????????????????????????System.out.println();
????????????????????????System.out.print(temp + "?? ");
????????????????????????System.out.print(link.get(i) + "?? ");
????????????????????}
????????????????????else
????????????????????{
????????????????????????System.out.print(link.get(i) + "?? ");
????????????????????}
????????????????}
????????????}
????
????????}
????????catch (Exception e)
????????{
????????????e.printStackTrace();
????????}
????}
????
????/**
?????* 处理文本
?????*
?????* @param data
?????* @param pos
?????*/
????public void handleText(char[] data, int pos)
????{
????????Pattern p = Pattern.compile("\\s*|\t|\r|\n");
????????Matcher m = null;
????????if (isAlink)
????????{
????????????String tempParagraphText = new String(data);
????????????m = p.matcher(tempParagraphText);
????????????if (paragraphText != null)
????????????{
?????????????// 符合条件的添加到集合中去
??????????????element.addElement(m.replaceAll(""));
????????????}
????????}
????????else if (isTd)
????????{
????????????String tempParagraphText = new String(data);
????????????m = p.matcher(tempParagraphText);
????????????if (paragraphText != null)
????????????{
????????????????// 符合条件的添加到集合中去
????????????????element.addElement(m.replaceAll(""));
????????????}
????????}
????}
????
????/**
?????* 处理开始标签
?????*
?????* @param t
?????* @param a
?????* @param pos
?????*/
????public void handleStartTag(HTML.Tag t,
???????????????????????MutableAttributeSet a, int pos)
????{
????????// System.out.println("start: "+t+"? "
????????????+a.getAttribute(HTML.Attribute.ID)+"? "
????????????+a.getAttribute(HTML.Attribute.CLASS));
????????// 如果是<div/>
????????if (t == HTML.Tag.DIV)
????????{
????????????// 7d 是要解析的div的id属性,用来和其他的div区分
????????????if ("7d".equals(a.getAttribute(HTML.Attribute.ID)))
????????????{
????????????????// 说明是要找的div
????????????????isDiv = true;
????????????}
????????}
????????// 如果是<table/>
????????if (t == HTML.Tag.TABLE)
????????{
????????????// yuBaoTable 是要解析的table的class属性,
????????????//用来和其他的table区分
????????????if ("yuBaoTable".equals(
??????????????????a.getAttribute(HTML.Attribute.CLASS)))
????????????{
????????????????// 说明是要找的table
????????????????isTable = true;
????????????}
????????}
????????// 如果是<a/>,加上是id=7d的限制
????????if (t == HTML.Tag.A && isDiv)
????????{
????
????????????if (a.getAttribute(HTML.Attribute.ID) == null)
????????????{
????????????????if (a.getAttribute(HTML.Attribute.HREF) != null ?
??????????????????a.getAttribute(HTML.Attribute.HREF).toString()
????????????????????????.endsWith(".php") : false)
????????????????{
????????????????????// 说明是要找的<a/>
????????????????????isAlink = true;
????????????????}
????
????????????}
????????}
????????if (t == HTML.Tag.TD && isDiv)
????????{
????????????isTd = true;
????????}
????}
????
????/**
?????* 解析出问题时的处理方法
?????*
?????* @param errorMsg
?????* @param pos
?????*/
????public void handleError(String errorMsg, int pos)
????{
????}
????
????/**
?????* 处理普通tag
?????*
?????* @param t
?????* @param a
?????* @param pos
?????*/
????public void handleSimpleTag(HTML.Tag t,
??????????????????????????MutableAttributeSet a, int pos)
????{
????????handleStartTag(t, a, pos);
????}
????
????/**
?????* getter method
?????*
?????* @return
?????*/
????public static String getParagraphText()
????{
????????return paragraphText;
????}
????
????/**
?????* 处理注释
?????*
?????* @param data
?????* @param pos
?????*/
????public void handleComment(char[] data, int pos)
????{
????}
????
????/**
?????* 处理end tag
?????*
?????* @param t
?????* @param pos
?????*/
????public void handleEndTag(HTML.Tag t, int pos)
????{
????????// System.out.println("end: "+t+"? "+pos);
????????// 如果是<a/>标签
????????if (t == HTML.Tag.A)
????????{
????????????if (isAlink)
????????????{
????????????????isAlink = false;
????????????}
????????}// 如果是<table/>标签
????????else if (t == HTML.Tag.TABLE && isAlink == false)
????????{
????????????if (isTable)
????????????{
????????????????isTable = false;
????????????????// 一个table标签解析完的时候,element中加入一个;
?????????????????//元素用来分隔每个table中的文本,方便输出
????????????}
????????????element.addElement(new String(";"));
????????}// 如果是<div/>标签
????????else if (t == HTML.Tag.DIV && isTable == false)
????????{
????????????if (isDiv == true && isTable == false)
????????????{
????????????????isDiv = false;
????????????}
????????}
????????else if (t == HTML.Tag.TD)
????????{
????????????isTd = false;
????????}
????}
????
????/**
?????* 程序的入口
?????*
?????* @param args
?????*/
????public static void main(String args[])
????{
????????InputStream input = null;
????????FileOutputStream fos = null;
????????BufferedReader brd = null;
????????try
????????{
????????????// 设置要提取的文件的URL
????????????URL url = new URL(FILE_URL);
????????????// 建立连接
????????????URLConnection conn = url.openConnection();
????????????conn.connect();
????????????// 获取输入流
????????????input = conn.getInputStream();
????????????// new 一个具体的文件输出流
????????????fos = new FileOutputStream(FILE_LOCATION);
????????????byte[] b = new byte[1024];
????????????int read = 0;
????????????// 输出
????????????while ((read = input.read(b)) != -1)
????????????{
????????????????fos.write(b, 0, read);
????????????}
????????????// 获取HTML文件流,以UTF-8编码
????????????brd = new BufferedReader(
?????????????????????new InputStreamReader(
?????????????????????new FileInputStream(
??????????????????????????FILE_LOCATION), "UTF-8"));
????????????// 开始解析HTML
????????????startParse(brd);
????????}
????????catch (Exception e)
????????{
????????????e.printStackTrace();
????????}
????????finally
????????{
????????????// 关闭资源
????????????if (input != null)
????????????{
????????????????try
????????????????{
????????????????????input.close();
????????????????}
????????????????catch (IOException e)
????????????????{
????????????????????input = null;
????????????????}
????????????}
????
????????????if (fos != null)
????????????{
????????????????try
????????????????{
????????????????????fos.close();
????????????????}
????????????????catch (IOException e)
????????????????{
????????????????????fos = null;
????????????????}
????????????}
????
????????????if (brd != null)
????????????{
????????????????try
????????????????{
????????????????????brd.close();
????????????????}
????????????????catch (IOException e)
????????????????{
????????????????????brd = null;
????????????????}
????????????}
????????}
????}
}

 

正确配置p6spy后没有日志输出的一个可能的原因

这几天一直想用p6spy来监控hibernate对数据库的操作,主要是要获取hibernate到底执行了那些sql语句,并且获得预编译里面set的值,但是按照网上的教程一步一步配置好后,却发现没有spy.log日志文件生成,而且控制台也没有任何输出。更郁闷的是还报了一个java.sql.SQLException: No suitable driver异常,要知道在没有添加p6spy的时候系统是好用的,driver也是能正常找到。后来在网上找了许久解决方法,最后终于找到了。原来是自己的tomcat安装目录中含有空格,而正好p6spy不允许这样(至于问什么这样自己可以去看源码去,本人没研究),然后我更改了tomcat的目录后p6spy就好使了。

具体的配置p6spy,可以自己去看看。

附:

p6spy教程:http://blog.csdn.net/ddwcyl/article/details/1762771

http://lxy19791111.iteye.com/blog/131293

p6spy配置文件spy.properties的详细解释:http://wenku.baidu.com/view/0897a93067ec102de2bd89d4.html