随笔:93 文章:11 评论:22 引用:0
首页 发新随笔
发新文章 联系 聚合管理

2010年3月7日

--查询表空间、表空间大小及表空间对应物理路径

select a.tablespace_name,b.file_name,a.block_size,a.block_size,b.bytes/1024

/1024 "Sum MB" from dba_tablespaces a,dba_data_files b where a.tablespace_name=b.tablespace_name;

--查询表空间使用情况

  SELECT UPPER(F.TABLESPACE_NAME) "表空间名",

  D.TOT_GROOTTE_MB "表空间大小(M)",

  D.TOT_GROOTTE_MB - F.TOTAL_BYTES "已使用空间(M)",

  TO_CHAR(ROUND((D.TOT_GROOTTE_MB - F.TOTAL_BYTES) / D.TOT_GROOTTE_MB * 100,2),'990.99') || '%' "使用比",

  F.TOTAL_BYTES "空闲空间(M)",

  F.MAX_BYTES "最大块(M)"

  FROM (SELECT TABLESPACE_NAME,

  ROUND(SUM(BYTES) / (1024 * 1024), 2) TOTAL_BYTES,

  ROUND(MAX(BYTES) / (1024 * 1024), 2) MAX_BYTES

  FROM SYS.DBA_FREE_SPACE

  GROUP BY TABLESPACE_NAME) F,

  (SELECT DD.TABLESPACE_NAME,

   ROUND(SUM(DD.BYTES) / (1024 * 1024), 2) TOT_GROOTTE_MB

  FROM SYS.DBA_DATA_FILES DD

  GROUP BY DD.TABLESPACE_NAME) D

  WHERE D.TABLESPACE_NAME = F.TABLESPACE_NAME

  ORDER BY 1;

--查询表空间的free space

  select tablespace_name,

  count(*) as extends,

  round(sum(bytes) / 1024 / 1024, 2) as MB,

  sum(blocks) as blocks

  from dba_free_space

group by tablespace_name;

 

--查询表空间的总容量

  select tablespace_name, sum(bytes) / 1024 / 1024 as MB

  from dba_data_files

  group by tablespace_name;

 

 

--查询表空间使用率

  select total.tablespace_name,

  round(total.MB, 2) as Total_MB,考试大论坛

  round(total.MB - free.MB, 2) as Used_MB,

  round((1 - free.MB / total.MB) * 100, 2) || '%' as Used_Pct

  from (select tablespace_name, sum(bytes) / 1024 / 1024 as MB

  from dba_free_space

  group by tablespace_name) free,

  (select tablespace_name, sum(bytes) / 1024 / 1024 as MB

  from dba_data_files

  group by tablespace_name) total

  where free.tablespace_name = total.tablespace_name;

 

 

1.查找当前表级锁的SQL如下:

select sess.sid,

    sess.serial#,

    lo.oracle_username,

    lo.os_user_name,

    ao.object_name,

    lo.locked_mode

    from v$locked_object lo,

    dba_objects ao,

    v$session sess

where ao.object_id = lo.object_id and lo.session_id = sess.sid;

 

2.杀掉锁表进程:

alter system kill session '436,35123';

 

3.RAC环境中锁查找:

SELECT inst_id,DECODE(request,0,'Holder: ','Waiter: ')||sid sess,

        id1, id2, lmode, request, type,block,ctime

FROM GV$LOCK

WHERE (id1, id2, type) IN

       (SELECT id1, id2, type FROM GV$LOCK WHERE request>0)

ORDER BY id1, request;

  

 

 

4.监控当前数据库谁在运行什么SQL语句

select osuser, username, sql_text 

from  v$session a, v$sqltext b

where  a.sql_address =b.address order by address, piece;

 

 

 

5.找使用CPU多的用户session

select a.sid,spid,status,substr(a.program,1,40) prog, a.terminal,osuser,value/60/100 value

from  v$session a,v$process b,v$sesstat c

where  c.statistic#=12 and 

       c.sid=a.sid and 

       a.paddr=b.addr 

       order by value desc;

 

 

6.查看死锁信息

SELECT (SELECT username

          FROM v$session

         WHERE SID = a.SID) blocker, a.SID, 'is blocking',

       (SELECT username

          FROM v$session

         WHERE SID = b.SID) blockee, b.SID

  FROM v$lock a, v$lock b

 WHERE a.BLOCK = 1 AND b.request > 0 AND a.id1 = b.id1 AND a.id2 = b.id2;

 

 

7.具有最高等待的对象

SELECT   o.OWNER,o.object_name, o.object_type, a.event,

         SUM (a.wait_time + a.time_waited) total_wait_time

    FROM v$active_session_history a, dba_objects o

   WHERE a.sample_time BETWEEN SYSDATE - 30 / 2880 AND SYSDATE

     AND a.current_obj# = o.object_id

GROUP BY o.OWNER,o.object_name, o.object_type, a.event

ORDER BY total_wait_time DESC;

 

 

SELECT   a.session_id, s.osuser, s.machine, s.program, o.owner, o.object_name,

         o.object_type, a.event,

         SUM (a.wait_time + a.time_waited) total_wait_time

    FROM v$active_session_history a, dba_objects o, v$session s

   WHERE a.sample_time BETWEEN SYSDATE - 30 / 2880 AND SYSDATE

     AND a.current_obj# = o.object_id

     AND a.session_id = s.SID

GROUP BY o.owner,

         o.object_name,

         o.object_type,

         a.event,

         a.session_id,

         s.program,

         s.machine,

         s.osuser

ORDER BY total_wait_time DESC;

 

 

 

8.查询当前连接会话数

select s.value,s.sid,a.username

from

v$sesstat S,v$statname N,v$session A

where

n.statistic#=s.statistic# and

name='session pga memory'

and s.sid=a.sid

order by s.value;

 

 

 

9.等待最多的用户

SELECT   s.SID, s.username, SUM (a.wait_time + a.time_waited) total_wait_time

    FROM v$active_session_history a, v$session s

   WHERE a.sample_time BETWEEN SYSDATE - 30 / 2880 AND SYSDATE

GROUP BY s.SID, s.username

ORDER BY total_wait_time DESC;

 

 

 

10.等待最多的SQL

SELECT   a.program, a.session_id, a.user_id, d.username, s.sql_text,

         SUM (a.wait_time + a.time_waited) total_wait_time

    FROM v$active_session_history a, v$sqlarea s, dba_users d

   WHERE a.sample_time BETWEEN SYSDATE - 30 / 2880 AND SYSDATE

     AND a.sql_id = s.sql_id

     AND a.user_id = d.user_id

GROUP BY a.program, a.session_id, a.user_id, s.sql_text, d.username;

 

 

 

11.查看消耗资源最多的SQL

SELECT hash_value, executions, buffer_gets, disk_reads, parse_calls

FROM V$SQLAREA

WHERE buffer_gets > 10000000 OR disk_reads > 1000000

ORDER BY buffer_gets + 100 * disk_reads DESC;

 

 

 

12.查看某条SQL语句的资源消耗

SELECT hash_value, buffer_gets, disk_reads, executions, parse_calls

FROM V$SQLAREA

WHERE hash_Value = 228801498 AND address = hextoraw('CBD8E4B0');

 

 

13.查询会话执行的实际SQL

SELECT   a.SID, a.username, s.sql_text

    FROM v$session a, v$sqltext s

   WHERE a.sql_address = s.address

     AND a.sql_hash_value = s.hash_value

     AND a.status = 'ACTIVE'

ORDER BY a.username, a.SID, s.piece;

 

 

14.显示正在等待锁的所有会话

SELECT * FROM DBA_WAITERS;

 

DDL

--------------------------------------------------------------

/*注意点:

 

1.如果在PL/SQL 等工具里打开的话,直接修改下面的代码中[斜体加粗部分]执行

 

2.确保路径存在,比如【D:\oracle\oradata\Oracle9i\】也就是你要保存文件的路径存在

 

/*分为四步 */

 

/*第1步:创建临时表空间  */

 

create temporary tablespace user_temp 

 

tempfile 'D:\oracle\oradata\Oracle9i\user_temp.dbf'

 

size 50m 

 

autoextend on 

 

next 50m maxsize 20480m 

 

extent management local; 

 

 

 

/*第2步:创建数据表空间  */

 

create tablespace user_data 

 

logging 

 

datafile 'D:\oracle\oradata\Oracle9i\user_data.dbf'

 

size 50m 

 

autoextend on 

 

next 50m maxsize 20480m 

 

extent management local; 

 

 

 

/*第3步:创建用户并指定表空间  */

 

create user username identified by password 

 

default tablespace user_data 

 

temporary tablespace user_temp; 

 

 

 

/*第4步:给用户授予权限  */

 

grant connect,resource,dba to username; 

posted @ 2014-03-03 15:48 redcoatjk 阅读(322) | 评论 (0)编辑 收藏
 
摘自: http://www.douban.com/note/235086917/
http://jackleechina.iteye.com/blog/1595397

为什么一般要采用事件监听而不是直接对元素的事件属性(如:onclick、onmouseover)赋值?

原来用事件属性只能赋值一种方法,即:

button1.onclick = function() { alert(1); };
button1.onclick = function() { alert(2); };

这样后面的赋值语句就将前面的onclick属性覆盖了

而使用添加事件监听的方式就可以实现并行。特别是当团队合作时,事件并行的需求增多,比如:监听document对象的鼠标事件或者window对象的载入事件等。使用事件属性则很容易造成事件覆盖掉

使用事件监听有两种方式:attachEvent和addEventListener

attachEvent与addEventListener区别
适应的浏览器版本不同,同时在使用的过程中要注意
attachEvent方法 按钮onclick
addEventListener方法 按钮click
attachEvent方法, (ie系列)
addEventListener方法 Mozilla系列

例子如下:
 1<!DOCTYPE html>
 2<html>
 3
 4<SCRIPT LANGUAGE="JavaScript">
 5function method1(){
 6    alert("method1");
 7}

 8function method2(){
 9    alert("method2");
10}

11function method3(){
12    alert("method3");
13}

14
</SCRIPT>
15<body>
16<input type="button"  value="dom 元素事件属性绑定的按钮" id="button1"/>
17<input type="button"  value="IE浏览器: attachEvent进行事件绑定的按钮" id="btn1"/>
18<input type="button"  value="火狐浏览器: addEventListener进行事件绑定的按钮" id="btn2"/>
19
20<SCRIPT LANGUAGE="JavaScript">
21     /**方法一: 使用元素的事件属性. [这种方式事件只可绑定一次,最后绑定的执行]**/
22     button1.onclick = function() { alert("1-1"); };
23     button1.onclick = function() { alert("1-2"); };
24     /**方法二: 使用attachEvent注册事件. 格式如下object.attachEvent(event,function);**/
25   var btn1Obj = document.getElementById("btn1");
26    btn1Obj.attachEvent("onclick", method1);
27   btn1Obj.attachEvent("onclick", method2);
28    btn1Obj.attachEvent("onclick", method3);
29     /**方法三: addEventListener. 格式如下element.addEventListener(type,listener,useCapture);**/
30    var btn2Obj = document.getElementById("btn2");
31    btn2Obj.addEventListener("click",method1,false);
32    btn2Obj.addEventListener("click",method2,false);
33    btn2Obj.addEventListener("click",method3,false);
34    //执行顺序为method1->method2->method3
35
</SCRIPT>
36<br/>attachEvent按照注册倒叙执行:    执行顺序为method3->method2->method1 
37<br/>addEventListener按照注册顺序执行:    执行顺序为method1->method2->method3 
38</body>
39</html>

相关衍生阅读:

JavaScript欲速则不达——基本处理事件详解和阻止事件传播

posted @ 2013-11-04 14:22 redcoatjk 阅读(415) | 评论 (0)编辑 收藏
 
     摘要: 摘自http://zhangjunhd.blog.51cto.com/113473/20629/ 1.Servlet过滤器 1.1 什么是过滤器 过滤器是一个程序,它先于与之相关的servlet或JSP页面运行在服务器上。过滤器可附加到一个或多个servlet或JSP页面上,并且可以检查进入这些资源的请求信息。在这之后,过滤器可以作如下的选择: ①以常规的方式调用资源(即,调...  阅读全文
posted @ 2013-04-16 17:28 redcoatjk 阅读(243) | 评论 (0)编辑 收藏
 
     摘要: 摘自 http://www.sandzhang.com/blog/2010/04/07/mysql-show-status-explained-detail/ 要查看MySQL运行状态,要优化MySQL运行效率都少不了要运行show status查看各种状态,下面是参考官方文档及网上资料整理出来的中文详细解释:  如有问题,欢迎指正 状态名 作用域 ...  阅读全文
posted @ 2012-09-05 15:33 redcoatjk 阅读(2086) | 评论 (1)编辑 收藏
 
     摘要: 代码中反复开关自动提交没有必要. 其他方面写得还是很不错的.清晰.摘自 http://wangqinqin.iteye.com/blog/547277  PreparedStatement: 1) addBatch()将一组参数添加到PreparedStatement对象内部。 2) executeBatch()将一批参数提交给数据库来执行,如果全部命令执行成功...  阅读全文
posted @ 2012-07-20 15:04 redcoatjk 阅读(18598) | 评论 (1)编辑 收藏
 
摘自 http://neoremind.net/2010/12/preparedstatement_diff/

JDBC中Statement与PreparedStatement的区别

1. statement每次执行sql语句,相关数据库都要执行sql语句的编译;preparedstatement是预编译的, 采用Cache机制(预编译语句,放在Cache中,下次执行相同SQL语句时,则可以直接从Cache中取出来,有利于sql生成查询计划。),对于批量处理可以大大提高效率. 也叫JDBC存储过程。

例如,如果要执行两条sql语句

SELECT colume FROM TABLE WHERE colume=1;
SELECT colume FROM TABLE WHERE colume=2;

会生成两个执行计划

一千个查询就生成一千个执行计划!

PreparedStatement用于使用绑定变量重用执行计划

SELECT colume FROM TABLE WHERE colume=:x;

通过set不同数据只需要生成一次执行计划,可以重用

是否使用绑定变量对系统影响非常大,生成执行计划极为消耗资源

两种实现 速度差距可能成百上千倍

后者使用了PreparedStatement对象,而前者是普通的Statement对象。PreparedStatement对象不仅包含了SQL语句,而且大多数情况下这个语句已经被预编译过,因而当其执行时,只需DBMS运行SQL语句,而不必先编译。当你需要执行Statement对象多次的时候,PreparedStatement对象将会大大降低运行时间,当然也加快了访问数据库的速度

这种转换也给你带来很大的便利,不必重复SQL语句的句法,而只需更改其中变量的值,便可重新执行SQL语句。选择PreparedStatement对象与否,在于相同句法的SQL语句是否执行了多次,而且两次之间的差别仅仅是变量的不同。如果仅仅执行了一次的话,在对数据库只执行一次性存取的时侯,用 Statement 对象进行处理,PreparedStatement 对象的开销比Statement大,对于一次性操作并不会带来额外的好处。

2. PrepareStatement中执行的SQL语句中是可以带参数的,也就是说可以替换变量,尽量采用使用?号的方式传递参数,增加代码的可读性又可以预编译加速;而Statement则不可以。

3. 防止SQL注入。在SQL中包含特殊字符或SQL的关键字(如:’ or 1 or ‘)时,Statement将出现不可预料的结果(出现异常或查询的结果不正确),可用PreparedStatement来解决。

SQL注入或者说SQL注入攻击就是利用Statement的漏洞完成的,例如用个用户登录,那么form表单有用户名和密码
那么我提交时,在用户名输入框内 输入 “aaa’ or ’a’=’a” 密码框随便输入,那么这样意味着 sql的
查询语言就是 “select * from 表 where 用户名=’aaa’ or ’a’=’a’ and 密码=’123’  ”,这样查询出来所有的数据或者是混乱。那么不被授权的用户照样可以登录,岂不是被黑了?!实际中现在java程序员早都不用这种方式写查询了,一般都用PreparedStatement来查询,或干脆就用hibernate之类的持久层框架,这样通过sql注入就无从谈起了。
posted @ 2012-07-20 11:14 redcoatjk 阅读(4394) | 评论 (2)编辑 收藏
 
摘自:http://ryxxlong.iteye.com/blog/552884

如何修改mysql root密码
  忘记MySQL ROOT密码是在MySQ使用中很常见的问题,可是有很多朋友并不会重置ROOT密码,那叫苦啊,特写此文章与大家交流:

1、编辑MySQL的配置文件:my.ini
一般在MySQL安装目录下有my.ini即MySQL的配置文件。
在此配置文件的最后添加如下一行:
skip-grant-tables
保存退出编辑。

2、然后重启MySQL服务
在命令行下执行:
net stop MySQL
net start MySQL

3、设置新的ROOT密码
然后用命令提示符cd到对应安装目录的bin下执行:
MySQL -u root -p MySQL或mysql -u root -p
直接回车无需密码即可进入数据库了。
此时,在命令行下执行 use mysql;
现在我们执行如下语句把root密码更新为:
update user set password=PASSWORD("root") where user='root';
(注意:此时不用使用mysqladmin -u root -p password '你的新密码'这条命令修改密码,因为'skip-grant-tables'配置,
不信的话,你可以试用一下,它肯定会报如下所示的错误:
F:\Documents and Settings\long>mysqladmin -u root -p password 'root'
Enter password:
Warning: single quotes were not trimmed from the password by your command
line client, as you might have expected.
mysqladmin:
You cannot use 'password' command as mysqld runs
 with grant tables disabled (was started with --skip-grant-tables).
Use: "mysqladmin flush-privileges password '*'" instead)
exit 退出MySQL。

4、还原配置文件并重启服务

然后修改MySQL配置文件把刚才添加的那一行'skip-grant-tables'删除。
再次重起MySQL服务,密码修改完毕。
用新密码root试一下吧,又能登入重新进入mysql了?



附mysql修改密码的一些方法:
1. 用MYSQL的grant语句,例如
mysql -h hostname –u root 命令登录到mysqld server 用grant 命令改变口令:
mysql -h 192.168.1.101 -u root
上边的192.168.1.101 是偶的mysqld 运行机器,你换成自己的,这样登录上去,就可以修改密码了,
其实没必要这么麻烦,直接mysql -u root就可以了。
GRANT ALL ON *.* TO 'root'@'localhost' IDENTIFIED BY 'root' WITH GRANT OPTION

2. mysqladmin -u 用户名 -p 旧密码 password 新密码
例1:给root 加个密码root。首先进入cmd中,然后键入
以下命令,至于在CMD下能否使用mysqladmin,
就要看你的Windows环境变量PATH中是否添加“E:\Program Files\MySQL\MySQL Server 5.1\bin;”(请改为你自己的安装路径)了。)
mysqladmin -u root password root
注:因为开始时root 没有密码,所以-p 旧密码一项就可以省略了。
例2:再将root 的密码改为admin。
mysqladmin –u root -proot password admin(注意-p 不要和后面的密码分
开写,要写在一起,不然会出错,错误如下所示:
F:\Documents and Settings\long>mysqladmin -u root -p root password admin
Enter password: ****
mysqladmin: Unknown command: 'root')
当然你也可以这样写:mysqladmin –u root -p password admin回车,
然后再输入你的旧密码,这样也是完全也可以的,看你的爱好了.
例3:再将root用户的密码去掉.
F:\Documents and Settings\long>mysqladmin -u root -p password  ;
Enter password: root
此时,root用户又没有密码了.可以通过下面的方法设置:
F:\Documents and Settings\long>mysql -u root
mysql>set password for 'root'@'localhost'=password('root');(语法:SET PASSWORD FOR '用户名'@'主机' = PASSWORD('密码'))
mysql>set password for 'root'@'%'=password('root');
//本条可选,这是在配置mysql数据库,如果你选择了允许root通过远程登录进来时,你在mysql数据库下的user表中,
use mysql;
select * from user;可以看到有两条记录,如果你没有配置这一项的话,只会第一条记录!
Host                    User     Password
'localhost', 'root', '*9C9F4927129ECC3209D8550DC8B67156FDBF9418', ...
'%', 'root', '*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B', ...
通过以上设置,root的密码将变为root这样就完成了根用户root密码的设置工作。

3. use mysql;
 update user set password =password('yourpass') where user='root'

(注:下面的这些方法我本人没有试过,不知对不对,我只是转载了一下:)
下面的方法都在mysql提示符下使用,且必须有mysql的root权限:
方法4
mysql> INSERT INTO mysql.user (Host,User,Password)
VALUES('%','jeffrey',PASSWORD('biscuit'));
mysql> FLUSH PRIVILEGES
确切地说这是在增加一个用户,用户名为jeffrey,密码为biscuit。
在《mysql中文参考手册》里有这个例子,所以我也就写出来了。
注意要使用PASSWORD函数,然后还要使用FLUSH PRIVILEGES。

方法5
和方法三一样,只是使用了REPLACE语句
mysql> REPLACE INTO mysql.user (Host,User,Password)
VALUES('%','jeffrey',PASSWORD('biscuit'));
mysql> FLUSH PRIVILEGES

方法6
使用SET PASSWORD语句,
mysql> SET PASSWORD FOR jeffrey@"%" = PASSWORD('biscuit');
你也必须使用PASSWORD()函数,
但是不需要使用FLUSH PRIVILEGES。


方法7
使用GRANT ... IDENTIFIED BY语句
mysql> GRANT USAGE ON *.* TO jeffrey@"%" IDENTIFIED BY 'biscuit';
这里PASSWORD()函数是不必要的,也不需要使用FLUSH PRIVILEGES。

注:mysql 新设置用户或更改密码后需用flush privileges刷新MySQL的系统权限相关表,
否则会出现拒绝访问,还有一种方法,就是重新启动mysql服务器,来使新设置生效。

posted @ 2012-07-19 10:54 redcoatjk 阅读(306) | 评论 (0)编辑 收藏
 

怎么查看端口占用情况?

       开始--运行--cmd 进入命令提示符 输入netstat -ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选"查看"-"选择列"

        经常,我们在启动应用的时候发现系统需要的端口被别的程序占用,如何知道谁占有了我们需要的端口,很多人都比较头疼,下面就介绍一种非常简单的方法,希望对大家有用

假如我们需要确定谁占用了我们的9050端口

1、Windows平台
在windows命令行窗口下执行:

C:\>netstat -aon|findstr "9050"

TCP 127.0.0.1:9050 0.0.0.0:0 LISTENING 2016


看到了吗,端口被进程号为2016的进程占用,继续执行下面命令:

C:\>tasklist|findstr "2016"

tor.exe 2016 Console 0 16,064 K

很清楚吧,tor占用了你的端口。

posted @ 2012-06-13 13:51 redcoatjk 阅读(238) | 评论 (0)编辑 收藏
 
JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与 JavaScript 的交互。本文将快速讲解 JSON 格式,并通过代码示例演示如何分别在客户端和服务器端进行 JSON 格式数据的处理。
管有许多宣传关于 XML 如何拥有跨平台,跨语言的优势,然而,除非应用于 Web Services,否则,在普通的 Web 应用中,开发者经常为 XML 的解析伤透了脑筋,无论是服务器端生成或处理 XML,还是客户端用 JavaScript 解析 XML,都常常导致复杂的代码,极低的开发效率。实际上,对于大多数 Web 应用来说,他们根本不需要复杂的 XML 来传输数据,XML 的扩展性很少具有优势,许多 AJAX 应用甚至直接返回 HTML 片段来构建动态 Web 页面。和返回 XML 并解析它相比,返回 HTML 片段大大降低了系统的复杂性,但同时缺少了一定的灵活性。

现在, JSON 为 Web 应用开发者提供了另一种数据交换格式。让我们来看看 JSON 到底是什么,同 XML 或 HTML 片段相比,JSON 提供了更好的简单性和灵活性。

JSON 数据格式解析

和 XML 一样,JSON 也是基于纯文本的数据格式。由于 JSON 天生是为 JavaScript 准备的,因此,JSON 的数据格式非常简单,您可以用 JSON 传输一个简单的 String,Number,Boolean,也可以传输一个数组,或者一个复杂的 Object 对象。

String,Number 和 Boolean 用 JSON 表示非常简单。例如,用 JSON 表示一个简单的 String “ abc ”,其格式为:

"abc"

这与绝大多数编程语言的表示方法一致,例如:

12345(整数)
-3.9e10(浮点数)

Boolean 类型表示为 truefalse 。此外,JavaScript 中的 null 被表示为 null,注意,truefalsenull 都没有双引号,否则将被视为一个 String 。

JSON 还可以表示一个数组对象,使用 [] 包含所有元素,每个元素用逗号分隔,元素可以是任意的 Value,例如,以下数组包含了一个 String,Number,Boolean 和一个 null:

["abc",12345,false,null]

Object 对象在 JSON 中是用 {} 包含一系列无序的 Key-Value 键值对表示的,实际上此处的 Object 相当于 Java 中的 Map<String, Object>,而不是 Java 的 Class 。注意 Key 只能用 String 表示。

例如,一个 Address 对象包含如下 Key-Value:

city:Beijing 
street:Chaoyang Road
postcode:100025(整数)

用 JSON 表示如下:

{"city":"Beijing","street":" Chaoyang Road ","postcode":100025}

其中 Value 也可以是另一个 Object 或者数组,因此,复杂的 Object 可以嵌套表示,例如,一个 Person 对象包含 name 和 address 对象,可以表示如下:

{"name":"Michael","address":
{"city":"Beijing","street":" Chaoyang Road ","postcode":100025}
}

JavaScript 处理 JSON 数据

上面介绍了如何用 JSON 表示数据,接下来,我们还要解决如何在服务器端生成 JSON 格式的数据以便发送到客户端,以及客户端如何使用 JavaScript 处理 JSON 格式的数据。

我们先讨论如何在 Web 页面中用 JavaScript 处理 JSON 数据。我们通过一个简单的 JavaScript 方法就能看到客户端如何将 JSON 数据表示给用户:

function handleJson() { 
var j={"name":"Michael","address":
{"city":"Beijing","street":" Chaoyang Road ","postcode":100025}
};
document.write(j.name);
document.write(j.address.city);
}

假定服务器返回的 JSON 数据是上文的:

{"name":"Michael","address":
{"city":"Beijing","street":" Chaoyang Road ","postcode":100025}
}

只需将其赋值给一个 JavaScript 变量,就可以立刻使用该变量并更新页面中的信息了,相比 XML 需要从 DOM 中读取各种节点而言,JSON 的使用非常容易。我们需要做的仅仅是发送一个 Ajax 请求,然后将服务器返回的 JSON 数据赋值给一个变量即可。有许多 Ajax 框架早已包含了处理 JSON 数据的能力,例如 Prototype(一个流行的 JavaScript 库:http://prototypejs.org)提供了 evalJSON() 方法,能直接将服务器返回的 JSON 文本变成一个 JavaScript 变量:

new Ajax.Request("http://url", { 
method: "get",
onSuccess: function(transport) {
var json = transport.responseText.evalJSON();
// TODO: document.write(json.xxx);
}
});

服务器端输出 JSON 格式数据

下面我们讨论如何在服务器端输出 JSON 格式的数据。以 Java 为例,我们将演示将一个 Java 对象编码为 JSON 格式的文本。

将 String 对象编码为 JSON 格式时,只需处理好特殊字符即可。另外,必须用 (") 而非 (') 表示字符串:


static String string2Json(String s) {
StringBuilder sb = new StringBuilder(s.length()+20);
sb.append('\"');
for (int i=0; i<s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '\"':
sb.append("\\\"");
break;
case '\\':
sb.append("\\\\");
break;
case '/':
sb.append("\\/");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
default:
sb.append(c);
}
}
sb.append('\"');
return sb.toString();
}

将 Number 表示为 JSON 就容易得多,利用 Java 的多态,我们可以处理 Integer,Long,Float 等多种 Number 格式:


static String number2Json(Number number) {
return number.toString();
}

Boolean 类型也可以直接通过 toString() 方法得到 JSON 的表示:


static String boolean2Json(Boolean bool) {
return bool.toString();
}

要将数组编码为 JSON 格式,可以通过循环将每一个元素编码出来:


static String array2Json(Object[] array) {
if (array.length==0)
return "[]";
StringBuilder sb = new StringBuilder(array.length << 4);
sb.append('[');
for (Object o : array) {
sb.append(toJson(o));
sb.append(',');
}
// 将最后添加的 ',' 变为 ']':
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}

最后,我们需要将 Map<String, Object> 编码为 JSON 格式,因为 JavaScript 的 Object 实际上对应的是 Java 的 Map<String, Object> 。该方法如下:


static String map2Json(Map<String, Object> map) {
if (map.isEmpty())
return "{}";
StringBuilder sb = new StringBuilder(map.size() << 4);
sb.append('{');
Set<String> keys = map.keySet();
for (String key : keys) {
Object value = map.get(key);
sb.append('\"');
sb.append(key);
sb.append('\"');
sb.append(':');
sb.append(toJson(value));
sb.append(',');
}
// 将最后的 ',' 变为 '}':
sb.setCharAt(sb.length()-1, '}');
return sb.toString();
}

为了统一处理任意的 Java 对象,我们编写一个入口方法 toJson(Object),能够将任意的 Java 对象编码为 JSON 格式:


public static String toJson(Object o) {
if (o==null)
return "null";
if (o instanceof String)
return string2Json((String)o);
if (o instanceof Boolean)
return boolean2Json((Boolean)o);
if (o instanceof Number)
return number2Json((Number)o);
if (o instanceof Map)
return map2Json((Map<String, Object>)o);
if (o instanceof Object[])
return array2Json((Object[])o);
throw new RuntimeException("Unsupported type: " + o.getClass().getName());
}

我们并未对 Java 对象作严格的检查。不被支持的对象(例如 List)将直接抛出 RuntimeException 。此外,为了保证输出的 JSON 是有效的,Map<String, Object> 对象的 Key 也不能包含特殊字符。细心的读者可能还会发现循环引用的对象会引发无限递归,例如,精心构造一个循环引用的 Map,就可以检测到 StackOverflowException


@Test(expected=StackOverflowError.class)
public void testRecurrsiveMap2Json() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("key", map);
JsonUtil.map2Json(map);
}

好在服务器处理的 JSON 数据最终都应该转化为简单的 JavaScript 对象,因此,递归引用的可能性很小。

最后,通过 Servlet 或 MVC 框架输出 JSON 时,需要设置正确的 MIME 类型(application/json)和字符编码。假定服务器使用 UTF-8 编码,则可以使用以下代码输出编码后的 JSON 文本:


response.setContentType("application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter pw = response.getWriter();
pw.write(JsonUtil.toJson(obj));
pw.flush();

小结

JSON 已经是 JavaScript 标准的一部分。目前,主流的浏览器对 JSON 支持都非常完善。应用 JSON,我们可以从 XML 的解析中摆脱出来,对那些应用 Ajax 的 Web 2.0 网站来说,JSON 确实是目前最灵活的轻量级方案。

posted @ 2012-02-27 11:00 redcoatjk 阅读(315) | 评论 (0)编辑 收藏
 
JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识。
那么,JNDI到底起什么作用?

要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。

没有JNDI的做法:
程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。
就像以下代码这样:

Connection conn=null;
try {Class.forName("com.mysql.jdbc.Driver",true, Thread.currentThread().getContextClassLoader());
conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");
/* 使用conn并进行SQL操作 */......
conn.close();
}
catch(Exception e)
{e.printStackTrace();}
finally {if(conn!=null)
{try {conn.close();}
catch(SQLException e) {
}}}

这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。

没有JNDI的做法存在的问题:
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整;
4、......

解决办法:
程序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。

由此,就有了JNDI。

用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。
具体操作如下(以JBoss为例):
1、配置数据源
在JBoss的 D:\jboss420GA\docs\examples\jca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:\jboss420GA\server\default\deploy。
修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
    <jndi-name>MySqlDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
</local-tx-datasource>
</datasources>

这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。

2、在程序中引用数据源:

Connection conn=null;
try
{
Context ctx=new InitialContext();
Object datasourceRef=ctx.lookup("java:MySqlDS");
//引用数据源
DataSource ds=(Datasource)datasourceRef;
conn=ds.getConnection();
/* 使用conn进行数据库SQL操作 */......
c.close();
}
catch(Exception e)
{e.printStackTrace();}
finally {if(conn!=null)
{
try
{
conn.close();
}
catch(SQLException e)
{
}}}
直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。

由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。

JNDI的扩展:
JNDI在满足了数据源配置的要求的基础上,还进一步扩充了作用:所有与系统外部的资源的引用,都可以通过JNDI定义和引用。

所以,在J2EE规范中,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。

EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。 外部资源”。


总结:
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。

在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。
posted @ 2012-02-14 10:24 redcoatjk 阅读(324) | 评论 (0)编辑 收藏
 

在Oracle中,要按特定条件查询前N条记录,用个rownum就搞定了。
select * from emp where rownum <= 5
结果只查询5条记录, oracle会自动展现一个rownum的属性表示记录排序号

而select * from emp where rownum > 5 ;则是失败的。
因为:rownum是oracle预处理字段,默认标序是1,只有记录集已经满足条件后才会进行后续编号。由于第一条记录rownum默认是1,而你的条件是rownum>=6 对第一条记录比较它的rownum肯定不大于6 所以不满足条件 oracle舍弃第一条记录将数据库第二条记录标序为1再进行比较  肯定都不满足rownum>=6  这样循环也就是说由于没有记录满足rownum>=6所以记录一直被舍弃,rownum一直是1 。

解决方案: 利用查询时,自动生成的rownum属性.
排序方法:
  select * from (
       select a1.*, rownum rwn  from emp a1   where rownum <=10
    ) where rwn >= 6;
  或者
  select * from (
    select qx.*,row_number() over(order by qx.empno) rwn from emp qx
  ) where rwn between 6 and 10
-------------------------------------------------------------------------
使用注意:

1排序导致数据重复:


table1中表有字段(其余字段省)
ID  主键
DATA_UPDATE_TIME 数据更新时间(只存储了年月日)

分页查询的语句如下
select *
   from (
        select row_.*, rownum rownum_
           from ( select  p.id from table1 p
             order by  p.DATA_UPDATE_TIME desc )
   row_ where rownum <= )
where rownum_ >

以每页显示10条为例
第一次  rownum <= 10)   where rownum_ > 0
第二次  rownum <= 20)   where rownum_ > 10

发现有一条记录在两次查询结果中重复出现,不知道问题出在哪里,请忙帮看看。

另:
DATA_UPDATE_TIME 的值有重复,不知道跟它有没有关系。
如果按ID排的话就不会出现这个问题


解答:
如果order by 不能唯一确定记录的顺序就会出现这个问题。
解决的方法是把分页部分全部拿到最外层进行。

Java代码 复制代码 收藏代码
  1. select * from (    
  2.   select row_.*, rownum rownum_    
  3.   from (   
  4.     select p.id from table1 p    
  5.     order by p.DATA_UPDATE_TIME desc    
  6.   ) row_   
  7. )    
  8. where rownum_ > ? and rownum_ <= ?   

2 排序的id顺序:
Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。
SQL> select rownum ,id,name from student order by name;
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
         3 200003 李三
         2 200002 王二
         1 200001 张一
         4 200004 赵四
可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询
SQL> select rownum ,id,name from (select * from student order by name);
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
         1 200003 李三
         2 200002 王二
         3 200001 张一
         4 200004 赵四
这样就成了按name排序,并且用rownum标出正确序号(有小到大)


------------
参考Oracle的rownum原理和使用  http://tenn.iteye.com/blog/99339

在Oracle中,要按特定条件查询前N条记录,用个rownum就搞定了。
select * from emp where rownum <= 5
而且书上也告诫,不能对rownum用">",这也就意味着,如果你想用
select * from emp where rownum > 5
则是失败的。要知道为什么会失败,则需要了解rownum背后的机制:
1 Oracle executes your query.

2 Oracle fetches the first row and calls it row number 1.

3 Have we gotten past row number meets the criteria? If no, then Oracle discards the row, If yes, then Oracle return the row.

4 Oracle fetches the next row and advances the row number (to 2, and then to 3, and then to 4, and so forth).

5 Go to step 3.

了解了原理,就知道rownum>不会成功,因为在第三步的时候查询出的行已经被丢弃,第四步查出来的rownum仍然是1,这样永远也不会成功。

同样道理,rownum如果单独用=,也只有在rownum=1时才有用。

 

对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数,而且rownum不能以任何表的名称作为前缀。
 举例说明:
例如表:student(学生)表,表结构为:
ID       char(6)      --学号
name    VARCHAR2(10)   --姓名
create table student (ID char(6), name VARCHAR2(100));
insert into sale values('200001',‘张一’);
insert into sale values('200002',‘王二’);
insert into sale values('200003',‘李三’);
insert into sale values('200004',‘赵四’);
commit;
(1) rownum 对于等于某值的查询条件
如果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
SQL> select rownum,id,name from student where rownum=1;(可以用在限制返回记录条数的地方,保证不出错,如:隐式游标)
SQL> select rownum,id,name from student where rownum=1;
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
         1 200001 张一
SQL> select rownum,id,name from student where rownum =2;
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
(2)rownum对于大于某值的查询条件
   如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录
SQL> select rownum,id,name from student where rownum >2;
ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
那如何才能找到第二行以后的记录呀。可以使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。
SQL>select * from(select rownum no ,id,name from student) where no>2;
        NO ID     NAME
---------- ------ ---------------------------------------------------
         3 200003 李三
         4 200004 赵四
SQL> select * from(select rownum,id,name from student)where rownum>2;
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
(3)rownum对于小于某值的查询条件
如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。显然rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。
SQL> select rownum,id,name from student where rownum <3;
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
1 200001 张一
        2 200002 王二
综上几种情况,可能有时候需要查询rownum在某区间的数据,那怎么办呀从上可以看出rownum对小于某值的查询条件是人为true的,rownum对于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。例如要查询rownum在第二行到第三行之间的数据,包括第二行和第三行数据,那么我们只能写以下语句,先让它返回小于等于三的记录行,然后在主查询中判断新的rownum的别名列大于等于二的记录行。但是这样的操作会在大数据集中影响速度。
SQL> select * from (select rownum no,id,name from student where rownum<=3 ) where no >=2;
        NO ID     NAME
---------- ------ ---------------------------------------------------
         2 200002 王二
         3 200003 李三
(4)rownum和排序
Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。
SQL> select rownum ,id,name from student order by name;
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
         3 200003 李三
         2 200002 王二
         1 200001 张一
         4 200004 赵四
可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询
SQL> select rownum ,id,name from (select * from student order by name);
    ROWNUM ID     NAME
---------- ------ ---------------------------------------------------
         1 200003 李三
         2 200002 王二
         3 200001 张一
         4 200004 赵四
这样就成了按name排序,并且用rownum标出正确序号(有小到大)

posted @ 2011-11-17 16:46 redcoatjk 阅读(301) | 评论 (0)编辑 收藏
 
仅为个人理解.请指正
Hibernate Session, 其作用无需多言.
运用中为避免资源消耗,一般都会手动封装一个HibernateUtil类(未使用Spring管理的前提下).
该类的作用使Hibernate加载配置文件config, 创建sessionFactory等只运行一次.
际运用中,经常需要将当前线程和session绑定.一般的用法为使用ThreadLocal: 在HibernateUtil类中封装hibernate的管理.通过openSession取得
session,并将其放入ThreadLocal变量中. 这样业务逻辑中仅需通过工具类取得当前线程对应的session.使用完毕后,调用工具类closeSession方法将
session关闭,当前线程的ThreadLocal变量置为NULL. 保证线程归还线程池复用后,ThreadLocal为空,以免出现导致其他线程访问到本线程变量.
后,Hibernate的SessionFactory提供获取session的新方法getCurrentSession (获得与当前线程绑定的session). 内部通过代理封装,此方式得到的session
不仅和当前线程绑定,也无需手动开关. 默认在事务提交之后,session自动关闭. 需注意的是,必须在事务开启的前提之下才可使用此种方式获得的session.
此外hibernate.cfg.xml配置文件中也许配置
<property name="current_session_context_class">thread</property> 基于线程
了,引入Spring之后.sessionfactory的创建等都交给spring管理.Spring也提供了HibernateTemplate,HibernateDaoSupport这样的封装方法.
用户可以不再考虑session的管理,事务的开启关闭.只需配置事务即可.
而所谓session关闭后,因延迟加载导致前台无法显示的问题以往解决方式为强制全部加载,现在也可通过在web.xml中配置
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter来解决.



------------------------------以下内容为工地资料-------------------------------------------------------------------------------
OpenSession : 手动打开,需手动关闭.[所以代码中充斥着try catch --sf.openSession --打开事务,提交-回滚 finall关闭session的代码]
threadlocal : hibernate给出的提示. 在HibernateUtil工具类中,new出threadlocal ,放入opensession.这样可以使当前线程绑定session.
使用后需关闭session,将threadlocal中session变量置为null .
3  getCurrentSession: hibernate3的新特性. 无需手动关闭session,自动获取当前线程的session,若无则新建之. 需在配置文件中配置thread属性.表明和当前线程绑定.
    参考网友资料,getCurrentSession模式,内部开启了session自动提交的功能且使用getCurrentSession的session,及时做load操作,也需要打开事务.
Title

1 getCurrentSession创建的session会和绑定到当前线程,而openSession不会。

2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭

这里getCurrentSession本地事务(本地事务:jdbc)时 要在配置文件里进行如下设置

 * 如果使用的是本地事务(jdbc事务)
 <property name="hibernate.current_session_context_class">thread</property>
 * 如果使用的是全局事务(jta事务)
 <property name="hibernate.current_session_context_class">jta</property>

getCurrentSession () 使用当前的session
openSession()         重新建立一个新的session

在一个应用程序中,如果DAO 层使用Spring 的hibernate 模板,通过Spring 来控制session 的生命周期,则首选getCurrentSession ()。

使用Hibernate的大多数应用程序需要某种形式的“上下文相关的” session,特定的session在整个特定的上下文范围内始终有效。然而,对不同类型的应用程序而言,要为什么是组成这种“上下文”下一个定义通常 是困难的;不同的上下文对“当前”这个概念定义了不同的范围。在3.0版本 之前,使用Hibernate的程序要么采用自行编写的基于 ThreadLocal的上下文session,要么采用HibernateUtil这样的辅助类,要么采用第三方框架(比如Spring或Pico), 它们提供了基于代理(proxy)或者基于拦截器(interception)的上下文相关session。从3.0.1版本开始,Hibernate增加了SessionFactory.getCurrentSession()方法。一 开始,它假定了采用JTA事务,JTA事务定义了当前session的范围和上下文(scope and context)。Hibernate开发团队坚信,因为有好几个独立的JTA TransactionManager实现稳定可用,不论是否被部署到一个J2EE容器中,大多数(假若不是所有的)应用程序都应该采用JTA事务管理。 基于这一点,采用JTA的上下文相关session可以满足你一切需要。

更好的是,从3.1开始,SessionFactory.getCurrentSession()的后台实现是可拔插的。因此,我们引入了新的扩展 接口 (org.hibernate.context.CurrentSessionContext)和新的配置参数 (hibernate.current_session_context_class),以便对什么是“当前session”的范围和上下文(scope and context)的定义进行拔插。

请参阅 org.hibernate.context.CurrentSessionContext接口的Javadoc,那里有关于它的契约的详细讨论。它定义 了单一的方法,currentSession(),特定的实现用它来负责跟踪当前的上下文session。Hibernate内置了此接口的两种实现。

org.hibernate.context.JTASessionContext - 当前session根据JTA来跟踪和界定。这和以前的仅支持JTA的方法是完全一样的。详情请参阅Javadoc。

org.hibernate.context.ThreadLocalSessionContext - 当前session通过当前执行的线程来跟踪和界定。详情也请参阅Javadoc。

这两种实现都提供了“每数据库事务对应一个session”的编程模型,也称作每次请求一个session。Hibernate session的起始和终结由数据库事务的生存来控制。假若你采用自行编写代码来管理事务(比如,在纯粹的J2SE,或者 JTA/UserTransaction/BMT),建议你使用Hibernate Transaction API来把底层事务实现从你的代码中隐藏掉。如果你在支持CMT的EJB容器中执行,事务边界是声明式定义的,你不需要在代码中进行任何事务或 session管理操作。请参阅第 11 章 事务和并发一节来阅读更多的内容和示例代码。

hibernate.current_session_context_class 配置参数定义了应该采用哪个org.hibernate.context.CurrentSessionContext实现。注意,为了向下兼容,如果未 配置此参数,但是存在org.hibernate.transaction.TransactionManagerLookup的配 置,Hibernate会采用org.hibernate.context.JTASessionContext。一般而言,此参数的值指明了要使用的实 现类的全名,但那两个内置的实现可以使用简写,即"jta"和"thread"。

1、getCurrentSession()与openSession()的区别?

* 采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession()
创建的session则不会
* 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession()
创建的session必须手动关闭
2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
* 如果使用的是本地事务(jdbc事务)
<property name="hibernate.current_session_context_class">thread</property>
* 如果使用的是全局事务(jta事务)
<property name="hibernate.current_session_context_class">jta</property>

利于ThreadLocal模式管理Session
   早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序
   时提供了一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,
   而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)
   其实的功用非常简单,就是为每一个使用某变量的线程都提供一个该变量值的副本,是每一个线程都可以独立地改变自己的副本,
   而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。
   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,
   用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):
public class HibernateUtil {

public static final ThreadLocal session =new ThreadLocal();

public static final SessionFactory sessionFactory;
   static {
      try {
        sessionFactory = new Configuration().configure().buildSessionFactory();
      } catch (Throwable ex) {
           throw new ExceptionInInitializerError(ex);
      }   
}

     public static Session currentSession() throws HibernateException {
        Session s = session.get();
        if(s == null) {
          s = sessionFactory.openSession();
          session.set(s);
           }
         return s;
       }

    public static void closeSession() throws HibernateException {
           Session s = session.get();
        if(s != null) {
            s.close();
        }
        session.set(null);
    }
}

以下为ThreadLocal的参考资料
Title

 最近由于需要用到ThreadLocal,在网上搜索了一些相关资料,发现对ThreadLocal经常会有下面几种误解

 一、ThreadLocal是java线程的一个实现
      ThreadLocal的确是和java线程有关,不过它并不是java线程的一个实现,它只是用来维护本地变量。针对每个线程,提供自己的变量版本,主要是为了避免线程冲突,每个线程维护自己的版本。彼此独立,修改不会影响到对方。

 二、ThreadLocal是相对于每个session的

        ThreadLocal顾名思义,是针对线程。在java web编程上,每个用户从开始到会话结束,都有自己的一个session标识。但是ThreadLocal并不是在会话层上。其 实,Threadlocal是独立于用户session的。它是一种服务器端行为,当服务器每生成一个新的线程时,就会维护自己的 ThreadLocal。对于这个误解,个人认为应该是开发人员在本地基于一些应用服务器测试的结果。众所周知,一般的应用服务器都会维护一套线程池,也 就是说,对于每次访问,并不一定就新生成一个线程。而是自己有一个线程缓存池。对于访问,先从缓存池里面找到已有的线程,如果已经用光,才去新生成新的线 程。所以,由于开发人员自己在测试时,一般只有他自己在测,这样服务器的负担很小,这样导致每次访问可能是共用同样一个线程,导致会有这样的误解:每个 session有一个ThreadLocal

 三、ThreadLocal是相对于每个线程的,用户每次访问会有新的ThreadLocal

  理论上来说,ThreadLocal是的确是相对于每个线程,每个线程会有自己的ThreadLocal。但是上面已经讲到,一般的应用服 务器都会维护一套线程池。因此,不同用户访问,可能会接受到同样的线程。因此,在做基于TheadLocal时,需要谨慎,避免出现 ThreadLocal变量的缓存,导致其他线程访问到本线程变量 .[senngr:HibernateUtil工具类中,一般都是通过closesession的方法,里面将opensession对应的session关闭.并将ThreadLocal变量置为NULL.这样线程池中如果再将这个线程分配给别人,对应的ThreadLocal是干净的.]

 四、对每个用户访问,ThreadLocal可以多用
        可以说,ThreadLocal是一把双刃剑,用得来的话可以起到非常好的效果。但是,ThreadLocal如果用得不好,就会跟全局变量一样。代码不 能重用,不能独立测试。因为,一些本来可以重用的类,现在依赖于ThreadLocal变量。如果在其他没有ThreadLocal场合,这些类就变得不 可用了。个人觉得ThreadLocal用得很好的几个应用场合,值得参考

  1、存放当前session用户:quake want的jert

  2、存放一些context变量,比如webwork的ActionContext

  3、存放session,比如Spring hibernate orm的session

posted @ 2011-11-02 01:37 redcoatjk 阅读(17657) | 评论 (3)编辑 收藏
 
http://space.itpub.net/9252210/viewspace-594453
今天在做数据导出的时候,由于用户名的密码使用的是特殊字符,所以遇到了错误代码:“EXP-00056: 遇到 ORACLE 错误 12154”,网上查找原因,需要用引号扩起来,但是os不同,方式也不同:

    windows os: exp username/"""password"""@devdb --3个双引号扩密码

    linux/unix os: exp 'username/"password"@devdb' --1个双引号扩密码,1个单引号扩全部

    实验结果如下:
1.创建带有特殊字符密码的用户
C:\Documents and Settings\Home>sqlplus /nolog
SQL*Plus: Release 10.2.0.1.0 - Production on 星期四 5月 7 17:37:36 2009
Copyright (c) 1982, 2005, Oracle.  All rights reserved.
SQL> connsys/oracle@devdbas sysdba
已连接。
SQL> create user exp identified by "12345!@#$%";
用户已创建。
SQL> grant connect, resource to exp;
授权成功。
SQL> conn exp/"12345!@#$%"@devdb2
已连接。
SQL> create table table1 as select * from dual;
表已创建。
SQL> exit
2.windows os导出测试
C:\Documents and Settings\Home>expexp/12345!@#$%@devdbfile=c:\exp.dmp wner=exp
Export: Release 9.2.0.1.0 - Production on 星期四 5月 7 17:39:42 2009
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
EXP-00056: 遇到 ORACLE 错误 12154
ORA-12154: TNS: 无法处理服务名
EXP-00000: 导出终止失败
C:\Documents and Settings\Home>exp exp/"12345!@#$%"@devdb2file=c:\exp.dmp wner=exp
Export: Release 9.2.0.1.0 - Production on 星期四 5月 7 17:39:57 2009
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
EXP-00056: 遇到 ORACLE 错误 12154
ORA-12154: TNS: 无法处理服务名
EXP-00000: 导出终止失败
C:\Documents and Settings\Home>exp exp/"""12345!@#$%"""@devdb2file=c:\exp.dmp wner=exp
Export: Release 9.2.0.1.0 - Production on 星期四 5月 7 17:41:54 2009
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
连接到: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options
已导出 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集
. 正在导出 pre-schema 过程对象和操作
. 正在导出用户 EXP 的外部函数库名称
. 导出 PUBLIC 类型同义词
. 导出私有类型同义词
. 正在导出用户 EXP 的对象类型定义
即将导出 EXP 的对象 ...
. 正在导出数据库链接
. 正在导出序号
. 正在导出群集定义
. 即将导出 EXP 的表通过常规路径 ...
. . 正在导出表                          TABLE1          1 行被导出
. 正在导出同义词
. 正在导出视图
. 正在导出存储的过程
. 正在导出运算符
. 正在导出引用完整性约束条件
. 正在导出触发器
. 正在导出索引类型
. 正在导出位图, 功能性索引和可扩展索引
. 正在导出后期表活动
. 正在导出实体化视图
. 正在导出快照日志
. 正在导出作业队列
. 正在导出刷新组和子组
. 正在导出维
. 正在导出 post-schema 过程对象和操作
. 正在导出统计
在没有警告的情况下成功终止导出。

3.linux/unix os导出测试
[oracle@rac2 ~]$ expexp/12345!@#$%@devdbfile=./exp.dmp wner=exp
-bash:!@#$%@devdb: event not found
[oracle@rac2 ~]$ exp exp/"""12345!@#$%"""@devdbfile=./exp.dmp wner=exp
-bash:!@#$%"""@devdb: event not found
[oracle@rac2 ~]$exp 'exp/"12345!@#$%"@devdb'file=./exp.dmp wner=exp 
Export: Release 10.2.0.4.0 - Production on Thu May 7 19:21:32 2009 
Copyright (c) 1982, 2007, Oracle.  All rights reserved. 
Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options
Export done in US7ASCII character set and AL16UTF16 NCHAR character set
server uses ZHS16GBK character set (possible charset conversion)
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user EXP
. exporting PUBLIC type synonyms
. exporting private type synonyms
. exporting object type definitions for user EXP
About to export EXP's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export EXP's tables via Conventional Path ...
. . exporting table                         TABLE1          1 rows exported
. exporting synonyms
. exporting views
. exporting stored procedures
. exporting operators
. exporting referential integrity constraints
. exporting triggers
. exporting indextypes
. exporting bitmap, functional and extensible indexes
. exporting posttables actions
. exporting materialized views
. exporting snapshot logs
. exporting job queues
. exporting refresh groups and children
. exporting dimensions
. exporting post-schema procedural objects and actions
. exporting statistics
Export terminated successfully without warnings.

posted @ 2011-10-19 17:03 redcoatjk 阅读(672) | 评论 (0)编辑 收藏
 

关于group by 的应用问题  

数据库内容为下面


 

写一SQL得出下面内容:


 

贴出SQL结果如下:(MySQL版本)

create table gosin_temp(rq varchar(10),shengfu nchar(1));

insert into gosin_temp values('2009-05-09','胜');
insert into gosin_temp values('2009-05-09','胜');
insert into gosin_temp values('2009-05-09','负');
insert into gosin_temp values('2009-05-09','负');
insert into gosin_temp values('2009-05-10','胜');
insert into gosin_temp values('2009-05-10','负');
insert into gosin_temp values('2009-05-10','负');

select * from gosin_temp;

得到结果的SQL:
select a1.rq,a1.胜,b1.负 from
(select a.rq, count(a.shengfu) 胜 from gosin_temp a where a.shengfu='胜' group by a.rq) a1,
(select b.rq, count(b.shengfu) 负 from gosin_temp b where b.shengfu='负' group by b.rq) b1
where a1.rq = b1.rq

类似的题目还有很多,如:

   胜 负
1 a b
2 b a
3 b a

要求写一SQL语句,输出如下结果:
   胜 负
a 1 2
b 2 1

其实都一样 只要熟悉使用group by 就不觉得难了。

posted @ 2011-08-25 17:12 redcoatjk 阅读(206) | 评论 (0)编辑 收藏
 
摘自:   http://www.iteye.com/topic/766418

-----------------------------------------
1,什么是Servlet
2,Servlet有什么作用
3,Servlet的生命周期
4,Servlet怎么处理一个请求
5,Servlet与JSP有什么区别
6,Servlet里的cookie技术
7,Servlet里的过滤器
8,Servlet里的监听器

 


一,什么是Servlet?


Servlet是一个Java编写的程序,此程序是基于Http协议的,在服务器端运行的(如tomcat),

 

是按照Servlet规范编写的一个Java类。



二,Servlet有什么作用?


主要是处理客户端的请求并将其结果发送到客户端。



三,Servlet的生命周期?


Servlet的生命周期是由Servlet的容器来控制的,它可以分为3个阶段;初始化,运行,销毁。

初始化阶段:


1,Servlet容器加载servlet类,把servlet类的.class文件中的数据读到内存中。


2,然后Servlet容器创建一个ServletConfig对象。ServletConfig对象包含了Servlet的初始化配置信息。


3,Servlet容器创建一个servlet对象。


4,Servlet容器调用servlet对象的init方法进行初始化。



运行阶段:


当servlet容器接收到一个请求时,servlet容器会针对这个请求创建servletRequest和servletResponse对象。

 

然后调用service方法。并把这两个参数传递给service方法。Service方法通过servletRequest对象获得请求的

 

信息。并处理该请求。再通过servletResponse对象生成这个请求的响应结果。然后销毁servletRequest和

 

servletResponse对象。我们不管这个请求是post提交的还是get提交的,最终这个请求都会由service方法来处理。


web服务器接受到一个http请求后,web服务器会将请求移交给 servlet容器,servlet容器首先对所请求的URL进行解析并根据
web.xml 配置文件找到相应的处理servlet,同时将request、response对象传递给它,servlet通过request对象可知道客户端
的请求 者、请求信息以及其他的信息等,servlet在处理完请求后会把所有需要返回的信息放入response对象中并返回到客户端,
servlet一旦处理 完请求,servlet容器就会刷新response对象,并把控制权重新返回给web服务器。

 


销毁阶段:


当Web应用被终止时,servlet容器会先调用servlet对象的destrory方法,然后再销毁servlet对象,

 

同时也会销毁与servlet对象相关联的servletConfig对象。我们可以在destroy方法的实现中,释放

 

servlet所占用的资源,如关闭数据库连接,关闭文件输入输出流等。



在这里该注意的地方:


在servlet生命周期中,servlet的初始化和和销毁阶段只会发生一次,而service方法执行的次数则取决于servlet被客户

 

端访问的次数

 

 


四,Servlet怎么处理一个请求?


当用户发送一个请求到某个Servlet的时候,Servlet容器会创建一个ServletRequst和ServletResponse对象。

 

在ServletRequst对象中封装了用户的请求信息,然后Servlet容器把ServletRequst和ServletResponse对象

 

传给用户所请求的Servlet,Servlet把处理好的结果写在ServletResponse中,然后Servlet容器把响应结果传

 

给用户。

 


五,Servlet与JSP有什么区别?


1,jsp经编译后就是servlet,也可以说jsp等于servlet。


2,jsp更擅长页面(表现)。servlet更擅长逻辑编辑。 (最核心的区别)。


3,在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中,

 

JSP位于MVC设计模式的视图层,而Servlet位于控制层。

 


六,Servlet里的cookie技术?


cookies是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段,是由Netscape公司开发出来的。


cookie技术的好处:


    1,Cookie有效期限未到时,Cookie能使用户在不键入密码和用户名的情况下进入曾经浏览过的一些站点。


    2,Cookie能使站点跟踪特定访问者的访问次数、最后访问时间和访问者进入站点的路径。
   
创建一个cookie

Java代码  收藏代码
  1. //里面的两个参数分别是cookie的名和cookie的值  
  2.   
  3. response.addCookie(new Cookie("abc","10000000"));  

 

使用cookie

Java代码  收藏代码
  1. Cookie[] cook =request.getCookies();//用一个Cookie数组来接收  
  2.   
  3. for(int j=0;j<cook.length;j++){//通过循环来打印Cookie  
  4.   
  5.         cook[j].getName()://取cookie的名    
  6.         cook[j].getValue()://去cookie的值  
  7.   
  8. }  
 


七,Servlet里的过滤器?


过滤器的主要作用


1,任何系统或网站都要判断用户是否登录。


2,网络聊天系统或论坛,功能是过滤非法文字


3,统一解决编码


(2)怎么创建一个过滤器:

 

1,生成一个普通的class类,实现Filter接口(javax.servlet.Filter;)。


2,重写接口里面的三个方法:init,doFilter,destroy。


3,然后在web.xml配置过滤器。



八,Servlet里的监听器?


监听器的作用:自动执行一些操作。

三种servlet监听器:

 

对request的监听。对session的监听。对application的监听。

怎么创建一个session监听器:


1,生成一个普通的class类,如果是对session的监听,则实现HttpSessionListener。


2,然后重写里面的五个方法:

 

Java代码  收藏代码
  1. public void sessionCreated(HttpSessionEvent arg0) {} // 创建  
  2.   
  3. public void sessionDestroyed(HttpSessionEvent arg0) {} // 销毁  
  4.   
  5. public void attributeAdded(HttpSessionEvent arg0) {} // 增加  
  6.   
  7. public void attributeRemoved(HttpSessionEvent arg0) {} // 删除  
  8.   
  9. public void attributeReplaced(HttpSessionEvent arg0) {} // 替换 
posted @ 2011-08-25 15:41 redcoatjk 阅读(209) | 评论 (0)编辑 收藏
 
这个写的不错.有的没看懂.
目前没时间细看,暂时先转载.
摘自:
http://www.jdon.com/artichect/state.htm

---------------------------------------------------

板桥里人 http://www.jdon.com 2006/1/2(转载请保留)

  这是一个实战中非常重要但是容易被忽视的概念,说它重要,是因为它比数据库重要;说它容易被忽视也是同样的原因,它经常被数据库概念替代。

  如果你经验和经历中没有状态这个概念,极端地说:可能你的Java系统经验还未积累到一定程度,状态是每个Java程序员深入Java系统后必然碰到的问题。

  本文我想试图表达的是:状态分两种:活动的状态对象和持久化的状态。而数据库中的数据只是状态的一种持久化结果,而Java系统 运行时,我们更多的可能是和一种活动的状态打交道,这种活动的状态存在内存中,而不是持久化到硬盘上,当然,需要时你可以通过数据库/文件持久化到硬盘上。

  但是,如果你以数据库数据替代状态,那么就可能导致数据库的频繁访问,而且 你的系统会变成一个非对象化的、紧耦合、到处是分散数据块的糟糕系统。这样的系统并不比传统的两层结构好到哪里!也不会比Jsp里嵌入Java代码伪三层系统高明到什么地方。

什么是状态?

  只要有对象就可能有状态,任何一个对象活动时,都有自己的状态属性,类的 字段属性极有可能成为状态,我们现在经常使用的Domain model其实就是一种 包含状态的对象,如果你对状态没有深入掌握,就不可能真正掌握对象系统特点,或者是Domain Model的执行情况。

  对于初学者,经常会疑问:我是将数据放在HttpSession中还是Request中,这里 其实已经开始接触状态,一旦你接触状态,你就要开始小心,因为你可能会将内存泄漏的恶魔导引进来。

  内存泄漏的恶魔爆发时刻取决于你状态的生存周期和系统并发访问量。

  状态的生存周期也就是包含这个状态的对象的生命周期,在简单系统中,我们只 需要通过new创建对象,然后它的消亡就会依靠JVM垃圾回收机制回收,但是事情会这么简单吗?

  状态的危险还会发生在多线程环境下,当多个线程对同一个内存中状态写操作时,这时怎么办?如果这个状 态持久化在数据库中,我们会依赖数据库提供的强大事务机制防止这种并发死锁,但是如果是在内存中,你就很难办,因此,我们就尽量避免发生这种多线程同时访 问一个状态的现象,而Singleton单例模式极容易发生这种现象,因此实践中,单例模式是J2EE开发中需要避免的,相关帖子讨论见:
http://www.jdon.com/jive/article.jsp?forum=91&thread=17578

  我们接触的Web容器或Jsp/Servlet本质就是一个多线程,这也是很多初学者不知道的, 因为多线程编程是复杂或困难的,因此才有jsp/Servlet这样的上层封装,但是我们使用他们
时,实际在进行多线程编程。

  生命周期和多线程并发使得我们简单的面向对象系统变得异常复杂和难以掌握起来。下面我从这个两个角度,给出两种模式思维解决之道。

生命周期(Scope)

  生命周期(Scope)就是指状态的活动周期,状态对象是什么时候被创建;然后什么时候被销毁,很显然,如果状态对象还没有被创建或已经被销毁,你再 访问这个状态对象可能失败,而状态的生命周期控制是可能散落在运行程序的各个地方,如果不象状态模式那样进行统一控制,有可能整个系统是危机四伏的。

  状态的生命周期其实就是对象生命周期,更加细化地说:是Domain Model这个对象的生命周期。这在一个以领域模型为驱动的设计概念中不可回避的课题,而领域模型实战的复杂性就复杂在此。

  状态的生命周期在J2EE中目前有三种:Request/Session和 Application,Request是每个客户端发出的一次请求,这是J2EE系统中最基本的事件激活单元, 当服务器端推出一个页面到客户端时,意味着这个Request的结束。那么如果我们的状态保存在Request中,意味着在request结束之前,这个 请求经历的任何一个环节都可以对这个状态(对象)进行操作。(掌握这个原理,对于你学习Struts和JSF很有帮助)

  如果是Session,则一直和该客户端有关,只要是该客户端发出的每次request的任何环节都可以对这个状态(对象)进行操作。

  如果是Application,则意味着这个状态是当前Web项目的全局状态。

  这三种状态形式都是以将状态保存在内存中形式存在的,是和持久化状态相对的。是一种内存活动状态。

  生命周期的选取当然是越短越好,这样,这个状态对象就可以被自动销毁,从而避免了
大访问量下的内存泄漏,但是在大访问量下,对象频繁创建和销毁是耗费性能的。

  那么,我们可能经常使用HttpSession来保存状态,这时你极有可能造成内存泄漏,我经常在 Jdon论坛上看到将很多数据库数据暂时保存在HttpSession中想法,这是相当危险的,因为一旦并发用户很多,相当多的HttpSession包 含了状态,而状态中有可能有更多其他引用,因此内存很快会爆满,或者垃圾回收机制频繁启动,造成应用系统运行暂停或缓慢。

  当你将状态放入HttpSession时,难道没有考虑将其手工消除吗?你要知道所有Web容器 (Tomcat/Weblogic等)都不会自动替你清除那些你可能不用的状态对象啊。如果每个人只管新增元素,不管重整或管理,这个系统能不变得混乱 吗?代码上这种现象我们是通过Refactoring等结构/行为模式来解决,那么在运行时的状态管理呢?

  状态管理模式或者说对象管理模式正是解决这种问题的。

   按照该模式,你必须手工自己管理放在HttpSession的状态,比如你为每个HttpSession
设立一个状态容器最大尺寸,当超过这个尺寸时,你需要将不用的状态从容器去除, 但是如果这个客户端在Session失效期内又来访问这个状态怎么办?那么你可能需要先临时将状态序列化保存到硬盘上,等Session失效期到达后再真正删除。

  是不是觉得很麻烦?
  捷径是有:
  1. 尽量少使用HttpSession保存状态,这对集群环境也是有利的,见该贴讨论:
http://www.jdon.com/jive/article.jsp?forum=121&thread=22282
那么这些状态放在哪里?使用Application的缓存中,

  2. 使用状态管理中间件,目前有几个选择:EJB的有态Bean;NanoContainer之类状态相关的微容器。那么Spring可以吗?目前没有发现有 该功能,甚至在Spring容器内无法直接使用Session性质的状态,只能通过线程级别的ThreadLocal来实现(对不起,你又要开始回到远古 的汇编线程时代了);而Jdon框架则可以。

  下面我们谈谈Application的状态,在这个范围内,一个对象状态可以被多个用户反复访问,在这个级别,状态类似数据库中数据,因为可以使用数据库来替代这个级别的状态,所以将状态放入缓存这个深层次技术被大多数初学者忽视了,甚至产生了对数据库依赖心理。

缓存中的状态

  虽然我们将状态保存在Application中,但是我们不可避免还是遇到Session同样的状态管理问题,这个问题所幸的是有专门缓存中间件解决 了,当然,在一个多服务器集群系统,如果一个客户端在一个服务器中存放了状态,那么能否在另外一个服务器的内存中访问到呢?回答是肯定的,前提是你必须使 用分布式缓存系统。

  目前分布式缓存系统是靠EJB服务器完成,当JBoss 5在2006变成完全解耦、可肢解时,
我们就可以使用原本只支持EJB的JBoss分布式缓存系统来支持我们的普通JavaBeans了(POJO)。这其中目前可能会花费一些力气,因为还没有一个统一的POJO构件接口标准,我相信以后
可能会有。

  如果你不想花费力气,而且可能就只是一台服务器,可以通过双核芯片提升性能,那么单态缓存如果实现?很简单,使用一个缓存产品如OsCache等,将其设定保存在 Application中,或者在web.xml中进行一下简单的配置即可。

  但是,这时你可能碰到另外一个问题:状态的唯一标识,如何通过唯一标识从缓存中那么
多对象状态中取出你要的那一个呢?比较琐碎。

  有没有一个框架帮助你省却这些麻烦,当然推荐Jdon Framework,只要将包含状态的类(主要是Domain Model)继承特定的类或接口(接口在1.4版本实现)即可,这个类的对象运行时就会被缓存或从缓存中读取,再也无需你照料缓存了,就这么简单。

  当然,Jdon Framework的底层缓存器是可以被替代,使用你喜欢的缓存产品,因为jdon
Framework是基于Ioc设计,构件之间是完全解耦、可彻底肢解,能够通过配置替代和更换的。
如果你不明白这个道理,需要好好研究一下Ioc模式带给我们革命性的新变化。

  从以上也可以看出:java复杂性还在于我们需要在编码时,却要想象其运行时的情形。而这种翻译联想没有深厚的实践功底,是很难顺利完成的。

状态管理中间件

  自从J2EE开辟中间件时代以来,就有相当多的高级中间件提供与具体应用无关的通用功能,状态管理中间件很早就有之,EJB的有态Session Bean是一个代表。

  一个中间件不但要有良好的松耦合设计,我们暂时称为静态设计;更要有优秀的动态设计,例如状态管理就属于一种动态设计。

  当然,如果你比较谦虚,不但要选择一些静态设计很好的框架或中间件;而且还要依赖一些拥有良好的动态运行管理的中间件。

  EJB无论是EJB1.X/EJB2.X/EJB3.X.在状态管理上要更加优秀,当然EJB3.X又吸收了优秀的静态设计概念,但是因为需要有一个具体服务器实现过程,这个过程中存在一些陷阱,如In-Box问题等。

  Spring无疑是一个静态设计非常优秀框架,它一直在AOP上孜孜不倦,力图探索一条从AOP角度进行动态运行管理干预捷径,相信会有惊人结果,当然,这种细粒度的AOP需要实践检验,当然如果整入JDK 6.0就更好。

  而Jdon Framework则试图在目前两者之间寻求了一个平衡,既有Ioc/AOP优秀的静态设计,虽然在AOP上不及Spring前卫;但提供了切实Session和Cache状态管理;

  如果你不需要EJB的分布式多服务器集群功能;又不是AOP的超级粉丝,无疑使用Jdon Framework之类的框架无疑是简化方便的。

状态设计的难点

  最后,我不得不重申,并不是有了良好的状态管理框架就可以高枕无忧了,状态的设计其实是我们每个项目必须面临的可变课题,如果状态复杂了可以使用状态模式对付,可惜往往状态不够复杂。

  一个对象本身属性和状态是应该耦合在一起,还是进行分离,属性和状态没有明显的泾渭分明的界限,我们举一个例子:

  论坛Forum这个对象,它有一些字段属性,如论坛名称、论坛描述,还有其他一些相关属性:如该论坛 的最新发帖;该论坛的发贴量,后两者好像也是论坛字段,但是他们可能经常变化的,应该属于状态,那么状态和Forum这个主体对象是什么关系?是将该论坛 的最新发帖和该论坛的发贴量两个字段并入Forum这个Domain Model中,还是应该单独建立一个状态对象?如果进行分离,分离的依据是什么?

  当然,这里分离的依据是因为对象的生存周期不同。对于我们熟悉的课题,我们能够马上分辨出其中的生存周期,如果是不熟悉领域的建模呢?

  所以,大家已经明白:状态设计的难点是:如何粒度细化地创建模型对象;然后分辨出其中动态的状态性质。这是域建模实战中一个难点。

  很多人问我:你提倡的域建模、设计模式和框架是什么意思?为什么说他们是Java开发设计的三件宝呢?或者说三个典型知识点呢?我想通过本篇我已经通过状态这个概念稍微解释了域建模的一些特点。

  当前,MDA中的四色原型模式Archetype将帮助我们更好地分辨出类的属性、状态和行为,这是一场带来以后十年的软件革命,
posted @ 2011-08-25 15:38 redcoatjk 阅读(174) | 评论 (0)编辑 收藏
 

我对浏览器请求流程的理解:

(1) 访问流程:

(1.1) 系统运行在某web容器,Tomcat(其运行和weblogic不同,Tomcat只有一个进程).

      其预设有初始的线程数.

(1.2) 浏览器打开某网站,网站及给其分配一个sessionID(页面隐式的发起request, Tomcat

      某个特定的监听线程给予response一个sessionid). sessionid用以识别本次访问.

(1.3) 用户点击登录/注册, 浏览器发起一个新的request, Tomcat线程池中空闲的线程进行

       处理. 反馈结果于前台展现.如线程池中线程不足,Tomcat每次按照一定规则创建出更多的空闲线程(其初始值,增加值,及最大值依据配置文件/JDK/硬件).

posted @ 2011-08-25 11:37 redcoatjk 阅读(215) | 评论 (0)编辑 收藏
 
来源:
http://www.iteye.com/topic/960652
http://godsend-jin.iteye.com/blog/1004386
--------------------------------------------------------------------

最近在做登录和权限控制模块,用到了session,发现session的好多方法都不熟悉,而且以前也听说过JsessionId 之类session窃取的事,

对这些一直都是一知半解。今天索性google了很多资料,先上sun的官网去看session的文档了解一些方法,又找了别人关于session的看法。

总结如下:

      1,session是什么?  what

          session经常译为会话,以打电话为例,从开始拨号到挂断电话就是你会话的生存周期。

      2,session 做什么用的  why?

          首先举个例子:

                咖啡店举行 消费满5杯咖啡赠送一杯的活动,可每个人一次消费5杯的时候非常少。这时候有3种办法:

                1,店员看到每个顾客时都能记住其消费了多少杯,从而给其优惠,这是协议本身具有状态

                2,给每个顾客一个卡片,上面记录顾客的每次消费,这是客户端保存状态

                3,给每个顾客一个卡片,卡片上只有一个编号,顾客每次的消费记录在店里,这就是 服务端有状态

          而http本身是无状态的,所以我们只能使用2,3中方法,来保存一些信息。

          实际采用的是第3种方法,服务器段保存一次会话所有的信息,并生成一个唯一的id,这个id没有规律而且不会重复,将这个id传回到客户段,

保存到cookie中。每次访问服务器时,客户端都会偷偷将这个id传到服务器,服务器根据id查到这次会话保存的内容。就能实现会话中共享一些数据。

     3,session怎样创建和销毁 ? how

         session是保存在内存中的,所以会有一些性能上的影响。因此本着这个原则,session是只有在使用到的时候才会被创建,如果始终没有用到

session,这个session是永远不会被创建的。

        比如: 访问servlet ,只要你代码中没有 request.getSession()或request.getSession(true);这两行是等价的,那session是不会创建。

又 当你访问静态页面时,根本不会生成servlet,所以也不会创建session。

       下面解释一些疑惑: session是第一次请求时创建的?

  大家都知道 jsp是被编译成servlet才执行的,问题就在jsp编译的过程。

 jsp中有个<%@ page session="true/false"%> 这个开关表示是否创建session,当你不写这行时,它会默认给你加上这句。所以会造成上面的疑惑。

当然还有一些标签中可能有getSession()操作,会产生一些不必要的session。

       session只能在服务端销毁,有三种途径: 1,到达session的最大请求间隔时间时,2,session。invalidate()

 3,服务器进程当掉。

       这里也有一些疑惑: 浏览器关闭时,session就会注销。

       首先浏览器关闭时,浏览器并没有给服务器发送任何消息,所以服务器不会知道浏览器何时关闭了。

 上面我们知道取得session 是因为浏览器cookie中有sessionid,而普通cookie通常会是会话cookie,也就是说浏览器关闭时,这个cookie会被注销,

所以当你再访问服务器时就没有sessionid了,所以造成session关闭了的假象,如果昵称通过特殊方法将sessionid传递给服务器,你会发现session还在。

如果想让cookie保存时间长一些,就需要手动指定cookie的过期时间

 

4,实际项目中的难点:

     1,浏览器禁用cookie

      这就没办法保存sessionid了,可以采用url重写,转发,加隐藏字段等方法来将sessionid传给服务器。

     如:  baidu.com:jsessionid=adfasdfasdfasdfasdfafdadf?asdfasdf

             baidu.com?jsessionid=asdfasdfasdfadsfad&&adfdf

 这根据服务器的不同实现,第一种可以将普通参数区分开。

 

     2,多人共用session的问题

        例: a 访问 baidu.com ,但他没有帐号,于是他将连接 baidu.com/login.jsp?jsessionid=adsfasdfad(这个a的sessionid) 发给B, B登录

后,a就相当于用b的帐号登录了。你们可以在在本地试试。

        解决方法: 当发现通过sessionid从url指定时, 创建一个新的session,将旧session的信息复制到 新sessoin中,然后将新session注销。

就能防止上面那种情况了。

    3,一个帐号多地方登录

       比如: 你用abc帐号登录了baidu.com,有打开了一个浏览器,又用abc帐号登录了一次。当不设计敏感操作时,这无所谓,而当你做一些敏感操

作时就必须禁止这样情况,防止同时操作,造成重复操作,或者数据损坏。

     解决方法: 监听session,将username和sessionid对应起来,当username再次登录时,注销掉以前的session,保存现在的session,这也是

一种比较不错的方案。

 

这是 sghcel 画的图,挺不错的:

 



其他:
 1session在何时被创建 
    一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时
才被创建,注意如果
JSP没有显示的使用 <%@page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句
HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。 

   
由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。


    2
session何时被删除
  综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();b.距离上一次收到客户端发送的session id时间间隔超过了session
超时设置
;c.服务器进程被停止(非持久session


    3
、如何做到在浏览器关闭时删除session
Title
  严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送
一个请求来删除
session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。


    4
、有个HttpSessionListener是怎么回事 

   
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener
而不是相反。类似的与
HttpSession有关的listener还有 HttpSessionBindingListenerHttpSessionActivationListener HttpSessionAttributeListener 

    5
、存放在session中的对象必须是可序列化的吗 


 
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。
Weblogic Serversession中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象,
session销毁时会有一个Exception,很奇怪。


    6
、如何才能正确的应付客户端禁止cookie的可能性
 
    对所有的URL使用URL重写,包括超链接,formaction,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770


    7
、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session 

   
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题
的答案有影响。
 

    8
、如何防止用户打开两个浏览器窗口操作导致的session混乱 
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,
客户端提交表单时必须把这个
id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。
可以参看《
J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id
以防主窗口无法操作,建议不要再
window.open打开的窗口里做修改操作,这样就可以不用设置。


    9
、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue 


做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。


   10HttpSession 和 hibernate session 有什么区别?
httpSession :

它的产生:J2EE的Web程序在运行的时候,会给每一个新的访问者建立一个HttpSession,这个Session是用户身份的唯一表示。注意,是容器

(Tomcat,Resin)自动创建的。

用途:存放这个用户的一些经常被用到的信息,例如:用户名,权限。例如在购物车程序里,存放用户买的商品。

销毁:一定时间(跟容器有关)内,用户无任何动作,session自动销毁。

得到的方法:
HttpSession session = request.getSession();
常用方法setAttribute
session.setAttribute(key,value);
这样在另一个jsp或者Servlet里,可以用
session.getAttribute(key);得到value
类似一个Map


hibernate session

它是hibernate操作数据库的一个句柄对象。它跟上面那个Session唯一的相似处就是名字有点像,其他没任何一样的地方。
它是hibernate表示操作数据库的一个会话

 

posted @ 2011-08-25 11:22 redcoatjk 阅读(1237) | 评论 (0)编辑 收藏
 

AOP术语介绍

1. 正常的编程为从上到下的调用,执行



2. 加入了安全性检查,日志这样的代码. 这是一个横切的问题,其于正常的业务毫无关系.

横切的问题会散布在代码的各个角落
.


3.这个横切就是横切性的关注点: Cross cutting concern





4.
将横切的关注点都放在一个类中(如动态代理项目中的SecurityHandler.java).这个类就叫做切面.

   对横切关注点进行模块化,这个模块化的类就叫做切面类(Aspect对应的类) ,


 

 

5. 在切面类中对某个问题如日志或安全性检查的具体实现方法,叫做横切关注点的具体实现(称为Advice).






 

 

6. 这个Advice可以进行分类. :在业务方法执行之前,之后.异常时候……


 

7. Advice应用的目标方法范围(那些方法之前,之后,异常….)这个过滤范围叫做切入点Pointcut()


 

8 .植入

Advice应用的目标方法的过程叫做植入(Weave)

Spring只支持针对业务方法执行前,执行后进行植入. 即只支持方法级别的植入

植入的地方就叫做连接点.

SpringAop是使用代理模式.

 

横切问题的处理思路:

发现横切性的问题,将其模块化(切片).然后形成切片类,在其中实现这些横切性的功能.

posted @ 2011-06-26 21:58 redcoatjk 阅读(188) | 评论 (0)编辑 收藏
 
对象: pickTicket 出库  1 : N  pickTicketDetail 出库明细
数据库: pickTicket的id作为pickTicketDetail 的外键(pickTicket.id)
---------
在对pickTicketDetail 某行数据进行insert/update/delete的事务提交给数据库进行持久化处理时,数据库会将
pickTicket整个表给予只读锁. 此为避免修改pickTicketDetail 时,pickTicket的数据进行修改而出现
pickTicketDetail中的pickTicket.id对应在pickTicket中不存在.(即确保数据库的数据完整性)

发生场景: 事件A增删改某表A时,会对该表的所有外键对应的表给予只读锁.如此时其他事务B正提交数据库,类似的需要锁定事件A中已锁定的表.
此时2个事务都在等待对方表解锁.产生死锁.


可以处理方案:
1尽量缩短事务处理的时间.
2对pickTicketDetail 增加pickTicket.id的索引.
------------
相关参考:
http://it.china-b.com/sjk/oracle/20090826/177376_1.html
测试:

session 1:

SQL> delete from emp where emp.deptno=10;

session 2:

SQL> delete from dept where deptno=40;

现象:在emp的字段deptno没有索引时session 2等待, 有索引则不等待.

结论:如果没有索引时,对父表的操作,会级联加一个TM S锁(level 4)到子表上;

如果有索引时,对父表的操作,会级联加一个TM RS锁(level 2)到子表上;

这时如果子表上本身就有个TM RX锁(这种锁很容易产生,insert update delete都会产生这种锁),

TM S锁和TM RX锁是互斥的, TM RS锁和TM RX锁是相容的.


-------

最后:其实想记录的是"外键未加索引的问题" .此问题可以google下了解.

本文写的较乱,只为存疑记录.有待进一步了解学习.



posted @ 2011-05-31 19:09 redcoatjk 阅读(150) | 评论 (0)编辑 收藏
 
摘自:
http://www.iteye.com/topic/257191

-----------------
今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36)。
1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的。
equals()方法在object类中定义如下:
  public boolean equals(Object obj) {
return (this == obj);
}
很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们必需清楚,当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。比 如在String类中如下:
  public boolean equals(Object anObject) {
if (this == anObject) {
    return true;
}
if (anObject instanceof String) {
    String anotherString = (String)anObject;
    int n = count;
    if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
    if (v1[i++] != v2[j++])
return false;
}
return true;
    }
}
return false;
}
很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。当然了基本类型是进行值的比较,这个没有什么好说的。
我们还应该注意,Java语言对equals()的要求如下,这些要求是必须遵循的:
• 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
• 反射性:x.equals(x)必须返回是“true”。
• 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
• 还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
• 任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。
2. 其次是hashcode() 方法,在object类中定义如下:
  public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、 Integer、Double。。。。等等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下:
    public int hashCode() {
int h = hash;
if (h == 0) {
    int off = offset;
    char val[] = value;
    int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
}
解释一下这个程序(String的API中写到):
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。)

3.这里我们首先要明白一个问题:
equals()相等的两个对象,hashcode()一定相等;
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。
反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解释 下第3点的使用范围,我的理解是在object、String等类中都能使用。在object类中,hashcode()方法是本地方法,返回的是对象的 地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然 hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时,
Hashcode()方法根据String类的重写(第2点里面已经分析了)代码的分析,也可知道hashcode()返回结果也会相等。以此类 推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的 类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。

4.谈到hashcode()和equals()就不能不说到hashset,hashmap,hashtable中的使用,具体是怎样呢,请看如下分析:
Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么hashset是根据什么原理来存取对象的呢?
在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?这就是问题的关键所在,经过一下午的查询求证终于获得了一点启示,和大家分享一下,在java的集合中,判断两个对象是否相等的规则是:
1),判断两个对象的hashCode是否相等
      如果不相等,认为两个对象也不相等,完毕
      如果相等,转入2)
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。后面会重点讲到这个问题。)
2),判断两个对象用equals运算是否相等
      如果不相等,认为两个对象也不相等
      如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。
比如下面的代码:

public static void main(String args[]){
String s1=new String("zhaoxudong");
String s2=new String("zhaoxudong");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()
System.out.println(s2.hashCode());
Set hashset=new HashSet();
hashset.add(s1);
hashset.add(s2);
/*实质上在添加s1,s2时,运用上面说到的两点准则,可以知道hashset认为s1和s2是相等的,是在添加重复元素,所以让s2覆盖了s1;*/
Iterator it=hashset.iterator();
            while(it.hasNext())
            {
             System.out.println(it.next());
            }
最后在while循环的时候只打印出了一个”zhaoxudong”。
输出结果为:false
            true
            -967303459
            -967303459
这是因为String类已经重写了equals()方法和hashcode()方法,所以在根据上面的第1.2条原则判定时,hashset认为它们是相等的对象,进行了重复添加。
但是看下面的程序:
import java.util.*;
public class HashSetTest
{
   public static void main(String[] args)
    {
                 HashSet hs=new HashSet();
                 hs.add(new Student(1,"zhangsan"));
                 hs.add(new Student(2,"lisi"));
                 hs.add(new Student(3,"wangwu"));
                 hs.add(new Student(1,"zhangsan"));
 
                 Iterator it=hs.iterator();
                 while(it.hasNext())
                 {
                        System.out.println(it.next());
                 }
     }
}
class Student
   {
     int num;
     String name;
     Student(int num,String name)
                {
                this.num=num;
                 this.name=name;
                 }
              public String toString()
                {
                    return num+":"+name;
                 }
           }     
输出结果为:
                      1:zhangsan
                   1:zhangsan
                   3:wangwu
                   2:lisi
问题出现了,为什么hashset添加了相等的元素呢,这是不是和hashset的原则违背了呢?回答是:没有
因为在根据hashcode()对两次建立的new Student(1,"zhangsan")对象进行比较时,生成的是不同的哈希码值,所以hashset把他当作不同的对象对待了,当然此时的 equals()方法返回的值也不等(这个不用解释了吧)。那么为什么会生成不同的哈希码值呢?上面我们在比较s1和s2的时候不是生成了同样的哈希码 吗?原因就在于我们自己写的Student类并没有重新自己的hashcode()和equals()方法,所以在比较时,是继承的object类中的 hashcode()方法,呵呵,各位还记得object类中的hashcode()方法比较的是什么吧!!
它是一个本地方法,比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象了(这个大家都能理解吧。。。),造成 的结果就是两个对象的hashcode()返回的值不一样。所以根据第一个准则,hashset会把它们当作不同的对象对待,自然也用不着第二个准则进行 判定了。那么怎么解决这个问题呢??
答案是:在Student类中重新hashcode()和equals()方法。
例如:
  class Student
{
int num;
String name;
Student(int num,String name)
{
            this.num=num;
            this.name=name;
}
public int hashCode()
{
            return num*name.hashCode();
}
public boolean equals(Object o)
{
            Student s=(Student)o;
            return num==s.num && name.equals(s.name);
}
public String toString()
{
            return num+":"+name;
}
}
根据重写的方法,即便两次调用了new Student(1,"zhangsan"),我们在获得对象的哈希码时,根据重写的方法hashcode(),获得的哈希码肯定是一样的(这一点应该没有疑问吧)。
当然根据equals()方法我们也可判断是相同的。所以在向hashset集合中添加时把它们当作重复元素看待了。所以运行修改后的程序时,我们会发现运行结果是:
                      1:zhangsan
                   3:wangwu
                   2:lisi
可以看到重复元素的问题已经消除。
关于在hibernate的pojo类中,重新equals()和hashcode()的问题:
1),重点是equals,重写hashCode只是技术要求(为了提高效率)
2),为什么要重写equals呢,因为在java的集合框架中,是通过equals来判断两个对象是否相等的
3),在hibernate中,经常使用set集合来保存相关对象,而set集合是不允许重复的。我们再来谈谈前面提到在向hashset集合中添加元素时,怎样判断对象是否相同的准则,前面说了两条,其实只要重写equals()这一条也可以。
但当hashset中元素比较多时,或者是重写的equals()方法比较复杂时,我们只用equals()方法进行比较判断,效率也会非常低, 所以引入了hashcode()这个方法,只是为了提高效率,但是我觉得这是非常有必要的(所以我们在前面以两条准则来进行hashset的元素是否重复 的判断)。
比如可以这样写:
public int hashCode(){
   return  1;}//等价于hashcode无效
这样做的效果就是在比较哈希码的时候不能进行判断,因为每个对象返回的哈希码都是1,每次都必须要经过比较equals()方法后才能进行判断是否重复,这当然会引起效率的大大降低。
我有一个问题,如果像前面提到的在hashset中判断元素是否重复的必要方法是equals()方法(根据网上找到的观点),但是这里并没有涉及到关于哈希表的问题,可是这个集合却叫hashset,这是为什么??
我想,在hashmap,hashtable中的存储操作,依然遵守上面的准则。所以这里不再多说。这些是今天看书,网上查询资料,自己总结出来 的,部分代码和语言是引述,但是千真万确是自己总结出来的。有错误之处和不详细不清楚的地方还请大家指出,我也是初学者,所以难免会有错误的地方,希望大 家共同讨论。
posted @ 2011-05-27 18:15 redcoatjk 阅读(353) | 评论 (0)编辑 收藏
 
摘自:
http://shitou521.iteye.com/blog/696006

JNDI的一篇文章

前端时间总是在搞不清JNDI到底是干什么,虽然是一值在用,却不知道他最初出现的原因,用来,说不清是用来干什么,下面我相信介能解开这个迷雾里。

转贴一篇】 
------------ 
JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识。 

那么,JNDI到底起什么作用?//带着问题看文章是最有效的 

要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。 

没有JNDI的做法: 

程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。 
就像以下代码这样: 

Java代码 
  1. Connection conn=null;  
  2. try {  
  3.   Class.forName("com.mysql.jdbc.Driver",  
  4.                 true, Thread.currentThread().getContextClassLoader());  
  5.   conn=DriverManager.  
  6.     getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");  
  7.   ......  
  8.   conn.close();  
  9. catch(Exception e) {  
  10.   e.printStackTrace();  
  11. finally {  
  12.   if(conn!=null) {  
  13.     try {  
  14.       conn.close();  
  15.     } catch(SQLException e) {}  
  16.   }  
  17. }  



这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。 

没有JNDI的做法存在的问题: 
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改; 
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改; 
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整; 
4、...... 

解决办法: 
程 序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。 

由此,就有了JNDI。 
//看的出来,是为了一个最最核心的问题:是为了解耦,是为了开发出更加可维护、可扩展//的系统 

用了JNDI之后的做法: 
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。 

//红色的字可以看出,JNDI是由j2ee容器提供的功能 

具体操作如下(以JBoss为例): 
1、配置数据源 
在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:\jboss420GA\server\default\deploy。 
修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下: 
Java代码 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <datasources>  
  3. <local-tx-datasource>  
  4.     <jndi-name>MySqlDS</jndi-name>  
  5.     <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>  
  6.     <driver-class>com.mysql.jdbc.Driver</driver-class>  
  7.     <user-name>root</user-name>  
  8.     <password>rootpassword</password>  
  9. <exception-sorter-class-name>  
  10. org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter  
  11. </exception-sorter-class-name>  
  12.     <metadata>  
  13.        <type-mapping>mySQL</type-mapping>  
  14.     </metadata>  
  15. </local-tx-datasource>  
  16. </datasources>  


这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。 

2、在程序中引用数据源: 

Java代码 
  1. Connection conn=null;  
  2. try {  
  3.   Context ctx=new InitialContext();  
  4.   Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源  
  5.   DataSource ds=(Datasource)datasourceRef;  
  6.   conn=ds.getConnection();  
  7.   ......  
  8.   c.close();  
  9. catch(Exception e) {  
  10.   e.printStackTrace();  
  11. finally {  
  12.   if(conn!=null) {  
  13.     try {  
  14.       conn.close();  
  15.     } catch(SQLException e) { }  
  16.   }  
  17. }  


直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。//解藕了,可扩展了 
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。 

由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。 

JNDI的扩展: 
JNDI在满足了数据源配置的要求的基础上,还进一步扩充了作用:所有与系统外部的资源的引用,都可以通过JNDI定义和引用。 
//注意什么叫资源 

所以,在J2EE规范中,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。 

EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。外部资源”。 


总结: 
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。//sun 果然喜欢制定规范JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。 

在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。 


从上面的文章中可以看出: 
1、JNDI 提出的目的是为了解藕,是为了开发更加容易维护,容易扩展,容易部署的应用。 
2、JNDI 是一个sun提出的一个规范(类似于jdbc),具体的实现是各个j2ee容器提供商,sun   只是要求,j2ee容器必须有JNDI这样的功能。 
3、JNDI 在j2ee系统中的角色是“交换机”,是J2EE组件在运行时间接地查找其他组件、资源或服务的通用机制。 
4、JNDI 是通过资源的名字来查找的,资源的名字在整个j2ee应用中(j2ee容器中)是唯一的。 

再转一篇文章: 


JNDI全称 Java Naming and Directory Interface 
JNDI 是Java平台的一个标准扩展,提供了一组接口、类和关于命名空间的概念。如同其它很多Java技术一样,JDNI是provider-based的技 术,暴露了一个API和一个服务供应接口(SPI)。这意味着任何基于名字的技术都能通过JNDI而提供服务,只要JNDI支持这项技术。JNDI目前所 支持的技术包括LDAP、CORBA Common Object Service(COS)名字服务、RMI、NDS、DNS、Windows注册表等等。很多J2EE技术,包括EJB都依靠JNDI来组织和定位实体。 
JDNI通过绑定的概念将对象和名称联系起来。在一个文件系统中,文件名被绑定给文件。在DNS中,一个IP地址绑定一个URL。在目录服务中,一个对象名被绑定给一个对象实体。 
JNDI 中的一组绑定作为上下文来引用。每个上下文暴露的一组操作是一致的。例如,每个上下文提供了一个查找操作,返回指定名字的相应对象。每个上下文都提供了绑 定和撤除绑定名字到某个对象的操作。JNDI使用通用的方式来暴露命名空间,即使用分层上下文以及使用相同命名语法的子上下文。 
jndi的用途: 
1。你可以用jndi来得到object类的属性 
如: 
Java代码 
  1. Attribute attr =directory.getAttributes(personName).get("email");   
  2. String email = (String)attr.get();   

2。你可以用jndi来搜索对象 
如: 
Java代码 
  1. foxes = directory.search("o=Wiz,c=US""sn=Fox", controls);   

查找谁的名字叫Fox在wiz部门的员工? 
3。你可以用jndi通过naming/directory服务查询像printers和databases的对象 
如:查询 Printer 
Java代码 
  1. Printer printer = (Printer)namespace.lookup(printerName);   
  2. printer.print(document);   

4。你可以用jndi列表出命名空间的特殊级别的内容 
如: 
Java代码 
  1. NamingEnumeration list = namespace.list("o=Widget, c=US";   
  2. while (list.hasMore()) {   
  3. NameClassPair entry = (NameClassPair)list.next();   
  4. display(entry.getName(), entry.getClassName());   
  5. }  
posted @ 2011-05-22 10:34 redcoatjk 阅读(403) | 评论 (0)编辑 收藏
 
各种数字类型转换成字符串型:

String s = String.valueOf( value); // 其中 value 为任意一种数字类型。

字符串型转换成各种数字类型:

String s = "169";
byte b = Byte.parseByte( s );
short t = Short.parseShort( s );
int i = Integer.parseInt( s );
long l = Long.parseLong( s );
Float f = Float.parseFloat( s );
Double d = Double.parseDouble( s );

数字类型与数字类对象之间的转换:

byte b = 169;
Byte bo = new Byte( b );
b = bo.byteValue();

short t = 169;
Short to = new Short( t );
t = to.shortValue();

int i = 169;
b = bo.byteValue();

short t = 169;
Short to = new Short( t );
t = to.shortValue();

int i = 169;
Integer io = new Integer( i );
i = io.intValue();

long l = 169;
Long lo = new Long( l );
l = lo.longValue();

float f = 169f;
Float fo = new Float( f );
f = fo.floatValue();

double d = 169f;
Double dObj = new Double( d );
d = dObj.doubleValue();
posted @ 2011-02-21 13:34 redcoatjk 阅读(191) | 评论 (0)编辑 收藏
 
  1. 触发器使用教程和命名规范  
  2.   
  3.   
  4. 目  录  
  5. 触发器使用教程和命名规范    1  
  6. 1,触发器简介 1  
  7. 2,触发器示例 2  
  8. 3,触发器语法和功能  3  
  9. 4,例一:行级触发器之一    4  
  10. 5,例二:行级触发器之二    4  
  11. 6,例三:INSTEAD OF触发器  6  
  12. 7,例四:语句级触发器之一   8  
  13. 8,例五:语句级触发器之二   9  
  14. 9,例六:用包封装触发器代码  10  
  15. 10,触发器命名规范  11  
  16.   
  17. 1,触发器简介  
  18. 触发器(Trigger)是数据库对象的一种,编码方式类似存储过程,与某张表(Table)相关联,当有DML语句对表进行操作时,可以引起触发器的执行,达到对插入记录一致性,正确性和规范性控制的目的。在当年C/S时代盛行的时候,由于客户端直接连接数据库,能保证数据库一致性的只有数据库本身,此时主键(Primary Key),外键(Foreign Key),约束(Constraint)和触发器成为必要的控制机制。而触发器的实现比较灵活,可编程性强,自然成为了最流行的控制机制。到了B/S时代,发展成4层架构,客户端不再能直接访问数据库,只有中间件才可以访问数据库。要控制数据库的一致性,既可以在中间件里控制,也可以在数据库端控制。很多的青睐Java的开发者,随之将数据库当成一个黑盒,把大多数的数据控制工作放在了Servlet中执行。这样做,不需要了解太多的数据库知识,也减少了数据库编程的复杂性,但同时增加了Servlet编程的工作量。从架构设计来看,中间件的功能是检查业务正确性和执行业务逻辑,如果把数据的一致性检查放到中间件去做,需要在所有涉及到数据写入的地方进行数据一致性检查。由于数据库访问相对于中间件来说是远程调用,要编写统一的数据一致性检查代码并非易事,一般采用在多个地方的增加类似的检查步骤。一旦一致性检查过程发生调整,势必导致多个地方的修改,不仅增加工作量,而且无法保证每个检查步骤的正确性。触发器的应用,应该放在关键的,多方发起的,高频访问的数据表上,过多使用触发器,会增加数据库负担,降低数据库性能。而放弃使用触发器,则会导致系统架构设计上的问题,影响系统的稳定性。  
  19.   
  20.   
  21. 2,触发器示例  
  22. 触发器代码类似存储过程,以PL/SQL脚本编写。下面是一个触发器的示例:  
  23. 新建员工工资表salary  
  24. create table SALARY  
  25. (  
  26.   EMPLOYEE_ID NUMBER, --员工ID  
  27.   MONTH       VARCHAR2(6), --工资月份  
  28.   AMOUNT      NUMBER --工资金额  
  29. )  
  30.   
  31. 创建与salary关联的触发器salary_trg_rai  
  32. 1   Create or replace trigger salary_trg_rai  
  33. 2   After insert on salary  
  34. 3   For each row  
  35. 4   declare  
  36. 5   Begin  
  37. 6     Dbms_output.put_line(‘员工ID:’ || :new.employee_id);  
  38. 7     Dbms_output.put_line(‘工资月份:’ || :new.month);  
  39. 8     Dbms_output.put_line(‘工资:’ || :new.amount);  
  40. 9     Dbms_output.put_line(‘触发器已被执行’);  
  41. 10   End;  
  42. 打开一个SQL Window窗口(使用PL/SQL Developer工具),或在sqlplus中输入:  
  43. Insert into salary(employee_id, month, amount) values(1, ‘200606’, 10000);  
  44. 执行后可以在sqlplus中,或在SQL Window窗口的Output中见到  
  45. 员工ID:1  
  46. 工资月份:200606  
  47. 工资:10000  
  48. 触发器已执行  
  49.   
  50. 在代码的第一行,定义了数据库对象的类型是trigger,定义触发器的名称是salary_trg_rai  
  51. 第二行说明了这是一个after触发器,在DML操作实施之后执行。紧接着的insert说明了这是一个针对insert操作的触发器,每个对该表进行的insert操作都会执行这个触发器。  
  52. 第三行说明了这是一个针对行级的触发器,当插入的记录有n条时,在每一条插入操作时都会执行该触发器,总共执行n次。  
  53. Declare后面跟的是本地变量定义部分,如果没有本地变量定义,此部分可以为空  
  54. Begin和end括起来的代码,是触发器的执行部分,一般会对插入记录进行一致性检查,在本例中打印了插入的记录和“触发器已执行”。  
  55. 其中:new对象表示了插入的记录,可以通过:new.column_name来引用记录的每个字段值  
  56.   
  57.   
  58. 3,触发器语法和功能  
  59. 触发器的语法如下  
  60. CREATE OR REPLACE TRIGGER trigger_name  
  61. <before | after | instead of> <insert | update | delete> ON table_name  
  62. [FOR EACH ROW]  
  63. WHEN (condition)  
  64. DECLARE  
  65. BEGIN  
  66.     --触发器代码  
  67. END;  
  68.   
  69. Trigger_name 是触发器的名称。<before | after | instead of>可以选择before或者after或instead of。 Before表示在DML语句实施前执行触发器,而after表示在在dml语句实施之后执行触发器,instead of触发器用在对视图的更新上。<insert | update | delete>可以选择一个或多个DML语句,如果选择多个,则用or分开,如:insert or update。Table_name是触发器关联的表名。  
  70. [FOR EACH ROW]为可选项,如果注明了FOR EACH ROW,则说明了该触发器是一个行级的触发器,DML语句处理每条记录都会执行触发器;否则是一个语句级的触发器,每个DML语句触发一次。  
  71. WHEN后跟的condition是触发器的响应条件,只对行级触发器有效,当操作的记录满足condition时,触发器才被执行,否则不执行。Condition中可以通过new对象和old对象(注意区别于前面的:new和:old,在代码中引用需要加上冒号)来引用操作的记录。  
  72. 触发器代码可以包括三种类型:未涉及数据库事务代码,涉及关联表(上文语法中的table_name)数据库事务代码,涉及除关联表之外数据库事务代码。其中第一种类型代码只对数据进行简单运算和判断,没有DML语句,这种类型代码可以在所有的触发器中执行。第二种类型代码涉及到对关联表的数据操作,比如查询关联表的总记录数或者往关联表中插入一条记录,该类型代码只能在语句级触发器中使用,如果在行级触发器中使用,将会报ORA-04091错误。第三种类型代码涉及到除关联表之外的数据库事务,这种代码可以在所有触发器中使用。  
  73.   
  74. 从触发器的功能上来看,可以分成3类:  
  75.    重写列(仅限于before触发器)  
  76.    采取行动(任何触发器)  
  77.    拒绝事务(任何触发器)  
  78. “重写列”用于对表字段的校验,当插入值为空或者插入值不符合要求,则触发器用缺省值或另外的值代替,在多数情况下与字段的default属性相同。这种功能只能在行级before触发器中执行。“采取行动”针对当前事务的特点,对相关表进行操作,比如根据当前表插入的记录更新其他表,银行中的总帐和分户帐间的总分关系就可以通过这种触发器功能来维护。“拒绝事务”用在对数据的合法性检验上,当更新的数据不满足表或系统的一致性要求,则通过抛出异常的方式拒绝事务,在其上层的代码可以捕获这个异常并进行相应操作。  
  79.   
  80. 下面将通过举例说明,在例子中将触发器主体的语法一一介绍,读者可以在例子中体会触发器的功能。  
  81.   
  82. 4,例一:行级触发器之一  
  83. CREATE OR REPLACE TRIGGER salary_raiu  
  84. AFTER INSERT OR UPDATE OF amount ON salary  
  85. FOR EACH ROW  
  86. BEGIN  
  87.     IF inserting THEN  
  88.         dbms_output.put_line(‘插入’);  
  89.     ELSIF updating THEN  
  90. dbms_output.put_line(‘更新amount列’);  
  91.     END IF;  
  92. END;  
  93. 以上是一个after insert和after update的行级触发器。在第二行中of amount on salary的意思是只有当amount列被更新时,update触发器才会有效。所以,以下语句将不会执行触发器:  
  94. Update salary set month = ‘200601’ where month = ‘200606’;  
  95. 在触发器主体的if语句表达式中,inserting, updating和deleting可以用来区分当前是在做哪一种DML操作,可以作为把多个类似触发器合并在一个触发器中判别触发事件的属性。  
  96.   
  97. 5,例二:行级触发器之二  
  98. 新建员工表employment  
  99. CREATE TABLE EMPLOYMENT  
  100. (  
  101.   EMPLOYEE_ID NUMBER, --员工ID  
  102.   MAXSALARY   NUMBER --工资上限  
  103. )  
  104. 插入两条记录  
  105. Insert into employment values(11000);  
  106. Insert into employment values(22000);  
  107.   
  108. CREATE OR REPLACE TRIGGER salary_raiu  
  109. AFTER INSERT OR UPDATE OF amount ON salary  
  110. FOR EACH ROW  
  111. WHEN ( NEW.amount >= 1000 AND (old.amount IS NULL OR OLD.amount <= 500))  
  112. DECLARE  
  113.     v_maxsalary NUMBER;  
  114. BEGIN  
  115.     SELECT maxsalary  
  116.         INTO v_maxsalary  
  117.         FROM employment  
  118.      WHERE employee_id = :NEW.employee_id;  
  119.     IF :NEW.amount > v_maxsalary THEN  
  120.         raise_application_error(-20000'工资超限');  
  121.     END IF;  
  122. END;  
  123.   
  124. 以上的例子引入了一个新的表employment,表中的maxsalary字段代表该员工每月所能分配的最高工资。下面的触发器根据插入或修改记录的 employee_id,在employment表中查到该员工的每月最高工资,如果插入或修改后的amount超过这个值,则报错误。  
  125. 代码中的when子句表明了该触发器只针对修改或插入后的amount值超过1000,而修改前的amount值小于500的记录。New对象和old对象分别表示了操作前和操作后的记录对象。对于insert操作,由于当前操作记录无历史对象,所以old对象中所有属性是null;对于delete操作,由于当前操作记录没有更新对象,所以new对象中所有属性也是null。但在这两种情况下,并不影响old和new对象的引用和在触发器主体中的使用,和普通的空值作同样的处理。  
  126. 在触发器主体中,先通过:new.employee_id,得到该员工的工资上限,然后在if语句中判断更新后的员工工资是否超限,如果超限则错误代码为-20000,错误信息为“工资超限”的自定义错误。其中的raise_application_error包含两个参数,前一个是自定义错误代码,后一个是自定义错误代码信息。其中自定义错误代码必须小于或等于-20000。执行完该语句后,一个异常被抛出,如果在上一层有exception子句,该异常将被捕获。如下面代码:  
  127. DECLARE  
  128.     code NUMBER;  
  129.     msg  VARCHAR2(500);  
  130. BEGIN  
  131.     INSERT INTO salary (employee_id, amount) VALUES (25000);  
  132. EXCEPTION  
  133.     WHEN OTHERS THEN  
  134.         code := SQLCODE;  
  135.         msg  := substr(SQLERRM, 1500);  
  136.         dbms_output.put_line(code);  
  137.         dbms_output.put_line(msg);  
  138. END;  
  139. 执行后,将在output中或者sqlplus窗口中见着以下信息:  
  140. -20000  
  141. ORA-20000: 工资超出限制  
  142. ORA-06512: 在"SCOTT.SALARY_RAI", line 9  
  143. ORA-04088: 触发器 'SCOTT.SALARY_RAI' 执行过程中出错  
  144.   
  145. 这里的raise_application_error相当于拒绝了插入或者修改事务,当上层代码接受到这个异常后,判断该异常代码等于-20000,可以作出回滚事务或者继续其他事务的处理。  
  146.   
  147. 以上两个例子中用到的inserting, updating, deleting和raise_application_error都是dbms_standard包中的函数,具体的说明可以参照Oracle的帮助文档。  
  148. create or replace package sys.dbms_standard is  
  149.   procedure raise_application_error(num binary_integer, msg varchar2,  
  150.   function inserting return boolean;  
  151.   function deleting  return boolean;  
  152.   function updating  return boolean;  
  153.   function updating (colnam varchar2) return boolean;  
  154. end;  
  155.   
  156. 对于before和after行级触发器,:new和:old对象的属性值都是一样的,主要是对于在Oracle约束(Constraint)之前或之后的执行触发器的选择。需要注意的是,可以在before行触发器中更改:new对象中的值,但是在after行触发器就不行。  
  157.   
  158. 下面介绍一种instead of触发器,该触发器主要使用在对视图的更新上,以下是instead of触发器的语法:  
  159. CREATE OR REPLACE TRIGGER trigger_name  
  160. INSTEAD OF <insert | update | delete> ON view_name  
  161. [FOR EACH ROW]  
  162. WHEN (condition)  
  163. DECLARE  
  164. BEGIN  
  165.     --触发器代码  
  166. END;  
  167.   
  168. 其他部分语法同前面所述的before和after语法是一样的,唯一不同的是在第二行用上了instead of关键字。对于普通的视图来说,进行 insert等操作是被禁止的,因为Oracle无法知道操作的字段具体是哪个表中的字段。但我们可以通过建立instead of触发器,在触发器主体中告诉Oracle应该更新,删除或者修改哪些表的哪部分字段。如:  
  169.   
  170. 6,例三:instead of触发器  
  171. 新建视图  
  172. CREATE VIEW employee_salary(employee_id, maxsalary, MONTH, amount) AS   
  173. SELECT a.employee_id, a.maxsalary, b.MONTH, b.amount  
  174. FROM employment a, salary b  
  175. WHERE a.employee_id = b.employee_id  
  176.   
  177. 如果执行插入语句  
  178. INSERT INTO employee_salary(employee_id, maxsalary, MONTH, amount)  
  179. VALUES(10100000'200606'10000);  
  180. 系统会报错:  
  181. ORA-01779:无法修改与非键值保存表对应的列  
  182.   
  183. 我们可以通过建立以下的instead of存储过程,将插入视图的值分别插入到两个表中:  
  184. create or replace trigger employee_salary_rii  
  185.   instead of insert on employee_salary    
  186.   for each ROW  
  187. DECLARE  
  188.     v_cnt NUMBER;  
  189. BEGIN  
  190.   --检查是否存在该员工信息  
  191.     SELECT COUNT(*)  
  192.         INTO v_cnt  
  193.         FROM employment  
  194.      WHERE employee_id = :NEW.employee_id;  
  195.     IF v_cnt = 0 THEN  
  196.         INSERT INTO employment  
  197.             (employee_id, maxsalary)  
  198.         VALUES  
  199.             (:NEW.employee_id, :NEW.maxsalary);  
  200.     END IF;  
  201.   --检查是否存在该员工的工资信息  
  202.     SELECT COUNT(*)  
  203.         INTO v_cnt  
  204.         FROM salary  
  205.      WHERE employee_id = :NEW.employee_id  
  206.          AND MONTH = :NEW.MONTH;  
  207.     IF v_cnt = 0 THEN  
  208.         INSERT INTO salary  
  209.             (employee_id, MONTH, amount)  
  210.         VALUES  
  211.             (:NEW.employee_id, :NEW.MONTH, :NEW.amount);  
  212.     END IF;  
  213. END employee_salary_rii;  
  214.   
  215. 该触发器被建立后,执行上述insert操作,系统就会提示成功插入一条记录。  
  216. 但需要注意的是,这里的“成功插入一条记录”,只是Oracle并未发现触发器中有异常抛出,而根据insert语句中涉及的记录数作出一个判断。若触发器的主体什么都没有,只是一个空语句,Oracle也会报“成功插入一条记录”。同样道理,即使在触发器主体里往多个表中插入十条记录,Oracle的返回也是“成功插入一条记录”。  
  217.   
  218.   
  219.   
  220.   
  221. 行级触发器可以解决大部分的问题,但是如果需要对本表进行扫描检查,比如要检查总的工资是否超限了,用行级触发器是不行的,因为行级触发器主体中不能有涉及到关联表的事务,这时就需要用到语句级触发器。以下是语句级触发器的语法:  
  222. CREATE OR REPLACE TRIGGER trigger_name  
  223. <before | after | instead of ><insert | update | delete > ON table_name  
  224. DECLARE  
  225. BEGIN  
  226.     --触发器主体  
  227. END;  
  228.   
  229. 从语法定义上来看,行级触发器少了for each row,也不能使用when子句来限定入口条件,其他部分都是一样的,包括insert, update, delete和instead of都可以使用。  
  230.   
  231.   
  232. 7,例四:语句级触发器之一  
  233. CREATE OR REPLACE TRIGGER salary_saiu  
  234. AFTER INSERT OR UPDATE OF amount ON salary  
  235. DECLARE  
  236.     v_sumsalary NUMBER;  
  237. BEGIN  
  238.   SELECT SUM(amount) INTO v_sumsalary FROM salary;  
  239.     IF v_sumsalary > 500000 THEN  
  240.         raise_application_error(-20001'总工资超过500000');  
  241.     END IF;  
  242. END;  
  243.   
  244. 以上代码定义了一个语句级触发器,该触发器检查在insert和update了amount字段后操作后,工资表中所有工资记录累加起来是否超过500000,如果超过则抛出异常。从这个例子可以看出,语句级触发器可以对关联表表进行扫描,扫描得到的结果可以用来作为判断一致性的标志。需要注意的是,在 before语句触发器主体和after语句触发器主体中对关联表进行扫描,结果是不一样的。在before语句触发器主体中扫描,扫描结果将不包括新插入和更新的记录,也就是说当以上代码换成 before触发器后,以下语句将不报错:  
  245. INSERT INTO salary(employee_id, month, amount) VALUEs(2'200601'600000)  
  246. 这是因为在主体中得到的v_sumsalary并不包括新插入的600000工资。  
  247. 另外,在语句级触发器中不能使用:new和:old对象,这一点和行级触发器是显著不同的。如果需要检查插入或更新后的记录,可以采用临时表技术。  
  248. 临时表是一种Oracle数据库对象,其特点是当创建数据的进程结束后,进程所创建的数据也随之清除。进程与进程不可以互相访问同一临时表中对方的数据,而且对临时表进行操作也不产生undo日志,减少了数据库的消耗。具体有关临时表的知识,可以参看有关书籍。  
  249. 为了在语句级触发器中访问新插入后修改后的记录,可以增加行级触发器,将更新的记录插入临时表中,然后在语句级触发器中扫描临时表,获得修改后的记录。临时表的表结构一般与关联表的结构一致。  
  250.   
  251.   
  252. 8,例五:语句级触发器之二  
  253. 目的:限制每个员工的总工资不能超过50000,否则停止对该表操作。  
  254. 创建临时表  
  255. create global temporary table SALARY_TMP  
  256. (  
  257.   EMPLOYEE_ID NUMBER,  
  258.   MONTH       VARCHAR2(6),  
  259.   AMOUNT      NUMBER  
  260. )  
  261. on commit delete rows;  
  262.   
  263. 为了把操作记录插入到临时表中,创建行级触发器:  
  264. CREATE OR REPLACE TRIGGER salary_raiu  
  265. AFTER INSERT OR UPDATE OF amount ON salary  
  266. FOR EACH ROW  
  267. BEGIN  
  268.   INSERT INTO salary_tmp(employee_id, month, amount)  
  269.   VALUES(:NEW.employee_id, :NEW.MONTH, :NEW.amount);  
  270. END;  
  271. 该触发器的作用是把更新后的记录信息插入到临时表中,如果更新了多条记录,则每条记录都会保存在临时表中。  
  272.   
  273. 创建语句级触发器:  
  274. CREATE OR REPLACE TRIGGER salary_sai  
  275. AFTER INSERT OR UPDATE OF amount ON salary  
  276. DECLARE  
  277.     v_sumsalary NUMBER;  
  278. BEGIN  
  279.     FOR cur IN (SELECT * FROM salary_tmp) LOOP  
  280.         SELECT SUM(amount)  
  281.             INTO v_sumsalary  
  282.             FROM salary  
  283.          WHERE employee_id = cur.employee_id;  
  284.         IF v_sumsalary > 50000 THEN  
  285.             raise_application_error(-20002'员工累计工资超过50000');  
  286.         END IF;  
  287.     DELETE FROM salary_tmp;  
  288.     END LOOP;  
  289. END;  
  290.   
  291. 该触发器首先用游标从salary_tmp临时表中逐条读取更新或插入的记录,取employee_id,在关联表salary中查找所有相同员工的工资记录,并求和。若某员工工资总和超过50000,则抛出异常。如果检查通过,则清空临时表,避免下次检查相同的记录。  
  292. 执行以下语句:  
  293. INSERT INTO salary(employee_id, month, amount) VALUEs(7'200601'20000);  
  294. INSERT INTO salary(employee_id, month, amount) VALUEs(7'200602'20000);  
  295. INSERT INTO salary(employee_id, month, amount) VALUEs(7'200603'20000);  
  296. 在执行第三句时系统报错:  
  297. ORA-20002:员工累计工资超过50000  
  298. 查询salary表,发现前两条记录正常插入了,第三条记录没有插入。  
  299.   
  300.   
  301. 如果系统结构比较复杂,而且触发器的代码比较多,在触发器主体中写过多的代码,对于维护来说是一个困难。这时可以将所有触发器的代码写到同一个包中,不同的触发器代码以不同的存储过程封装,然后触发器主体中调用这部分代码。  
  302.   
  303. 9,例六:用包封装触发器代码  
  304. 目的:改写例五,封装触发器主体代码  
  305. 创建代码包:  
  306. CREATE OR REPLACE PACKAGE BODY salary_trigger_pck IS  
  307.   
  308.     PROCEDURE load_salary_tmp(i_employee_id IN NUMBER,  
  309.                             i_month       IN VARCHAR2,  
  310.                             i_amount      IN NUMBER) IS  
  311.     BEGIN  
  312.         INSERT INTO salary_tmp VALUES (i_employee_id, i_month, i_amount);  
  313.     END load_salary_tmp;  
  314.   
  315.     PROCEDURE check_salary IS  
  316.         v_sumsalary NUMBER;  
  317.     BEGIN  
  318.         FOR cur IN (SELECT * FROM salary_tmp) LOOP  
  319.             SELECT SUM(amount)  
  320.                 INTO v_sumsalary  
  321.                 FROM salary  
  322.              WHERE employee_id = cur.employee_id;  
  323.             IF v_sumsalary > 50000 THEN  
  324.                 raise_application_error(-20002'员工累计工资超过50000');  
  325.             END IF;  
  326.             DELETE FROM salary_tmp;  
  327.         END LOOP;  
  328.     END check_salary;  
  329. END salary_trigger_pck;  
  330. 包salary_trigger_pck中有两个存储过程,load_salary_tmp用于在行级触发器中调用,往salary_tmp临时表中装载更新或插入记录。而check_salary用于在语句级触发器中检查员工累计工资是否超限。  
  331.   
  332. 修改行级触发器和语句级触发器:  
  333. CREATE OR REPLACE TRIGGER salary_raiu  
  334.     AFTER INSERT OR UPDATE OF amount ON salary  
  335.     FOR EACH ROW  
  336. BEGIN  
  337.     salary_trigger_pck.load_salary_tmp(:NEW.employee_id,     :NEW.MONTH, :NEW.amount);  
  338. END;  
  339.   
  340. CREATE OR REPLACE TRIGGER salary_sai  
  341. AFTER INSERT OR UPDATE OF amount ON salary  
  342. BEGIN  
  343.     salary_trigger_pck.check_salary;  
  344. END;  
  345.   
  346. 这样主要代码就集中到了salary_trigger_pck中,触发器主体中只实现了一个调用功能。  
  347.   
  348. 10,触发器命名规范  
  349. 为了方便对触发器命名和根据触发器名称了解触发器含义,需要定义触发器的命名规范:  
  350. Trigger_name = table_name_trg_<R|S><A|B|I><I|U|D>  
  351.   
  352. 触发器名限于30个字符。必须缩写表名,以便附加触发器属性信息。  
  353. <R|S>基于行级(row)还是语句级(statement)的触发器  
  354. <A|B|I>after, before或者是instead of触发器  
  355. <I|U|D>触发事件是insert,update还是delete。如果有多个触发事件则连着写  
  356.   
  357. 例如:  
  358. Salary_rai      salary表的行级after触发器,触发事件是insert  
  359. Employee_sbiud  employee表的语句级before触发器,触发事件是insert,update和delete  
posted @ 2010-08-04 15:27 redcoatjk 阅读(314) | 评论 (0)编辑 收藏
 

经常在用apache和tomcat等这些服务器,可是总感觉还是不清楚他们之间有什么关系,在用tomcat的时候总出现apache,总感到迷惑,到底谁是主谁是次,因此特意在网上查询了一些这方面的资料,总结了一下:

 

apache支持静态页,tomcat支持动态的,比如servlet等,


一般使用apache+tomcat的话,apache只是作为一个转发,对jsp的处理是由tomcat来处理的。

apache可以支持php\cgi\perl,但是要使用java的话,你需要tomcat在apache后台支撑,将java请求由apache转发给tomcat处理。

apache是web服务器,Tomcat是应用(java)服务器,它只是一个servlet(jsp也翻译成servlet)容器,可以认为是apache的扩展,但是可以独立于apache运行。

 

这两个有以下几点可以比较的:
1、两者都是apache组织开发的
2、两者都有HTTP服务的功能
3、两者都是免费的

不同点:
Apache是专门用了提供HTTP服务的,以及相关配置的(例如虚拟主机、URL转发等等)
Tomcat是Apache组织在符合J2EE的JSP、Servlet标准下开发的一个JSP服务器

 

 

二:

APACHE是一个web服务器环境程序 启用他可以作为web服务器使用 不过只支持静态网页 如(asp,php,cgi,jsp)等动态网页的就不行
如果要在APACHE环境下运行jsp 的话就需要一个解释器来执行jsp网页 而这个jsp解释器就是TOMCAT, 为什么还要JDK呢?因为jsp需要连接数据库的话 就要jdk来提供连接数据库的驱程,所以要运行jsp的web服务器平台就需要APACHE+TOMCAT+JDK

整合的好处是:
如果客户端请求的是静态页面,则只需要Apache服务器响应请求
如果客户端请求动态页面,则是Tomcat服务器响应请求
因为jsp是服务器端解释代码的,这样整合就可以减少Tomcat的服务开销

 

 

三:

apache:侧重于http server
tomcat:侧重于servlet引擎,如果以standalone方式运行,功能上与apache等效 , 支持JSP,但对静态网页不太理想;
apache是web服务器,tomcat是应用(java)服务器,它只是一个servlet(jsp也翻译成servlet)容器,可以认为是apache的扩展,但是可以独立于apache运行。
换句话说,apache是一辆卡车,上面可以装一些东西如html等。但是不能装水,要装水必须要有容器(桶),而这个桶也可以不放在卡车上。

posted @ 2010-08-02 16:03 redcoatjk 阅读(186) | 评论 (0)编辑 收藏
 
     摘要:       proxool一个数据库连接池框架,提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速,成熟,健 壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。到目前为止最新版本是proxool 0.9.1,可从官网下载最新版本http://proxool.sourceforge.net ...  阅读全文
posted @ 2010-07-29 12:17 redcoatjk 阅读(605) | 评论 (0)编辑 收藏
 
1.
在hibernate中写hql时候.
"from item i where i.supplier="+supplier  这样写法是不行的.
改为
"from item i where i.supplier.id="+supplier.getId(); 才可以.
2.boolean的处理
java中
1 /**是否可库存**/
2     private boolean invItemFlag = false;

hbm中
1         <property name="invItemFlag" type="boolean">
2             <column name="INV_ITEM_FLAG" />
3         </property>
然后插入数据库时,false的值对应db的0.true的值对应db的1.
在hibernate的hql查询时候
不可以写
from item i where i.invItemFlag='ture'
而应该写
from item i where i.invItemFlag=1


posted @ 2010-07-27 10:28 redcoatjk| 编辑 收藏
 
载自http://xiaoxinshome.javaeye.com/blog/139609
--------
语法规则:
Create [or replace] trigger [模式.]触发器名

       Before| after   insert|delete|(update of 列名)

On 表名

[for each row]

When 条件

PL/SQL块

说明:

For each row的意义是:在一次操作表的语句中,每操作成功一行就会触发一 次;不写的话,表示是表级触发器,则无论操作多少行,都只触发一次;

When条件的出现说明了,在DML操作的时候也许一定会触发触发器,但是触发器不一定会做实际的工作,比如when 后的条件不为真的时候,触发器只是简单地跳过了PL/SQL块;

例子:

sql 代码
  1. create or replace trigger wf_tri_user_list before insert or update or delete on user_list   
  2. for each row   
  3. declare  
  4.    uid varchar2(10); useq varchar2(10); asql varchar2(200); namea varchar2(200); nameb varchar2(200);   
  5. begin  
  6.    namea:=NULL;   
  7.    nameb:=NULL;   
  8.    if inserting then  
  9.       insert into wflow.bpm_org_user(userid,username,diaplayname,seq) values(:NEW.user_id,:NEW.user_name,:NEW.user_realname,:NEW.user_id);   
  10.       dbms_output.put_line('insert trigger is chufale .....');   
  11.         
  12.    end if;   
  13.    if updating then  
  14.       if (:NEW.user_name<>:OLD.user_name) and (:NEW.user_realname<>:OLD.user_realname) then  
  15.          namea:=:NEW.user_name;   
  16.          nameb:=:NEW.user_realname;   
  17.          asql:='update wflow.bpm_org_user set diaplayname=:1 where username=:2';   
  18.          execute immediate asql using namea,nameb;   
  19.       else  
  20.         if :NEW.user_name<>:OLD.user_name then  
  21.           namea:=:NEW.user_name;   
  22.           asql:='update wflow.bpm_org_user set user_name=:1 where username=:2';   
  23.           execute immediate asql using namea;   
  24.         else  
  25.           if :NEW.user_realname<>:OLD.user_realname then  
  26.             nameb:=:NEW.user_realname;   
  27.             asql:='update wflow.bpm_org_user set diaplayname=:1 where username=:2';   
  28.             execute immediate asql using nameb,:OLD.user_id;   
  29.           end if;   
  30.         end if;   
  31.       end if;   
  32.    end if;   
  33.    if deleting then  
  34.       update wflow.bpm_org_jobusers set userid = 0 where :OLD.user_id =userid and parentid=-1;   
  35.       delete from wflow.bpm_org_jobusers where userid = :OLD.user_id;   
  36.       delete wflow.bpm_org_user where userid=:OLD.user_id;   
  37.    end if;   
  38.    commit;   
  39. end;   
  40.   

 

关键字:

:NEW 和:OLD使用方法和意义,new 只出现在insert和update时,old只出现在update和delete时。在insert时new表示新插入的行数据,update时new 表示要替换的新数据、old表示要被更改的原来的数据行,delete时old表示要被删除的数据。

注意:

在触发器中不能使用commit。



posted @ 2010-06-12 11:49 redcoatjk 阅读(137) | 评论 (0)编辑 收藏
 
 DECODE()函数,它将输入数值与函数中的参数列表相比较,根据输入值返回一个对应值。函数的参数列表是由若干数值及其对应结果值组成的若干序偶形 式。当然,如果未能与任何一个实参序偶匹配成功,则函数也有默认的返回值。

区别于SQL的其它函数,DECODE函数还能识别和操作空值。

语法:DECODE(control_value,value1,result1[,value2,result2…] [,default_result]);

control _value试图处理的数值。DECODE函数将该数值与后面的一系列的偶序相比较,以决定返回值。

value1是一组成序偶的数值。如果输入数值与之匹配成功,则相应的结果将被返回。对应一个空的返回值,可以使用关键字NULL于之对应

result1 是一组成序偶的结果值。

default_result 未能与任何一个值匹配时,函数返回的默认值。

例如:

selectdecode( x , 1 , ‘x is 1 ’, 2 , ‘x is 2 ’, ‘others’) from dual

当x等于1时,则返回‘x is 1’。

当x等于2时,则返回‘x is 2’。

否则,返回others’。

需要,比较2个值的时候,可以配合SIGN()函数一起使用。

SELECT DECODE( SIGN(5 -6), 1 'Is Positive', -1, 'Is Nagative', 'Is Zero')

同样,也可以用CASE实现:

SELECT CASE SIGN(5 - 6)

WHEN 1 THEN 'Is Positive'

WHEN -1 THEN 'Is Nagative'

ELSE 'Is Zero' END

FROM DUAL

此外,还可以在Order by中使用Decode。

例如:表table_subject,有subject_name列。要求按照:语、数、外的顺序进行排序。这时,就可以非常轻松的使用 Decode完成要求了。

select * from table_subject order by decode(subject_name, '语文', 1, '数学', 2, , '外语',3)
posted @ 2010-06-12 11:05 redcoatjk 阅读(157) | 评论 (0)编辑 收藏
 

Unique约束

Unique约束可应用于一列或多列字段上。如果字段值存在,必须为唯一的,可以取null值
1、一张表只能有一个PK约束但可以有多个Unique约束
2、作为PK的字段不能为null,但作为Unique的字段可以为null,但不为null的行必须是Unique的
3、当创建一个PK时,创建一个Index,创建一个Unique时也创建一个Index
4、PK和Unique约束字段可以作为FK的父亲。FK约束字段引用PK约束,也引用Unique约束

创建语句:
  CREATE TABLE temp (pk NUMBER PRIMARY KEY, a NUMBER, b NUMBER);
     ALTER TABLE temp ADD CONSTRAINT uk_temp_a_b UNIQUE (a, b);

Example:
CREATE TABLE students
 (student_id    VARCHAR2(10) NOT NULL,
  student_name  VARCHAR2(30) NOT NULL,
  college_major VARCHAR2(15) NOT NULL,
  status        VARCHAR2(15) NOT NULL,
  state         VARCHAR2(2),
  license_no    VARCHAR2(30)) TABLESPACE student_data;

ALTER TABLE students
  ADD CONSTRAINT pk_students PRIMARY KEY (student_id)
  USING INDEX TABLESPACE student_index;

ALTER TABLE students
  ADD CONSTRAINT uk_students_license
  UNIQUE (state, license_no)
  USING INDEX TABLESPACE student_index;

ALTER TABLE students
   ADD CONSTRAINT ck_students_st_lic
   CHECK ((state IS NULL AND license_no IS NULL) OR
          (state IS NOT NULL AND license_no is NOT NULL));
posted @ 2010-05-27 09:49 redcoatjk 阅读(354) | 评论 (0)编辑 收藏
 
作为开源的连接池Proxool
有以下优点。
透明性   可以明的添加接连池而不影响你原来的项目的JDBC代码;
开放性 你可以方便的与其它的开源产品进行整合。如hibernate  中自带的这个Proxool
标准性 它是在J2SE下开出来的。你可以放心的开发
易用性  非常容易 的进行配置。
proxool是一个非常强大的连接池工具包,我觉得相比dbcp、c3p0这两个连接池包都要好用,我用loadrunner测试过,这三个连 接池的从性能上排名如下:proxool>c3p0>dbcp,特别是dbcp在大并发的情况下总是出现各种异常。

下面是实现proxool的几种方式:

JDBC连接方法:
首先建一个proxool的配置文件proxool.xml

proxool.xml 代码
xml 代码

<!--sp-->xml version="1.0" encoding="UTF-8"?>    
  
<!-- the proxool configuration can be embedded within your own application's. Anything outside the "proxool" tag is ignored. -->    
  
<something-else-entirely>  
    <proxool>  
        <!--连接池的别名-->  
        <alias>DBPool</alias>  
        <!--proxool只能管理由自己产生的连接-->  
        <driver-url>jdbc:oracle:thin:@192.168.0.40:1521:drcom</driver-url>  
        <!--JDBC驱动程序-->  
        <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>  
        <driver-properties>  
            <property name="user" value="drcom"/>  
            <property name="password" value="drcom"/>  
        </driver-properties>  
        <!-- proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁-->  
        <house-keeping-sleep-time>90000</house-keeping-sleep-time>  
        <!-- 指因未有空闲连接可以分配而在队列中等候的最大请求数,超过这个请求数的用户连接就不会被接受-->    
        <maximum-new-connections>150</maximum-new-connections>  
        <!-- 最少保持的空闲连接数-->    
        <prototype-count>3</prototype-count>  
        <!-- 允许最大连接数,超过了这个连接,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定-->    
        <maximum-connection-count>100</maximum-connection-count>  
        <!-- 最小连接数-->  
        <minimum-connection-count>3</minimum-connection-count>  
    </proxool>  
</something-else-entirely>  

再在web.xml中进行配置,其中的ServletConfigurator是装载WEB-INF目录下的proxool.xml,并设置为Tomcat启动时就加载。Admin这个Servlet是proxool提供的察看连接池的信息的工具,


web.xml 代码
xml 代码

<servlet>
      <servlet-name>proxoolServletConfigurator</servlet-name>
     <servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
     <init-param>
       <param-name>xmlFile</param-name>
       <param-value>WEB-INF/config/proxool.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
   </servlet>
<!-- proxool提供的管理监控工具,可查看当前数据库连接情况。如果运行不成功,请删除本行 -->
<servlet>
    <servlet-name>Admin</servlet-name>
      <servlet-class>org.logicalcobwebs.proxool.admin.servlet.AdminServlet</servlet-class>
   </servlet>
   <servlet-mapping>
    <servlet-name>Admin</servlet-name>
    <url-pattern>/admin</url-pattern>
   </servlet-mapping>  


以上配置完成后,第三步就可以创建一个连接池的类了


java 代码

package selfservice;        
      
import java.sql.Connection;        
import java.sql.DriverManager;        
import java.sql.ResultSet;        
import java.sql.SQLException;        
import java.sql.Statement;        
      
import org.logicalcobwebs.proxool.ProxoolException;        
import org.logicalcobwebs.proxool.ProxoolFacade;        
import org.logicalcobwebs.proxool.admin.SnapshotIF;        
      
      
public class PoolManager {        
            
    private static int activeCount = 0;        
            
            
    public PoolManager(){        
                
    }          
    /**      
     * 获取连接      
     * getConnection      
     * @param name      
     * @return      
     */      
    public Connection getConnection() {        
        try{        
            Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");//proxool驱动类        
            Connection conn = DriverManager.getConnection("proxool.DBPool");    
           //此处的DBPool是在proxool.xml中配置的连接池别名      
            showSnapshotInfo();        
                    
            return conn;        
        }catch(Exception ex){        
            ex.printStackTrace();        
        }        
        return null;        
    }        
    /**      
     * 此方法可以得到连接池的信息      
     * showSnapshotInfo      
     */      
    private void showSnapshotInfo(){        
        try{        
            SnapshotIF snapshot = ProxoolFacade.getSnapshot("DBPool", true);        
            int curActiveCount=snapshot.getActiveConnectionCount();//获得活动连接数        
            int availableCount=snapshot.getAvailableConnectionCount();//获得可得到的连接数        
            int maxCount=snapshot.getMaximumConnectionCount() ;//获得总连接数        
            if(curActiveCount!=activeCount)//当活动连接数变化时输出的信息        
            {        
             System.out.println("活动连接数:"+curActiveCount+"(active)  可得到的连接数:"+availableCount+"(available)  总连接数:"+maxCount+"(max)");                    
             activeCount=curActiveCount;        
            }        
        }catch(ProxoolException e){        
            e.printStackTrace();        
        }        
    }        
    /**      
     * 获取连接      
     * getConnection      
     * @param name      
     * @return      
     */      
    public Connection getConnection(String name){        
        return getConnection();        
    }        
    /**      
     * 释放连接      
     * freeConnection      
     * @param conn      
     */      
    public void freeConnection(Connection conn){        
        if(conn!=null){        
            try {        
                conn.close();        
            } catch (SQLException e) {                      
                e.printStackTrace();        
            }        
        }        
    }        
    /**      
     * 释放连接      
     * freeConnection      
     * @param name      
     * @param con      
     */      
    public void freeConnection (String name,Connection con){        
        freeConnection(con);        
    }        
            
    public void getQuery() {                
        try {        
            Connection conn = getConnection();        
            if(conn != null){        
                Statement statement = conn.createStatement();        
                ResultSet rs = statement.executeQuery("select * from tblgxinterface");        
                int c = rs.getMetaData().getColumnCount();        
                while(rs.next()){                          
                    System.out.println();        
                    for(int i=1;i<=c;i++){        
                        System.out.print(rs.getObject(i));        
                    }        
                }        
                rs.close();        
            }        
            freeConnection(conn);        
        } catch (SQLException e) {                  
            e.printStackTrace();        
        }        
      
    }        
      
}      


就这样我们完成了一个连接池的功能。proxool的连接池我用loadrunner进行大并发的测试,性能还是很好的。

Hibernate中proxool连接池的方式:

首先步骤跟JDBC的连接池一样,也是新建一个proxool.xml配置文件,再在web.xml中配置,具体参考上面。
第二步在hibernate的配置文件hibernate.cfg.xml中配置proxool连接设置:


hibernate.cfg.xml代码
xml 代码

<?xmlversion='1.0'encoding='UTF-8'?>

<!DOCTYPEhibernate-configurationPUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<property name="hibernate.connection.provider_class">org.hibernate.connection.ProxoolConnectionProvider</property>

<property name="hibernate.proxool.pool_alias">DBPool</property>

<property name="hibernate.proxool.xml">Proxool.xml</property>

<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<mappin gresource="hibernate.cfg.xml"/>

这里放Hibernate的映射文件

</session-factory>

  

Spring中proxool连接池的方式:

首先布骤与JDBC的连接池一样,先建一个proxool.xml配置文件,再在web.xml中配置,具体参考上面的。
第二步在spring配置文件applicationContext.xml中配置proxool连接设置

applicationContext.xml代码
xml 代码

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" singleton="true">  
        <property name="driverClassName" value="org.logicalcobwebs.proxool.ProxoolDriver"/>  
        <property name="url" value="proxool.StatDBPool"/>  
    </bean>  
    <bean id="transactionManager"    
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource">  
            <ref local="dataSource" />  
        </property>  
    </bean>    


这样spring就能得到一个dataSource的数据源。


proxool还有很多功能,我这只是简单的应用。具体请察看proxool用户指南。
posted @ 2010-05-25 10:21 redcoatjk 阅读(272) | 评论 (0)编辑 收藏
 
概述
J2SE(TM) 5.0引入了很多激进的语言元素变化,这些变化或多或少减轻了我们开发人员的一些编码负担,其中的大部分也必然会被应用到即将发布的J2EE(TM) 5.0中。主要的新特性包括:

  · 泛型

  · 增强的for循环

  · 自动装箱和自动拆箱

  · 类型安全的枚举

  · 可变长度参数

  · 静态引入

  · 元数据(注解)

  · C风格的格式化输出

  这当中,泛型、枚举和注解可能会占用较大的篇幅,而其余的因为用法直截了当,抑或相对简单,我就稍作介绍,剩下的留给读者去思考、去探索了。

  1.4. 泛型

  泛型这个题目相当大,大到完全可以就这个话题写一本书。有关Java是否需要泛型和如何实现泛型的讨论也早就在Java社群广为流传。终于,我们在J2SE(TM) 5.0中看到了它。也许目前Java对泛型的支持还算不上足够理想,但这一特性的添加也经足以让我们欣喜一阵了。

  在接下来的介绍中,我们会了解到:Java的泛型虽然跟C++的泛型看上去十分相似,但其实有着相当大的区别,有些细节的东西也相当复杂(至少很多地方会跟我们的直觉背道而驰)。可以这样说,泛型的引入在很大程度上增加了Java语言的复杂度,对初学者尤其是个挑战。下面我们将一点一点往里挖。

  首先我们来看一个简单的使用泛型类的例子:

    ArrayList<Integer> aList = new ArrayList<Integer>();
    aList.add(new Integer(1));
    // ...
    Integer myInteger = aList.get(0);

  我们可以看到,在这个简单的例子中,我们在定义aList的时候指明了它是一个直接受Integer类型的ArrayList,当我们调用aList.get(0)时,我们已经不再需要先显式的将结果转换成Integer,然后再赋值给myInteger了。而这一步在早先的Java版本中是必须的。也许你在想,在使用Collection时节约一些类型转换就是Java泛型的全部吗?远不止。单就这个例子而言,泛型至少还有一个更大的好处,那就是使用了泛型的容器类变得更加健壮:早先,Collection接口的get()和Iterator接口的next()方法都只能返回Object类型的结果,我们可以把这个结果强制转换成任何Object的子类,而不会有任何编译期的错误,但这显然很可能带来严重的运行期错误,因为在代码中确定从某个Collection中取出的是什么类型的对象完全是调用者自己说了算,而调用者也许并不清楚放进Collection的对象具体是什么类的;就算知道放进去的对象“应该”是什么类,也不能保证放到Collection的对象就一定是那个类的实例。现在有了泛型,只要我们定义的时候指明该Collection接受哪种类型的对象,编译器可以帮我们避免类似的问题溜到产品中。我们在实际工作中其实已经看到了太多的ClassCastException,不是吗?

  泛型的使用从这个例子看也是相当易懂。我们在定义ArrayList时,通过类名后面的<>括号中的值指定这个ArrayList接受的对象类型。在编译的时候,这个ArrayList会被处理成只接受该类或其子类的对象,于是任何试图将其他类型的对象添加进来的语句都会被编译器拒绝。

  那么泛型是怎样定义的呢?看看下面这一段示例代码:(其中用E代替在实际中将会使用的类名,当然你也可以使用别的名称,习惯上在这里使用大写的E,表示Collection的元素。)

    public class TestGenerics<E> {
    Collection<E> col;
    public void doSth(E elem) {
    col.add(elem);
    // ...
    }
   
       在泛型的使用中,有一个很容易有的误解,那就是既然Integer是从Object派生出来的,那么ArrayList<Integer>当然就是ArrayList<Object>的子类。真的是这样吗?我们仔细想一想就会发现这样做可能会带来的问题:如果我们可以把ArrayList<Integer>向上转型为ArrayList<Object>,那么在往这个转了型以后的ArrayList中添加对象的时候,我们岂不是可以添加任何类型的对象(因为Object是所有对象的公共父类)?这显然让我们的ArrayList<Integer>失去了原本的目的。于是Java编译器禁止我们这样做。那既然是这样,ArrayList<Integer>以及ArrayList<String>、ArrayList<Double>等等有没有公共的父类呢?有,那就是ArrayList<?>。?在这里叫做通配符。我们为了缩小通配符所指代的范围,通常也需要这样写:ArrayList<? extends SomeClass>,这样写的含义是定义这样一个类ArrayList,比方说SomeClass有SomeExtendedClass1和SomeExtendedClass2这两个子类,那么ArrayList<? extends SomeClass>就是如下几个类的父类:ArrayList<SomeClass>、ArrayList<SomeExtendedClass1>和ArrayList<SomeExtendedClass2>。 

       接下来我们更进一步:既然ArrayList<? extends SomeClass>是一个通配的公用父类,那么我们可不可以往声明为ArrayList<? extends SomeClass>的ArrayList实例中添加一个SomeExtendedClass1的对象呢?答案是不能。甚至你不能添加任何对象。为什么?因为ArrayList<? extends SomeClass>实际上代表了所有ArrayList<SomeClass>、ArrayList<SomeExtendedClass1>和ArrayList<SomeExtendedClass2>三种ArrayList,甚至包括未知的接受SomeClass其他子类对象的ArrayList。我们拿到一个定义为ArrayList<? extends SomeClass>的ArrayList的时候,我们并不能确定这个ArrayList具体是使用哪个类作为参数定义的,因此编译器也无法让这段代码编译通过。举例来讲,如果我们想往这个ArrayList中放一个SomeExtendedClass2的对象,我们如何保证它实际上不是其他的如ArrayList<SomeExtendedClass1>,而就是这个ArrayList<SomeExtendedClass2>呢?(还记得吗?ArrayList<Integer>并非ArrayList<Object>的子类。)怎么办?我们需要使用泛型方法。泛型方法的定义类似下面的例子:
    public static <T extends SomeClass> void add (Collection<T> c, T elem) {
    c.add(elem);
    }

  其中T代表了我们这个方法期待的那个最终的具体的类,相关的声明必须放在方法签名中紧靠返回类型的位置之前。在本例中,它可以是SomeClass或者SomeClass的任何子类,其说明放在void关键字之前(只能放在这里)。这样我们就可以让编译器确信当我们试图添加一个元素到泛型的ArrayList实例中时,可以保证类型安全。

  Java泛型的最大特点在于它是在语言级别实现的,区别于C# 2.0中的CLR级别。这样的做法使得JRE可以不必做大的调整,缺点是无法支持一些运行时的类型甄别。一旦编译,它就被写死了,能提供的动态能力相当弱。

  个人认为泛型是这次J2SE(TM) 5.0中引入的最重要的语言元素,给Java语言带来的影响也是最大。举个例子来讲,我们可以看到,几乎所有的Collections API都被更新成支持泛型的版本。这样做带来的好处是显而易见的,那就是减少代码重复(不需要提供多个版本的某一个类或者接口以支持不同类的对象)以及增强代码的健壮性(编译期的类型安全检查)。不过如何才能真正利用好这个特性,尤其是如何实现自己的泛型接口或类供他人使用,就并非那么显而易见了。让我们一起在使用中慢慢积累。

  1.5. 增强的for循环

  你是否已经厌倦了每次写for循环时都要写上那些机械的代码,尤其当你需要遍历数组或者Collection,如:(假设在Collection中储存的对象是String类型的)

    public void showAll (Collection c) {
    for (Iterator iter = c.iterator(); iter.hasNext(); ) {
    System.out.println((String) iter.next());
    }
    }

    public void showAll (String[] sa) {
    for (int i = 0; i < sa.length; i++) {
    System.out.println(sa[i]);
    }
    }

  这样的代码不仅显得臃肿,而且容易出错,我想我们大家在刚开始接触编程时,尤其是C/C++和Java,可能多少都犯过以下类似错误的一种或几种:把for语句的三个表达式顺序弄错;第二个表达式逻辑判断不正确(漏掉一些、多出一些、甚至死循环);忘记移动游标;在循环体内不小心改变了游标的位置等等。为什么不能让编译器帮我们处理这些细节呢?在5.0中,我们可以这样写:

    public void showAll (Collection c) {
    for (Object obj : c) {
    System.out.println((String) obj);
    }
    }

    public void showAll (String[] sa) {
    for (String str : sa) {
    System.out.println(str);
    }
    }

  这样的代码显得更加清晰和简洁,不是吗?具体的语法很简单:使用":"分隔开,前面的部分写明从数组或Collection中将要取出的类型,以及使用的临时变量的名字,后面的部分写上数组或者Collection的引用。加上泛型,我们甚至可以把第一个方法变得更加漂亮:

    public void showAll (Collection<String> cs) {
    for (String str : cs) {
    System.out.println(str);
    }
    }

  有没有发现:当你需要将Collection替换成String[],你所需要做的仅仅是简单的把参数类型"Collection"替换成"String[]",反过来也是一样,你不完全需要改其他的东西。这在J2SE(TM) 5.0之前是无法想象的。

  对于这个看上去相当方便的新语言元素,当你需要在循环体中访问游标的时候,会显得很别扭:比方说,当我们处理一个链表,需要更新其中某一个元素,或者删除某个元素等等。这个时候,你无法在循环体内获得你需要的游标信息,于是需要回退到原先的做法。不过,有了泛型和增强的for循环,我们在大多数情况下已经不用去操心那些烦人的for循环的表达式和嵌套了。毕竟,我们大部分时间都不会需要去了解游标的具体位置,我们只需要遍历数组或Collection,对吧?

  1.6. 自动装箱/自动拆箱

  所谓装箱,就是把值类型用它们相对应的引用类型包起来,使它们可以具有对象的特质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double,等等。所谓拆箱,就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为值类型的数据。

  在J2SE(TM) 5.0发布之前,我们只能手工的处理装箱和拆箱。也许你会问,为什么需要装箱和拆箱?比方说当我们试图将一个值类型的数据添加到一个Collection中时,就需要先把它装箱,因为Collection的add()方法只接受对象;而当我们需要在稍后将这条数据取出来,而又希望使用它对应的值类型进行操作时,我们又需要将它拆箱成值类型的版本。现在,编译器可以帮我们自动地完成这些必要的步骤。下面的代码我提供两个版本的装箱和拆箱,一个版本使用手工的方式,另一个版本则把这些显而易见的代码交给编译器去完成:

    public static void manualBoxingUnboxing(int i) {
    ArrayList<Integer> aList = new ArrayList<Integer>();
    aList.add(0, new Integer(i));
    int a = aList.get(0).intValue();
    System.out.println("The value of i is " + a);
    }

    public static void autoBoxingUnboxing(int i) {
    ArrayList<Integer> aList = new ArrayList<Integer>();
    aList.add(0, i);
    int a = aList.get(0);
    System.out.println("The value of i is " + a);
    }

  看到了吧,在J2SE(TM) 5.0中,我们不再需要显式的去将一个值类型的数据转换成相应的对象,从而把它作为对象传给其他方法,也不必手工的将那个代表一个数值的对象拆箱为相应的值类型数据,只要你提供的信息足够让编译器确信这些装箱/拆箱后的类型在使用时是合法的:比方讲,如果在上面的代码中,如果我们使用的不是ArrayList而是ArrayList或者其他不兼容的版本如ArrayList,会有编译错误。

  当然,你需要足够重视的是:一方面,对于值类型和引用类型,在资源的占用上有相当大的区别;另一方面,装箱和拆箱会带来额外的开销。在使用这一方便特性的同时,请不要忘记了背后隐藏的这些也许会影响性能的因素。

  1.7. 类型安全的枚举

  在介绍J2SE(TM) 5.0中引入的类型安全枚举的用法之前,我想先简单介绍一下这一话题的背景。

  我们知道,在C中,我们可以定义枚举类型来使用别名代替一个集合中的不同元素,通常是用于描述那些可以归为一类,而又具备有限数量的类别或者概念,如月份、颜色、扑克牌、太阳系的行星、五大洲、四大洋、季节、学科、四则运算符,等等。它们通常看上去是这个样子:

  typedef enum {SPRING, SUMMER, AUTUMN, WINTER} season;

  实质上,这些别名被处理成int常量,比如0代表SPRING,1代表SUMMER,以此类推。因为这些别名最终就是int,于是你可以对它们进行四则运算,这就造成了语意上的不明确。

  Java一开始并没有考虑引入枚举的概念,也许是出于保持Java语言简洁的考虑,但是使用Java的广大开发者对于枚举的需求并没有因为Java本身没有提供而消失,于是出现了一些常见的适用于Java的枚举设计模式,如int enum和typesafe enum,还有不少开源的枚举API和不开源的内部实现。

  我大致说一下int enum模式和typesafe enum模式。所谓int enum模式就是模仿C中对enum的实现,如:

    public class Season {
    public static final int SPRING = 0;
    public static final int SUMMER = 1;
    public static final int AUTUMN = 2;
    public static final int WINTER = 3;
    }
    这种模式跟C中的枚举没有太多本质上的区别,C枚举的局限它基本上也有。而typesafe enum模式则要显得健壮得多:
    public class Season {
    private final String name;
    private Season(String name) {
    this.name = name;
    }
    public String toString() {
    return name;
    }
    public static final Season SPRING = new Season("spring");
    public static final Season SUMMER = new Season("summer");
    public static final Season AUTUMN = new Season("autumn");
    public static final Season WINTER = new Season("winter");
    }

  后一种实现首先通过私有的构造方法阻止了对该类的继承和显式实例化,因而我们只可能取得定义好的四种Season类别,并且提供了方便的toString()方法获取有意义的说明,而且由于这是一个完全意义上的类,所以我们可以很方便的加入自己的方法和逻辑来自定义我们的枚举类。

  最终,Java决定拥抱枚举,在J2SE(TM) 5.0中,我们看到了这一变化,它所采用的设计思路基本上就是上面提到的typesafe enum模式。它的语法很简单,用一个实际的例子来说,要定义一个枚举,我们可以这样写:

  public enum Language {CHINESE, ENGLISH, FRENCH, HUNGARIAN}

  接下来我们就可以通过Language.ENGLISH来使用了。呃…这个例子是不是有点太小儿科了,我们来看一个复杂点的例子。使用Java的类型安全枚举,我们可以为所有枚举元素定义公用的接口,然后具体到每个元素本身,可以针对这些接口实现一些特定的行为。这对于那些可以归为一类,又希望能通过统一的接口访问的不同操作,将会相当方便。通常,为了实现类似的功能,我们需要自己来维护一套继承关系或者类似的枚举模式。这里借用Java官方网站上的一个例子:

    public enum Operation {
    PLUS { double eval(double x, double y) { return x + y; } },
    MINUS { double eval(double x, double y) { return x - y; } },
    TIMES { double eval(double x, double y) { return x * y; } },
    DIVIDE { double eval(double x, double y) { return x / y; } };

    // Do arithmetic op represented by this constant
    abstract double eval(double x, double y);
    }
    在这个枚举中,我们定义了四个元素,分别对应加减乘除四则运算,对于每一种运算,我们都可以调用eval()方法,而具体的方法实现各异。我们可以通过下面的代码来试验上面这个枚举类:
    public static void main(String args[]) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    for (Operation op : Operation.values()) {
    System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y));
    }
    }

  怎么样,使用枚举,我们是不是能够很方便的实现一些有趣的功能?其实说穿了,Java的类型安全枚举就是包含了有限数量的已生成好的自身实例的一种类,这些现成的实例可以通过类的静态字段来获取。

  1.8. 可变长度参数

  顾名思义,可变长度参数就是指在方法的参数体中,只要定义恰当,我们可以使用任意数量的参数,类似于使用数组。在J2SE(TM) 5.0中,一个新的语法被引入,就是在参数类型名称后面加上"...",表示该方法可以接受多个该类型的参数。需要说明的是可变长度参数必须放在参数列表的最后,且一个方法只能包含一个这样的参数。在方法体内部,这样的参数被当作数组处理,看上去代码应该类似这个样子:

    public String testVararg(String... args) {
    StringBuilder sb = new StringBuilder();
    for (String str : args) {
    sb.append(str);
    }
    return sb.toString();
    }

  这样的方法签名跟你写成testVararg(String[] args)的区别在于:在调用时,你不再需要传入一个包装好的String数组,你只需要简单的写一连串String参数,以逗号隔开即可,就如同这个方法正好有一个重载的版本是接受那么多个String参数一样。

  1.9. 静态引入

  所谓静态引入就是指除了引入类之外,我们现在又多了一种选择:引入某个类的静态字段。如:

  import static java.lang.Math.PI;

  或者

  import static java.lang.Math.*;

  这样我们在接下来的代码中,当我们需要使用某个被引入的静态字段时,就不用再写上前面的类名了。当然,出现名字冲突时,跟原来的类引入一样,还是需要前缀以示区分。我个人认为这个新语言元素意义不大。当引入太多静态字段后,代码会变得难以阅读和维护。由于静态字段的名字通常不如类名那么具有描述性,我认为原先在静态字段前写上类名才是更好的选择。不过,毕竟每个人的喜好和需求不同,如果你觉得它对你有用,既然提供了,那么就用咯。

  1.10. 元数据(注解)

  注解是J2SE(TM) 5.0引入的重要语言元素,它所对应的JSR是JSR 175,我们先来看看JSR 175的文档对注解的说明:

  注解不会直接影响程序的语义,而开发和部署工具则可以读取这些注解信息,并作相应处理,如生成额外的Java源代码、XML文档、或者其他将与包含注解的程序一起使用的物件。

  在之前的J2SE版本中,我们已经使用到了一部分早期的注解元素,如@deprecated等。这些元素通常被用于产生HTML的Javadoc。在J2SE(TM) 5.0中,注解被正式引入,且推到了Java历史上前所未有的高度。

  现在,注解不仅仅被用来产生Javadoc,更重要的,注解使得代码的编译期检查更加有效和方便,同时也增强了代码的描述能力。有一些注解是随着J2SE(TM) 5.0一起发布的,我们可以直接使用。除此之外,我们也可以很方便的实现自定义的注解。在此基础上,很多以前我们只能靠反射机制来完成的功能也变得更加容易实现。

  我们来看现成的有哪些有用的注解:

  首先是@Override,这个注解被使用在方法上,表明这个方法是从其父类继承下来的,这样的写法可以很方便的避免我们在重写继承下来的方法时,不至于不小心写错了方法签名,且悄悄的溜过了编译器,造成隐蔽性相当高的bug。

  其次是@Deprecated,表明该项(类、字段、方法)不再被推荐使用。

  还有一个@SuppressWarnings,表明该项(类、字段、方法)所涵盖的范围不需要显示所有的警告信息。这个注解需要提供参数,如unchecked等等。

  下面我通过一个例子向大家说明这些现成的注解的用法:

    public class Main {
    @Deprecated
    public String str;
    public static void main(String[] args) {
    new SubMain().doSomething();
    }
    public void doSomething() {
    System.out.println("Done.");
    }
    }

    class SubMain extends Main {
    @Override
    @SuppressWarnings("unchecked", "warning")
    public void doSomething() {
    java.util.ArrayList aList = new java.util.ArrayList();
    aList.add(new Integer(0));
    System.out.println("Done by SubMain.");
    }
    }

  当然,我们也完全可以写自己的注解。注解定义的语法是@interface关键字。J2SE(TM) 5.0支持三种形式的注解:不带参数的标记注解、带一个参数的注解和带多个参数的完整注解。下面分别举例说明:

    标记注解,类似@Deprecated,如:
    @interface SomeEmptyAnnotation {}
    单个参数的注解,如:
    @interface MySingleElementAnnotation {
    String value();
    }
    以及多个参数的注解,如:
    @interface MyAnnotationForMethods {
    int index();
    String info();
    String developer() default "Sean GAO";
    }

  我们可以看到,注解的定义跟interface的定义相当类似,我们还可以指定默认值。对于这些注解,我们也可以为其添加注解,所谓“注解的注解”。比方讲,我们通常会使用@Target指定注解的作用对象,以及用@Retention指定注解信息写入的级别,如源代码、类文件等等。举个例子:

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface SignedMethod {
    }
    在使用时,我们需要在注解名称前面写上@,然后()中指定参数值,如:
    @MyAnnotationForMethods (
    index = 1,
    info = "This is a method to test MyAnnotation.",
    developer = "Somebody else"
    )
    public void testMethod1() {
    // ...
    }

  注解的最大作用在于它在源代码的基础上增加了有用的信息,使得源代码的描述性更强。这些信息可以被代码之外的工具识别,从而可以很方便的增加外部功能,以及减少不必要的相关代码/文件的维护。这里我想简单提一个超出J2SE(TM) 5.0范畴的话题:在未来的EJB 3.0规范中会有相当多的对注解的应用,让我们预览一下将来的无状态会话bean用注解来定义会是什么样子:

    @Stateless public class BookShelfManagerBean {
    public void addBook(Book aBook) {
    // business logic goes here...
    }
    public Collection getAllBooks() {
    // business logic goes here...
    }
    // ...
    }

  我们甚至不用写任何接口和部署描述符,这些工作将完全由外部工具通过读取注解加上反射来完成,这不是很好吗?

  1.11. C风格格式化输出

  Java总算也有类似C的printf()风格的方法了,方法名同样叫作printf(),这一特性依赖于前边提到的可变长度参数。举个例子来说,我们现在可以写:

  System.out.printf("%s has a value of %d.%n", someString, a);

  怎么样,看上去还不错吧?需要注意的是Java为了支持多平台,新增了%n标示符,作为对\n的补充。有关Java格式化输出的具体语法,请参考java.util.Formatter的API文档。

  1.12. 结语

  在这一篇介绍性的文章中,我们一起领略了J2SE 5.0带来的新的语言元素,不知道大家是否也跟笔者一样,感受到了这些新特性在提高我们的开发效率上所作的巨大努力。其实不只是语言元素,J2SE(TM) 5.0的发布在其他很多方面都作了不小的改进,包括虚拟机、新的API类库等等,性能和功能上都有大幅提升。

  对于主要靠J2EE吃饭的朋友来讲,也许真正意义上要在工作中充分利用这些新的元素,恐怕要等主流的J2EE服务器都支持J2EE(TM) 5.0的那一天了,对此我充满期待。

posted @ 2010-05-16 15:35 redcoatjk 阅读(145) | 评论 (0)编辑 收藏
 

java截取字符串,截串,substring和split,分割字母和数字,正则缝隙

关键字: java截取字符串 截串 substring

需求,把"01:大汽车",分成01和大汽车

有两种做法:一是substring

Java代码
  1. package test;  
  2.   
  3. public class substringTest  
  4. {  
  5.  public static void main(String args[])   
  6.  {   
  7.   String N = "01:大汽车";   
  8.   String L="";   
  9.   String R="";   
  10.   int k= N.length();   
  11.   for (int i = 0; i < N.length(); i++)   
  12.   {   
  13.    if (N.substring(i, i + 1).equals("|"))   
  14.    {     
  15.     L=N.substring(0,i).trim();   
  16.     R=N.substring(i+1,k).trim();   
  17.    }   
  18.    else   
  19.    {   
  20.                 
  21.    }   
  22.    System.out.println(L);   
  23.    System.out.println(R);   
  24.   }  
  25.  }  
  26. }   

 另外一种方法是CSDN上一位叫老六的人给我写的

package Test

Java代码
  1. public class splitTest  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         String s = new String("01:大汽车");   
  6.         String a[] = s.split(":");  
  7.       
  8.         System.out.println(a[0]);  
  9.         System.out.println(a[1]);  
  10.     }  
  11. }  

 split分割字母和数字,简单正则缝隙

Java代码
  1. public class Test01 {  
  2.     public static void main(String[] args) {  
  3.         String str = "one123";  
  4.         String regex = "(?<=one)(?=123)";  
  5.         String[] strs = str.split(regex);  
  6.         for(int i = 0; i < strs.length; i++) {  
  7.             System.out.printf("strs[%d] = %s%n", i, strs[i]);  
  8.         }  
  9.     }  
  10. }  

 

 

 

substring讲解:

s=s.substring(int begin);截取掉s从首字母起长度为begin的字符串,将剩余字符串赋值给s;

s=s.substring(int begin,int end);截取s中从begin开始至end结束时的字符串,并将其赋值给s;

split讲解:

java.lang.string.split
split 方法
将一个字符串分割为子字符串,然后将结果作为字符串数组返回。
stringObj.split([separator,[limit]])
参数
stringObj
必选项。要被分解的 String 对象或文字。该对象不会被 split 方法修改。
separator
可选项。字符串或 正则表达式 对象,它标识了分隔字符串时使用的是一个还是多个字符。如果忽

略该选项,返回包含整个字符串的单一元素数组。
limit
可选项。该值用来限制返回数组中的元素个数。

说明
split 方法的结果是一个字符串数组,在 stingObj 中每个出现 separator 的位置都要进行分解

。separator 不作为任何数组元素的部分返回。

split 的实现直接调用的 matcher 类的 split 的方法。“ . ”在正则表达式中有特殊的含义,因此我们使用的时候必须进行转义。

Java代码
  1. public static void main(string[] args) {  
  2. string value = "192.168.128.33";  
  3. string[] names = value.split("\\.");  
  4. for (int i = 0; i < names.length; i++) {  
  5. system.out.println(names[i]);  
  6. }}  

 如果用竖线“|”分隔的话,将出现不可得到的结果,必须改为“\\|” 

posted @ 2010-03-19 17:34 redcoatjk 阅读(157781) | 评论 (2)编辑 收藏
 
摘自:http://www.blogjava.net/ghyghost/archive/2008/06/16/208309.html
标题           在Java中实现浮点数的精确计算         AYellow(原作)   修改          
  关键字           Java   浮点数   精确计算        
   
   
  问题的提出:   
  编译运行下面这个程序会看到什么?  
  public   class   Test{  
          public   static   void   main(String   args[]){  
                  System.out.println(0.05+0.01);  
                  System.out.println(1.0-0.42);  
                  System.out.println(4.015*100);  
                  System.out.println(123.3/100);  
          }  
  };  
   
  你没有看错!结果确实是  
  0.060000000000000005  
  0.5800000000000001  
  401.49999999999994  
  1.2329999999999999  
   
  Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。  
  这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。  
  在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。  
   
     
   
  四舍五入  
  我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位):  
  public   double   round(double   value){  
          return   Math.round(value*100)/100.0;  
  }  
   
  非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的  
  4.015*100=401.49999999999994  
  因此如果我们要做到精确的四舍五入,不能利用简单类型做任何运算  
  java.text.DecimalFormat也不能解决这个问题:  
  System.out.println(new   java.text.DecimalFormat("0.00").format(4.025));  
  输出是4.02  
   
     
   
  BigDecimal  
  在《Effective   Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal。BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个, 它们是:  
  BigDecimal(double   val)    
                      Translates   a   double   into   a   BigDecimal.    
  BigDecimal(String   val)    
                      Translates   the   String   repre   sentation   of   a   BigDecimal   into   a   BigDecimal.  
   
  上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。我们可能想都不想就用上了,会有什么问题呢?等到出了问题的时候,才发现上面哪个够造方法的详细说明中有这么一段:  
  Note:   the   results   of   this   constructor   can   be   somewhat   unpredictable.   One   might   assume   that   new   BigDecimal(.1)   is   exactly   equal   to   .1,   but   it   is   actually   equal   to   .1000000000000000055511151231257827021181583404541015625.   This   is   so   because   .1   cannot   be   represented   exactly   as   a   double   (or,   for   that   matter,   as   a   binary   fraction   of   any   finite   length).   Thus,   the   long   value   that   is   being   passed   in   to   the   constructor   is   not   exactly   equal   to   .1,   appearances   nonwithstanding.    
  The   (String)   constructor,   on   the   other   hand,   is   perfectly   predictable:   new   BigDecimal(".1")   is   exactly   equal   to   .1,   as   one   would   expect.   Therefore,   it   is   generally   recommended   that   the   (String)   constructor   be   used   in   preference   to   this   one.  
   
  原来我们如果需要精确计算,非要用String来够造BigDecimal不可!在《Effective   Java》一书中的例子是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。  
     
   
  解决方案  
  现在我们已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来够造。  
  但是想像一下吧,如果我们要做一个加法运算,需要先将两个浮点数转为String,然后够造成BigDecimal,在其中一个上调用add方法,传入另 一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。你能够忍受这么烦琐的过程吗?下面我们提供一个工具类Arith来简化操作。它 提供以下静态方法,包括加减乘除和四舍五入:  
  public   static   double   add(double   v1,double   v2)  
  public   static   double   sub(double   v1,double   v2)  
  public   static   double   mul(double   v1,double   v2)  
  public   static   double   div(double   v1,double   v2)  
  public   static   double   div(double   v1,double   v2,int   scale)  
  public   static   double   round(double   v,int   scale)  
   
   
   
  附录  
   
   
  源文件Arith.java:  
   
  import   java.math.BigDecimal;  
  /**  
    *   由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精  
    *   确的浮点数运算,包括加减乘除和四舍五入。  
    */  
   
  public   class   Arith{  
   
          //默认除法运算精度  
          private   static   final   int   DEF_DIV_SCALE   =   10;  
   
   
          //这个类不能实例化  
          private   Arith(){  
          }  
   
     
          /**  
            *   提供精确的加法运算。  
            *   @param   v1   被加数  
            *   @param   v2   加数  
            *   @return   两个参数的和  
            */  
   
          public   static   double   add(double   v1,double   v2){  
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));  
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));  
                  return   b1.add(b2).doubleValue();  
          }  
   
          /**  
            *   提供精确的减法运算。  
            *   @param   v1   被减数  
            *   @param   v2   减数  
            *   @return   两个参数的差  
            */  
   
          public   static   double   sub(double   v1,double   v2){  
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));  
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));  
                  return   b1.subtract(b2).doubleValue();  
          }    
   
          /**  
            *   提供精确的乘法运算。  
            *   @param   v1   被乘数  
            *   @param   v2   乘数  
            *   @return   两个参数的积  
            */  
   
          public   static   double   mul(double   v1,double   v2){  
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));  
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));  
                  return   b1.multiply(b2).doubleValue();  
          }  
   
     
   
          /**  
            *   提供(相对)精确的除法运算,当发生除不尽的情况时,精确到  
            *   小数点以后10位,以后的数字四舍五入。  
            *   @param   v1   被除数  
            *   @param   v2   除数  
            *   @return   两个参数的商  
            */  
   
          public   static   double   div(double   v1,double   v2){  
                  return   div(v1,v2,DEF_DIV_SCALE);  
          }  
   
     
   
          /**  
            *   提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指  
            *   定精度,以后的数字四舍五入。  
            *   @param   v1   被除数  
            *   @param   v2   除数  
            *   @param   scale   表示表示需要精确到小数点以后几位。  
            *   @return   两个参数的商  
            */  
   
          public   static   double   div(double   v1,double   v2,int   scale){  
                  if(scale<0){  
                          throw   new   IllegalArgumentException(  
                                  "The   scale   must   be   a   positive   integer   or   zero");  
                  }  
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));  
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));  
                  return   b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();  
          }  
   
     
   
          /**  
            *   提供精确的小数位四舍五入处理。  
            *   @param   v   需要四舍五入的数字  
            *   @param   scale   小数点后保留几位  
            *   @return   四舍五入后的结果  
            */  
   
          public   static   double   round(double   v,int   scale){  
                  if(scale<0){  
                          throw   new   IllegalArgumentException(  
                                  "The   scale   must   be   a   positive   integer   or   zero");  
                  }  
                  BigDecimal   b   =   new   BigDecimal(Double.toString(v));  
                  BigDecimal   one   =   new   BigDecimal("1");  
                  return   b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();  
          }  
  };  


posted @ 2010-03-07 17:23 redcoatjk 阅读(447) | 评论 (0)编辑 收藏
CALENDER
<2010年3月>
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(3)

随笔分类(22)

随笔档案(76)

文章分类(12)

文章档案(17)

搜索

  •  

积分与排名

  • 积分 - 249249
  • 排名 - 227

最新评论

评论排行榜


Powered By: 博客园
模板提供沪江博客