在Cetia4 REST 后继的讨论中又列出了下面两个不错的 REST 框架:
XX Framework (see www.xxframework.org)
http://www.restlet.org/ 这是个老牌的 REST 框架,这个框架的设计目的不是完全面向Web Application的,学习曲线比较高。
restlet 和 cetia4 的最大区别是 restlet 不是基于 java servlet 的,而后者是。当然 restlet 也可以在 servlet 容器中运行
I look, touch and think
在Cetia4 REST 后继的讨论中又列出了下面两个不错的 REST 框架:
XX Framework (see www.xxframework.org)
http://www.restlet.org/ 这是个老牌的 REST 框架,这个框架的设计目的不是完全面向Web Application的,学习曲线比较高。
restlet 和 cetia4 的最大区别是 restlet 不是基于 java servlet 的,而后者是。当然 restlet 也可以在 servlet 容器中运行
conn sys/oracle@xmlindex as sysdba
grant plustrace to xdb;
set autot trace
set autot off
1.thin:
jdbc:oracle:thin:@xedo 其中xedo在tns中configuration
jdbc:oracle:thin:@//152.69.90.48:1521/orcl
2.oci:
jdbc:oracle:oci8:@xedo 其中xedo在tns中configuration
jdbc:oracle:oci8:@//152.69.90.48:1521/orcl
3.sqlplus
sqlplus xedo/xedo@xedo 其中xedo在tns中configuration
1.建立一个目录别名
create DIRECTORY 'tmpdir' AS '/tmp';
GRANT READ ON DIRECTORY bfile_dir1 TO scott;
2.
建立一个含有bfile字段的表
create table bfiletest(id number(3), fname bfile);
建立一个含有BLOB字段的表
create table blobtest(id number(3),ablob blob);
3.插入数据
INSERT INTO bfiletest VALUES (1, BFILENAME ('XMLDIR', 'tmptest'));
4.bfile转blob
CREATE OR REPLACE PROCEDURE loadLOBFromBFILE_proc(
TID IN NUMBER,rfilename in varchar2,upmessage out varchar2)
AS
Dest_loc BLOB;
Src_loc BFILE;
BEGIN
INSERT INTO BLOBTEST(ID,ABLOB) VALUES(TID,EMPTY_BLOB()) RETURN ABLOB INTO DEST_LOC;
Src_loc := BFILENAME('XMLDIR',rfilename);
IF (DBMS_LOB.FILEEXISTS(Src_loc) != 0)
THEN
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(Dest_loc, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc,DBMS_LOB.GETLENGTH(Src_loc));
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.CLOSE(Src_loc);
COMMIT;
upmessage := '0';
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
upmessage := '操作失败';
END;
5.bfile转clob
create or replace function getDocument(filename varchar2)
return CLOB deterministic
is
file bfile := bfilename('XMLDIR',filename);
charContent CLOB := ' ';
targetFile bfile;
warning number;
begin
targetFile := file;
DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
DBMS_LOB.loadfromFile(charContent,targetFile, DBMS_LOB.getLength(targetFile),1,1);
DBMS_LOB.fileclose(targetFile);
return charContent;
end;
/
6.Select到LOB字段的定位器
对某一LOB字段进行选择,则返回的不是LOB的值,而是该LOB字段的定位器
DELCARE
AUDIO_INFO BLOB;
BENGIN
SELECT ablob INTO AUDIO_INFO FROM blobtest WHERE id=100;
END;
1.运行远程控制
如果是本机安装,运行xhost +x
如果是远程控制,运行export DISPLAY=152.69.90.92:0.0
测试运行xeyes,xclock,gnome-session,应该出现图形界面
1.4.创建用户和组
# su root
# /usr/sbin/groupadd oinstall
# /usr/sbin/groupadd dba
# /usr/sbin/useradd -g oinstall -G dba oracle
# passwd oracle
如果存在oracle用户: /usr/sbin/userdel oracle
可以通过查看或直接修改/etc/passwd和/etc/group来看是否生效
su oracle
unzip 10201_database_linux32.zip
使用oracle用户upload和unzip有好处,因为oracle将拥有10201_database_linux32.zip的访问限制
如果没有权限:
chown -R oracle:dba database (optional)
1.7 安装路径设置
推荐安装在/home/oracle下面,因为oracle对该目录有访问权限,无须其它操作,如果想换地方,例如:
创建安装目录 mkdir /opt/oracle
注意如果待安装目录dir对于oracle用户而言没有权限,可以修改目录权限,使用如下命令:chown -R oracle:dba /opt/oracle (optional)
这样oracle就拥有了/opt/oracle的访问权限
2.修改核心参数
su root
vi /etc/sysctl.conf
kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
fs.file-max = 65536
net.ipv4.ip_local_port_range = 1024 65000
net.core.rmem_default=262144
net.core.wmem_default=262144
net.core.rmem_max=262144
net.core.wmem_max=262144
命令操作来使我们所做的变更生效
/sbin/sysctl -p
vi /home/oracle/.bash_profile
添加如下内容,你的具体值应该不会和这个完全相同.
export ORACLE_BASE=/home/oracle
export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1
export ORACLE_SID=ORCL
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib
export LC_CTYPE=en_US.UTF-8
su - oracle
在安装文件目录下找到runInstaller文件
./runInstaller
之后就会出现安装向导,sid最好用小写,安装前把domain改成有意义的域名
用默认值下一步下一步就可以了
注意上面开始的检测提示,如果有不对的地方要退出进行修正,我在安装的时候就遇到glibc的版本不对,这时应该下载安装需要的版本进行升级,rpm命令详见rpm文章。
在Oracle数据库中,用户的权限分为两种(在这里我们不讨论dba或dbopr的权限,只考虑普通用户的权限),分别是System Privilege系统权限 和User Table Privilege用户数据表权限.
1.首先,创建用户,以下几条命令可以创建一个用户,前提是必须以DBA的身份登录(如果你不是DBA,不要看下去了):
create user DB_USER identified by DB_USER_PW '创建用户DB_USER,密码为DB_USER_PW
grant create session to DB_USER '给用户创建会话的权限
grant resource to DB_USER
2.当用户建立后,会自动在Oracle数据库系统中生成属于该用户的Scheme (可以理解为所有属于该用户的表,视图....等对象的集合).
该用户可以将对这些对象的访问权限赋予其它的系统用户.
3.该用户用sqlplus登录后,以下命令可以看到该用户的权限(该部分取自于CNOUG网站):
本用户读取其他用户对象的权限:
select * from user_tab_privs;
本用户所拥有的系统权限:
select * from user_sys_privs;
drop user username cascade;
create user username identified by my_password;
grant create any directory, drop any directory to username;
grant connect, resource to username;
grant dba to username;
grant all on emp to username; --把emp用户的权限都给username
ALTER USER scott ACCOUNT LOCK -- lock a user account
ALTER USER scott ACCOUNT UNLOCK; -- unlocks a locked users account
ALTER USER scott PASSWORD EXPIRE; -- Force user to choose a new password
ALTER USER <username> IDENTIFIED BY <new_password> --改密码
在Oracle9i和10g中,一个新的文件被引入spfile,spfile用于服务器端,管理初始化参数。
在9i以前,Oracle使用pfile存储初始化参数设置,这些参数在实例启动时被读取,任何修改需要重起实例才能生效;使用spfile你可以使用ALTER SYSTEM或者ALTER SESSION来动态修改那些可动态修改的参数,所有更改可以立即生效
1.pfile和spfile 的系统缺省目录
Unix: $ORACLE_HOME/dbs; NT: $ORACLE_HOME\database
Oralce在缺省目录中搜索参数文件,先找spfile,再找pfile
2.创建SPFILE(spfile${ORACLE_SID}.ora)
SQL> CREATE SPFILE='E:\ora9i\database\SPFILEEYGLE.ORA' FROM PFILE='E:\ora9i\admin\eygle\pfile\init.ora';
2.创建pfile(init${ORACLE_SID}.ora)
SQL> CREATE SPFILE='E:\ora9i\database\SPFILEEYGLE.ORA' FROM PFILE='E:\ora9i\admin\eygle\pfile\init.ora';
3.使用pfile启动数据库
SQL> startup pfile='E:\Oracle\admin\eyglen\pfile\init.ora';
4.使用spfile启动数据库
你不能以同样的方式指定spfile,但是可以创建一个包含spfile参数的pfile文件,指向spfile.
我们修改PFILE文件内容如下:
#Pfile link to SPFILE
SPFILE= 'E:\Oracle\Ora9iR2\database\SPFILEEYGLEN.ORA'
log_archive_start = false
4.查看当前使用的spfile
SQL> SHOW parameter spfile
结果value列返回空值,那么说明你在使用pfile
5.导出SPFILE文件后手动编辑
SQL> create pfile='c:\initeyglen.ora' from spfile;
SQL> SHUTDOWN immediate
手动编辑c:\initeyglen.ora
SQL>STARTUP pfile='e:\initeyglen.ora'
SQL> CREATE spfile FROM pfile='e:\initeyglen.ora';
SQL> STARTUP
6.在线修改系统参数
ALTER SYSTEM SET {parameter}=TRUE SCOPE=BOTH;
其中{parameter}可以是select name from v$parameter;的其中一个name值
SCOPE参数有三个可选值:MEMORY(只改变当前实例运行) 、SPFILE (只改变SPFILE的设置)、BOTH(改变实例及SPFILE)。
设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据的一致性和准确性
数据库锁方式有三种:共享锁,独占锁,共享更新锁
共享锁SHARE
特点:可在一个表上共存多个
效果: 所有用户只可查询
独占锁EXCLUSIVE
特点:在一个表上只能单独存在,在用户执行DML语句INSERT、UPDATE、DELETE时隐含获得
效果: 独占的用户可查可更新,其他用户只可查
共享更新锁(行锁)
特点:对一个表的一行或多行进行封锁
效果: 查询也可以更新被封锁的数据行,其它用户只能查询但不能更新被封锁的数据行.
释放锁: 执行COMMIT或ROLLBACK语句,退出数据库
ORACLE里锁有以下几种模式:
0:none
1:null 空
2:Row-S 行共享(RS):共享表锁
3:Row-X 行专用(RX):用于行的修改
4:Share 共享锁(S):阻止其他DML操作
5:S/Row-X 共享行专用(SRX):阻止其他事务操作
6:exclusive 专用(X):独立访问使用
数字越大锁级别越高, 影响的操作越多。
一般的查询语句如select ... from ... ;是小于2的锁, 有时会在v$locked_object出现。
select ... from ... for update; 是2的锁。
当对话使用for update子串打开一个游标时,
所有返回集中的数据行都将处于行级(Row-X)独占式锁定,
其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。
insert / update / delete ... ; 是3的锁。
没有commit之前插入同样的一条记录会没有反应,
因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。
创建索引的时候也会产生3,4级别的锁。
locked_mode为2,3,4不影响DML(insert,delete,update,select)操作,
但DDL(alter,drop等)操作会提示ora-00054错误。
有主外键约束时 update / delete ... ; 可能会产生4,5的锁。
DDL语句时是6的锁。
1.查看当前数据库里锁的情况
select s.username,s.sid,s.serial#,object_name from v$locked_object l,v$session s ,dba_objects o
where l.session_id=s.sid and o.object_id=l.object_id order by s.logon_time;
select /*+ RULE */ ls.osuser os_user_name, ls.username user_name,
decode(ls.type, 'RW', 'Row wait enqueue lock', 'TM', 'DML enqueue lock', 'TX',
'Transaction enqueue lock', 'UL', 'User supplied lock') lock_type,
o.object_name object, decode(ls.lmode, 1, null, 2, 'Row Share', 3,
'Row Exclusive', 4, 'Share', 5, 'Share Row Exclusive', 6, 'Exclusive', null)
lock_mode, o.owner, ls.sid, ls.serial# serial_num, ls.id1, ls.id2
from sys.dba_objects o, ( select s.osuser, s.username, l.type,
l.lmode, s.sid, s.serial#, l.id1, l.id2 from v$session s,
v$lock l where s.sid = l.sid ) ls where o.object_id = ls.id1 and o.owner
<> 'SYS' order by o.owner, o.object_name
select *from v$lock;
2.杀掉不正常的锁
alter system kill session 'sid,serial#';
select * from all_directories;
create or replace directory XMLDIR as '/home/oracle/rain'
drop directory XMLDIR
conn / as sysdba
grant read on directory XMLDIR to public;
grant write on directory XMLDIR to public;
--从OS文件系统中读入文件到CLOB
create or replace function getDocument(filename varchar2)
return CLOB deterministic
is
file bfile := bfilename('XMLDIR',filename);
charContent CLOB := ' ';
targetFile bfile;
warning number;
begin
targetFile := file;
DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
DBMS_LOB.loadfromFile(charContent,targetFile, DBMS_LOB.getLength(targetFile),1,1);
DBMS_LOB.fileclose(targetFile);
return charContent;
end;
/
create or replace function getFileContent(file bfile)
return CLOB deterministic
is
charContent CLOB := ' ';
targetFile bfile;
warning number;
begin
targetFile := file;
DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
DBMS_LOB.loadfromFile(charContent,targetFile,
DBMS_LOB.getLength(targetFile),1,1);
DBMS_LOB.fileclose(targetFile);
return charContent;
end;
/
drop user username cascade;
create user username identified by my_password;
grant create any directory, drop any directory to username;
grant connect, resource to username;
grant dba to username;
grant all on emp to username; --把emp用户的权限都给username
ALTER USER scott ACCOUNT LOCK -- lock a user account
ALTER USER scott ACCOUNT UNLOCK; -- unlocks a locked users account
ALTER USER scott PASSWORD EXPIRE; -- Force user to choose a new password
ALTER USER <username> IDENTIFIED BY <new_password> --改密码
1.VNC 客户端
下载一个vnc-3.3.7-x86_win32_viewer.exe
2.在Linux上启动VNC服务
/>vncserver
然后设置vnc登陆密码
3.使用客户端访问
RedHat AS中预装的是vsftpd
vsftpd 是一个基于GPL发布的类Unix类操作系统上运行的服务器的名字(是一种守护进程),它可以运行在诸如Linux、BSD、Solaris、HP-UX 以及IRIX上面。它支持很多其他传统的FTP服务器所不支持的特征。它具有如下特点:非常高的安全性、带宽限制、良好的扩展性、支持创建虚拟用户、支持IPv6、支持虚拟IP、高速、稳定。
启动vsftpd目前我知道一种方法:
在GNome中 开始->系统->服务设置->服务
在服务中选中vsftpd,点击启动便可以了
rpm -ivh xorg-version.rpm // 安装
rpm -ivh --replacepkgs xorg-version.rpm // 强行重新安装
rpm -ivh --nodeps xorg-version.rpm // 强行安装
rpm -q xorg // 查询
rpm -e xorg // 删除
rpm -Uvh xorg-version.rpm // 升级安装
zip all.zip *.jpg
这条命令是将所有.jpg的文件压缩成一个zip包
unzip all.zip
这条命令是将all.zip中的所有文件解压出来
tar -cf all.tar *.jpg
这条命令是将所有.jpg的文件打成一个名为all.tar的包。-c是表示产生新的包,-f指定包的文件名。
tar -rf all.tar *.gif
这条命令是将所有.gif的文件增加到all.tar的包里面去。-r是表示增加文件的意思。
tar -uf all.tar logo.gif
这条命令是更新原来tar包all.tar中logo.gif文件,-u是表示更新文件的意思。
tar -tf all.tar
这条命令是列出all.tar包中所有文件,-t是列出文件的意思
tar -xf all.tar
这条命令是解出all.tar包中所有文件,-t是解开的意思
tar调用gzip
# tar -czf all.tar.gz *.jpg
这条命令是将所有.jpg的文件打成一个tar包,并且将其用gzip压缩,生成一个gzip压缩过的包,包名为all.tar.gz
# tar -xzf all.tar.gz
这条命令是将上面产生的包解开。
tar调用bzip2
# tar -cjf all.tar.bz2 *.jpg
这条命令是将所有.jpg的文件打成一个tar包,并且将其用bzip2压缩,生成一个bzip2压缩过的包,包名为all.tar.bz2
# tar -xjf all.tar.bz2
这条命令是将上面产生的包解开。
tar调用compress
# tar -cZf all.tar.Z *.jpg
这条命令是将所有.jpg的文件打成一个tar包,并且将其用compress压缩,生成一个uncompress压缩过的包,包名为all.tar.Z
# tar -xZf all.tar.Z
这条命令是将上面产生的包解开
对于.tar结尾的文件
tar -xf all.tar
对于.gz结尾的文件
gzip -d all.gz
gunzip all.gz
对于.tgz或.tar.gz结尾的文件
tar -xzf all.tar.gz
tar -xzf all.tgz
对于.bz2结尾的文件
bzip2 -d all.bz2
bunzip2 all.bz2
对于tar.bz2结尾的文件
tar -xjf all.tar.bz2
对于.Z结尾的文件
uncompress all.Z
对于.tar.Z结尾的文件
tar -xZf all.tar.z
Rar文件
# rar a all *.jpg
这条命令是将所有.jpg的文件压缩成一个rar包,名为all.rar,该程序会将.rar 扩展名将自动附加到包名后。
# unrar e all.rar
这条命令是将all.rar中的所有文件解压出来
procedure:
1.enter /usr/src/linux
2.make mrproper
3.make dep
4.make clean
5.make bzImage
6.make modules
7.make module_install
8.make install
1.delete the line
dd
2.copy
Y
3.exit and save
:wq
4.exit and not save
:q!
5.import mode
i
6.find forward
/string
7.find backward
?stirng
8.copy line
yy
9.copy the position where the cursor is
p
10.replace
:s/old/new/g
基本命令
C-x C-c : 退出Emacs
C-x C-f : 打开一个文件,如果文件不存在,则创建一个文件
C-g : 取消未完成的命令
编辑
C-z (redefined): Undo;原来C-z是挂起Emacs(然后用fg命令调出);C-x u 是默认的命令; 移动一下光标,再C-z就可以redo
M-d : 删除光标后的词语
移动光标
C-v : 向前翻页
M-v : 向后翻页
M-r : 将光标移动到屏幕中间那行
C-a : 移到行首
M-a : 移到句首,从行首到句首之间可能有空格
C-e : 移到行尾
M-e : 移到句尾
M-{ : 向上移动一段
M-} : 向下移动一段
C-right : 向前移动一个单词
C-left : 向后移动一个单词
C-up : 向前移动一段
C-down : 向后移动一段
M-< : 移到整个文本开头
M-> : 移到整个文本末尾
C-u 数字 命令 : 执行多次(数字表示次数)该命令;“M-数字 命令” 也可以
M-x goto-line : 移动到某一行
C-l : 重绘屏幕,效果就是当前编辑行移动窗口中央
Buffer 相关
C-x k : 关闭当前buffer
C-x b : 切换到前一个编辑的buffer
C-x C-b : 列出当前所有buffer
C-x C-s : 保存当前buffer
C-x s : 保存所有未保存的buffer,会提示你是否需要保存
C-x C-w : 文件另存为
拷贝与粘贴
M-space (redefined): 设置mark; C-@ 是默认命令
C-w (redefined) : 剪切一块区域;如果没有设置mark,则是剪切一行
M-w (redefined) : 拷贝一块区域;如果没有设置mark, 则是拷贝一行
C-k : 从当前位置剪切到行尾
C-y : 粘贴
M-y : 用C-y拉回最近被除去的文本后,换成 M-y可以拉回以前被除去的文本。键入多次的M-y可以拉回更早以前被除去的文本。
C-x r k : 执行矩形区域的剪切
C-x r y : 执行矩形区域的粘贴
窗口操作
C-x 0 : 关闭当前窗口
C-x 1 : 将当前窗口最大化
C-x 2 : 垂直分割窗口
C-x 3 : 水平分割窗口
M-o (redefined) : 在窗口之间切换; C-x o 是默认命令
C-x 5 1/2/3/0 : 对frame类似的操作
C-x < : 窗口内容右卷
C-x > : 窗口内容左卷(这两个命令在垂直分割窗口后比较有用)
(C-u) C-x ^ : 加高当前窗口,如果有C-u,则每次加高4行
(C-u) C-x } : 加宽当前窗口
(C-u) C-x { : 压窄当前窗口
ESC C-v : 在其它窗口进行卷屏操作
搜索和替换
C-s : 向前搜索(增量式搜索);连续C-s,跳到下一个搜索到的目标
C-s RET : 普通搜索
C-r : 向前搜索
C-s RET C-w : 按单词查询
M-% : 查询替换,也就是替换前会询问一下
M-x replace-string : 普通替换
Tags
M-! etags .c .h : 创建TAGS文件
M-. : 跳到tag所在位置
M-x list-tags : 列出tags
Bookmark
C-x r m : 设置书签bookmark
C-x r b : 跳到bookmark处
帮助
C-h ? : 查看帮助信息
C-h f : 查看一个函数
C-h v : 查看一个变量
C-h k : 查看一个键绑定 (C-h c 也是查看键绑定,但是信息较简略)
C-h C-f : 查看一个函数的Info,非常有用
C-h i : 看Info
其它
C-M-\ : 对选中区域,按照某种格式(比如C程序)进行格式化
C-x h : 全部选中
M-! : 执行外部shell命令
M-x shell : 模拟shell的buffer
M-x term : 模拟terminal, C-c k 关闭terminal
C-x C-q : 修改buffer的只读属性
1、安装Xmanager或XWin32软件。
2、启动telnet登陆到待远程控制的主机
1)在用户的目录下找到文件.bash_profile或profile,用vi对其进行编辑。加入下列命令行:
DISPLAY=192.168.0.11:0.0;export DISPLAY
2)保存,退出。
3)如果只想临时在客户端用一下图形界面,无需进行第4、5步,直接在xterm界面的命令行中输入:
export DISPLAY=192.168.0.11:0.0
注:192.168.0.11是你本机IP
3、然后就可以在命令行中运行图形界面程序了,测试。
运行xeyes,应该出现图形界面。
4、redhat下运行gnome-session,启动图形用户界面
if you want to run jdev in the current directory
./jdev& : if the shell is closed, the jdev application will be closed followed by the shell being closed.
nohup ./jdev : if the shell is closed, the jdev application won't be closed followed by the shell being closed.
Samba: http://www.e2web.cn/temp/0005/20066142358339294.htm
mount -t smbfs -o username=abc,password=abc //lily/download /mnt/download
Cifs : sudo mount -t cifs //10.182.112.107/public /mnt/rhost/arctic/pub -o uid=1000,gid=1000,username=spsz,password=showmethemoney,codepage=utf8,iocharset=utf8
本文主要談一下密碼學中的加密和數字簽名,以及其在java中如何進行使用。對密碼學有興趣的夥伴,推薦看Bruce Schneier的著作:Applied Crypotography。在jdk1.5的發行版本中安全性方面有了很大的改進,也提供了對RSA算法的直接支持,現在我們從實例入手解決問題(本文僅是作爲簡單介紹):
一、密碼學上常用的概念
1)消息摘要:
這是一種與消息認證碼結合使用以確保消息完整性的技術。主要使用單向散列函數算法,可用于檢驗消息的完整性,和通過散列密碼直接以文本形式保存等,目前廣泛使用的算法有MD4、MD5、SHA-1,jdk1.5對上面都提供了支持,在java中進行消息摘要很簡單, java.security.MessageDigest提供了一個簡易的操作方法:
/**
*MessageDigestExample.java
*Copyright 2005-2-16
*/
import java.security.MessageDigest;
/**
*單一的消息摘要算法,不使用密碼.可以用來對明文消息(如:密碼)隱藏保存
*/
public class MessageDigestExample{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java MessageDigestExample text");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//使用getInstance("算法")來獲得消息摘要,這裏使用SHA-1的160位算法
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
System.out.println("\n"+messageDigest.getProvider().getInfo());
//開始使用算法
messageDigest.update(plainText);
System.out.println("\nDigest:");
//輸出算法運算結果
System.out.println(new String(messageDigest.digest(),"UTF8"));
}
}
還可以通過消息認證碼來進行加密實現,javax.crypto.Mac提供了一個解決方案,有興趣者可以參考相關API文檔,本文只是簡單介紹什麽是摘要算法。
2)私鑰加密:
消息摘要只能檢查消息的完整性,但是單向的,對明文消息並不能加密,要加密明文的消息的話,就要使用其他的算法,要確保機密性,我們需要使用私鑰密碼術來交換私有消息。
這種最好理解,使用對稱算法。比如:A用一個密鑰對一個文件加密,而B讀取這個文件的話,則需要和A一樣的密鑰,雙方共享一個私鑰(而在web環境下,私鑰在傳遞時容易被偵聽):
使用私鑰加密的話,首先需要一個密鑰,可用javax.crypto.KeyGenerator産生一個密鑰 (java.security.Key),然後傳遞給一個加密工具(javax.crypto.Cipher),該工具再使用相應的算法來進行加密,主要對稱算法有:DES(實際密鑰只用到56位),AES(支持三種密鑰長度:128、192、256位),通常首先128位,其他的還有DESede等, jdk1.5種也提供了對對稱算法的支持,以下例子使用AES算法來加密:
/**
*PrivateExmaple.java
*Copyright 2005-2-16
*/
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import java.security.Key;
/**
*私?加密,保證消息機密性
*/
public class PrivateExample{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java PrivateExample ");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//通過KeyGenerator形成一個key
System.out.println("\nStart generate AES key");
KeyGenerator keyGen=KeyGenerator.getInstance("AES");
keyGen.init(128);
Key key=keyGen.generateKey();
System.out.println("Finish generating DES key");
//獲得一個私?加密類Cipher,ECB是加密方式,PKCS5Padding是填充方法
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
System.out.println("\n"+cipher.getProvider().getInfo());
//使用私?加密
System.out.println("\nStart encryption:");
cipher.init(Cipher.ENCRYPT_MODE,key);
byte[] cipherText=cipher.doFinal(plainText);
System.out.println("Finish encryption:");
System.out.println(new String(cipherText,"UTF8"));
System.out.println("\nStart decryption:");
cipher.init(Cipher.DECRYPT_MODE,key);
byte[] newPlainText=cipher.doFinal(cipherText);
System.out.println("Finish decryption:");
System.out.println(new String(newPlainText,"UTF8"));
}
}
3)公鑰加密:
上面提到,私鑰加密需要一個共享的密鑰,那麽如何傳遞密鑰呢?web環境下,直接傳遞的話很容易被偵聽到,幸好有了公鑰加密的出現。公鑰加密也叫不對稱加密,不對稱算法使用一對密鑰對,一個公鑰,一個私鑰,使用公鑰加密的數據,只有私鑰能解開(可用于加密);同時,使用私鑰加密的數據,只有公鑰能解開(簽名)。但是速度很慢(比私鑰加密慢100到1000倍),公鑰的主要算法有RSA,還包括Blowfish,Diffie-Helman 等,jdk1.5種提供了對RSA的支持,是一個改進的地方:
/**
*PublicExample.java
*Copyright 2005-2-16
*/
import java.security.Key;
import javax.crypto.Cipher;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
/**
*一個簡單的公?加密例子,Cipher類使用KeyPairGenerator生成的公?和私?
*/
public class PublicExample{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java PublicExample ");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//構成一個RSA密鑰
System.out.println("\nStart generating RSA key");
KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key=keyGen.generateKeyPair();
System.out.println("Finish generating RSA key");
//獲得一個RSA的Cipher類,使用公?加密
Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
System.out.println("\n"+cipher.getProvider().getInfo());
System.out.println("\nStart encryption");
cipher.init(Cipher.ENCRYPT_MODE,key.getPublic());
byte[] cipherText=cipher.doFinal(plainText);
System.out.println("Finish encryption:");
System.out.println(new String(cipherText,"UTF8"));
//使用私?解密
System.out.println("\nStart decryption");
cipher.init(Cipher.DECRYPT_MODE,key.getPrivate());
byte[] newPlainText=cipher.doFinal(cipherText);
System.out.println("Finish decryption:");
System.out.println(new String(newPlainText,"UTF8"));
}
}
4)數字簽名:
數字簽名,它是確定交換消息的通信方身份的第一個級別。上面A通過使用公鑰加密數據後發給B,B利用私鑰解密就得到了需要的數據,問題來了,由于都是使用公鑰加密,那麽如何檢驗是A發過來的消息呢?上面也提到了一點,私鑰是唯一的,那麽A就可以利用A自己的私鑰進行加密,然後B再利用A的公鑰來解密,就可以了;數字簽名的原理就基于此,而通常爲了證明發送數據的真實性,通過利用消息摘要獲得簡短的消息內容,然後再利用私鑰進行加密散列數據和消息一起發送。java中爲數字簽名提供了良好的支持,java.security.Signature類提供了消息簽名:
/**
*DigitalSignature2Example.java
*Copyright 2005-2-16
*/
import java.security.Signature;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.SignatureException;
/**
*數字簽名,使用RSA私鑰對對消息摘要簽名,然後使用公?驗證 測試
*/
public class DigitalSignature2Example{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java DigitalSignature2Example ");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//形成RSA公鑰對
System.out.println("\nStart generating RSA key");
KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key=keyGen.generateKeyPair();
System.out.println("Finish generating RSA key");
//使用私?簽名
Signature sig=Signature.getInstance("SHA1WithRSA");
sig.initSign(key.getPrivate());
sig.update(plainText);
byte[] signature=sig.sign();
System.out.println(sig.getProvider().getInfo());
System.out.println("\nSignature:");
System.out.println(new String(signature,"UTF8"));
//使用公?驗證
System.out.println("\nStart signature verification");
sig.initVerify(key.getPublic());
sig.update(plainText);
try{
if(sig.verify(signature)){
System.out.println("Signature verified");
}else System.out.println("Signature failed");
}catch(SignatureException e){
System.out.println("Signature failed");
}
}
}
5)數字證書。
還有個問題,就是公鑰問題,A用私鑰加密了,那麽B接受到消息後,用A提供的公鑰解密;那麽現在有個討厭的C,他把消息攔截了,然後用自己的私鑰加密,同時把自己的公鑰發給B,並告訴B,那是A的公鑰,結果....,這時候就需要一個中間機構出來說話了(相信權威,我是正確的),就出現了 Certificate Authority(也即CA),有名的CA機構有Verisign等,目前數字認證的工業標准是:CCITT的X.509:
數字證書:它將一個身份標識連同公鑰一起進行封裝,並由稱爲認證中心或 CA 的第三方進行數字簽名。
密鑰庫:java平台爲你提供了密鑰庫,用作密鑰和證書的資源庫。從物理上講,密鑰庫是缺省名稱爲 .keystore 的文件(有一個選項使它成爲加密文件)。密鑰和證書可以擁有名稱(稱爲別名),每個別名都由唯一的密碼保護。密鑰庫本身也受密碼保護;您可以選擇讓每個別名密碼與主密鑰庫密碼匹配。
使用工具keytool,我們來做一件自我認證的事情吧(相信我的認證):
1、創建密鑰庫keytool -genkey -v -alias feiUserKey -keyalg RSA 默認在自己的home目錄下(windows系統是c:\documents and settings\<你的用戶名> 目錄下的.keystore文件),創建我們用 RSA 算法生成別名爲 feiUserKey 的自簽名的證書,如果使用了-keystore mm 就在當前目錄下創建一個密鑰庫mm文件來保存密鑰和證書。
2、查看證書:keytool -list 列舉了密鑰庫的所有的證書
也可以在dos下輸入keytool -help查看幫助。
二、JAR的簽名
我們已經學會了怎樣創建自己的證書了,現在可以開始了解怎樣對JAR文件簽名,JAR文件在Java中相當于 ZIP 文件,允許將多個 Java 類文件打包到一個具有 .jar 擴展名的文件中,然後可以對這個jar文件進行數字簽名,以證實其來源和真實性。該 JAR 文件的接收方可以根據發送方的簽名決定是否信任該代碼,並可以確信該內容在接收之前沒有被篡改過。同時在部署中,可以通過在策略文件中放置訪問控制語句根據簽名者的身份分配對機器資源的訪問權。這樣,有些Applet的安全檢驗訪問就得以進行。
使用jarsigner工具可以對jar文件進行簽名:
現在假設我們有個Test.jar文件(可以使用jar命令行工具生成):
jarsigner Test.jar feiUserKey (這裏我們上面創建了該別名的證書) ,詳細信息可以輸入jarsigner查看幫助
驗證其真實性:jarsigner -verify Test.jar(注意,驗證的是jar是否被修改了,但不檢驗減少的,如果增加了新的內容,也提示,但減少的不會提示。)
使用Applet中: 然後浏覽器就會提示你:准許這個會話-拒絕-始終准許-查看證書等。
三、安全套接字層(SSL Secure Sockets Layer)和傳輸層安全性(TLS Transport Layer Security)
安全套接字層和傳輸層安全性是用于在客戶機和服務器之間構建安全的通信通道的協議。它也用來爲客戶機認證服務器,以及(不太常用的)爲服務器認證客戶機。該協議在浏覽器應用程序中比較常見,浏覽器窗口底部的鎖表明 SSL/TLS 有效:
1)當使用 SSL/TLS(通常使用 https:// URL)向站點進行請求時,從服務器向客戶機發送一個證書。客戶機使用已安裝的公共 CA 證書通過這個證書驗證服務器的身份,然後檢查 IP 名稱(機器名)與客戶機連接的機器是否匹配。
2)客戶機生成一些可以用來生成對話的私鑰(稱爲會話密鑰)的隨機信息,然後用服務器的公鑰對它加密並將它發送到服務器。服務器用自己的私鑰解密消息,然後用該隨機信息派生出和客戶機一樣的私有會話密鑰。通常在這個階段使用 RSA 公鑰算法。
3)客戶機和服務器使用私有會話密鑰和私鑰算法(通常是 RC4)進行通信。使用另一個密鑰的消息認證碼來確保消息的完整性。
java中javax.net.ssl.SSLServerSocketFactory類提供了一個很好的SSLServerSocker 的工廠類,熟悉Socket編程的讀者可以去練習。當編寫完服務器端之後,在浏覽器上輸入https://主機名:端口就會通過SSL/TLS進行通話了。注意:運行服務端的時候要帶系統環境變量運行:javax.net.ssl.keyStore=密鑰庫(創建證書時,名字應該爲主機名,比如localhost)和javax.net.ssl.keyStorePassword=你的密碼。
问题描述:
1 表单提交的数据,用request.getParameter(“xxx”)返回的字符串为乱码或者??
2 Tomcat5下直接通过url如http://localhost/a.jsp?name=中国,这样的get请求在服务端用request. getParameter(“name”)时返回的是乱码;按tomcat4的做法设置Filter也没有用或者用 request.setCharacterEncoding("GBK");也不管用
原因:
1 tomcat5的j2ee实现对表单提交即post方式提示时处理参数采用缺省的iso-8859-1来处理
2 tomcat5对get方式提交的请求对query-string 处理时采用了和post方法不一样的处理方式。(与tomcat4不一样,所以设置setCharacterEncoding(“gbk”))不起作用。
1 post方式的解决办法
1)首先所有的jsp文件都加上:
<%@ page contentType="text/html; charset=GBK"%>
2)实现一个Filter.设置处理字符集为GBK。(在tomcat的webapps/servlet-examples目录有一个完整的例子。请参考web.xml和SetCharacterEncodingFilter的配置。)
把%TOMCAT安装目录%/ webapps\servlets-examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class 文件拷到你的webapp目录/filters下,如果没有filters目录,就创建一个。
3)在你的web.xml里加入如下几行:
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2 get方式的解决办法
1) 打开tomcat的server.xml文件,找到区块,加入如下一行:
URIEncoding=”GBK”
完整的应如下:
<Connector port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="GBK"/>
當 Tomcat 發現 QueryString 並沒有設定 encode 時,並非像文件中所說預設採用 ISO-8859-1 的編碼,而是用一段 fast conversion 來處理,才會造成中文問題,所以,還是必須在 Server.xml 中,加上 URLEncoding 的參數設定才行哦。
Ajax是一种提高web站点吸引力和实用性的书写web页面的方法。它从服务器端更新web页面的特殊区域,从而增强用户的交互性。它允许信息在短时间的延迟或不用刷新页面的情况下更新。
DWR减少了开发时间,也减少了一些可能的错误,这些错误是在提供常用的方法函数并消除一些与高交互性web站点有关的重复性代码的时候产生的。
DWR是作为开源软件(ASL verssion 2.0)而可以免费得到的。它凭借它的广阔的库、例子和指南非常易于实现。把它结合到一个现有的站点是非常简单的,同样它也可以简单地与大多数java框架结合。
util.js
util.js包含了一些使用的方法,从而帮助你利用j avascript(可能)从服务器端更新你的web数据。
你可以在DWR之外的地方使用它,因为它并不依赖与DWR而实现。
它包含四个页面处理函数:getValue[s]()、setValue[s]()作用于除tables、lists和images以外的大多数html元素。getText()作用于select lists。
addRows()和removeAllRows()用于编辑tables。addOptions()和removeAllOptions()用于编辑lists(如:select lists、ul、ol)。
$()
$函数(在j avascript中,他的名字是合法的)的思想是从prototype引进的。一般说来,$ = document.getElementById。在以后你花大量时间进行ajax编程的时候,在合适的地方使用这种格式是很有益的。
'$'通过给定的ID在当前HTML页面找到元素,如果多于一个的参数被提交,它就会返回一个包含已找到元素的数组。这个函数从prototype的library中得到的灵感,而且,它还能更好的工作在不同的浏览器中。
Generating Lists
DWR的一个功能可以给一个下拉列表(select list)添加选项,只需使用DWRUtil.addOptions()。
如果你在更新列表之前,希望保留一些选项,你需要写以下一些代码:
var sel = DWRUtil.getValue(id);
DWRUtil.removeAllOptions(id);
DWRUtil.addOptions(id, ...);
DWRUtil.setValue(id, sel);
如果你想有个初始化选项,如:“please select”,你可以直接使用:
DWRUtil.addOptions(id, ["Please select"]);
DWRUtil.addOptions 有5种调用方法:Array: DWRUtil.addOptions(selectid, array) 。selectid为目标ID,array为每一项的text。
Array of objects (option text = option value): DWRUtil.addOptions(selectid, data, prop) 用text和value的集合来为每一个数组元素创建一个选项,pro参数指定text和value的值。
Array of objects (with differing option text and value): DWRUtil.addOptions(selectid, array, valueprop, textprop) 用text和value的集合来为每一个数组元素创建一个选项,valueprop确定value,textprop确定text。
Object: DWRUtil.addOptions(selectid, map, reverse) 为map中每一个属性(property)创建一个选项,属性名作为选项的value,属性的value作为选项的text。这样做看起来是错的,但实际上这种做法的确是正确的。如果reverse参数被设置为true,则属性的value还是被用做选项的value。
Map of objects: DWRUtil.addOptions(selectid, map, valueprop, textprop) 为map中的每一个对象创建一个选项,valueprop指定选项的value,textprop指定选项的text。
Generating Tables
DWRUtil.addRows() 从一个数组(第二个参数)取得值,创建table的每一行。从另一个数组(第三个参数)去得值,为table的每一行创建若干列。
DWRUtil.addRows()的用法:
DWRUtil.addRows(”items”,items,cellFunctions);
第一个items,应该是table的id,第2个items,是远程返回的结果集,cellFunctions,对表格行填充的回调函数一类的。
var cellFunctions = [ function(item) { return item.name; },
function(item) { return item.description; },
function(item) { return item.formattedPrice; },
function(item) { var btn = document.createElement(”button”); btn.innerHTML = “Add to cart”; btn.itemId = item.id; btn.onclick = addToCartButtonHandler; return btn; }
应该是一个function(item)就代表一列,return的就是传入的结果集的某列的内容。那么这个表格就应该有4列。最后一列是静态的html代码了。不过没研究一下,return回去动静混合的内容,就是一个字串会如何。我猜应该也可以吧。
DWRUtil.getText(id)
可以根据id取得text的值,这个方法只能用于select list
DWRUtil.getValue(id)
可以根据id取得value,使用这个方法,你不必在意div和select list的不同。
DWRUtil.getValues()
getValues() is similar to getValue() except that the input is a Javascript object that contains name/value pairs. The names are assumed to be the IDs of HTML elements, and the values are altered to reflect the contents of those IDs. This method does not return the object in question, it alters the value that you pass to it.
这个方法和getValue()一样,只是它传入的是一个包含名字和数值的j avascript对象.这个名字就是HTML元素的ID。这个方法不会返回任何对象,它只会将ID的value映射给传入的value。例:
function doGetValues() {
var text= "{
div:null,
textarea:null,
select:null,
text:null,
password:null,
formbutton:null,
button:null
}";
var object = objectEval(text); //j avascript对象
DWRUtil.getValues(object);
var reply = DWRUtil.toDescriptiveString(object, 2); //toString
reply = reply.replace(/n/g, "<br/>"); //转意
DWRUtil.setValue("getvaluesret", reply); //显示
}
DWRUtil.onReturn
贴一段代码,暂时不理解,用onReturn和不用有什么区别
<script>
function submitFunction()
{
$("alert").style.display = "inline";
setTimeout("unsubmitFunction();", 1000);
}
function unsubmitFunction()
{
$("alert").style.display = "none";
}
</script>
<p><input type="text" onkeydown="DWRUtil.onReturn(event, submitFunction)"/>
<input type="button" onclick="submitFunction()" value="GO"/>
<span id="alert" style="display:none; background:#FFFFDD; font-weight:bold;">submitFunction called</span>
</p>
DWRUtil.selectRange
在一个input box里选一个范围
DWRUtil.selectRange("sel-test", $("start").value, $("end").value);
DWRUtil.setValue(id, value)
用ID找到元素,并更新value
DWRUtil.setValues()
和setValue(id,value)一样,只是它需要的参数是个j avascript对象,如:
DWRUtil.setValues({
div: "new div content",
password: "1234567890"
});
DWRUtil.toDescriptiveString
带debug信息的toString,第一个为将要debug的对象,第二个参数为处理等级。等级如下:
0: Single line of debug 单行调试
1: Multi-line debug that does not dig into child objects 不分析子元素的多行调试
2: Multi-line debug that digs into the 2nd layer of child objects 最多分析到第二层子元素的多行调试
And so on. Level 2 and greater probably produce too much output.
总结:DWR不但屏蔽了许多client与server交互的重复且复杂的代码,而且还提供了一些常用的方法,一些思想还是从prototype继承而来,并有一定的改进。同时,它也考虑到了与struts、hibernate、spring的结合问题。
需要注意的是,DWR是一种把服务器端的java代码通过j avascript直接从浏览器调用的方法(DWR is a way of calling Java code on the server directly from Javascript in the browser.),而不是一个j avascript的库(Generally speaking DWR is not a generic JavaScript library so it does not attempt to provide fill this need. However this is one of these really useful functions to have around if you are doing Ajax work.)能做到怎么多,已经很难得了。
DWR自04年11月草案提出到现在的Version 1.1 beta 3(2005-12-29),已经更新发布了20多次了,但愿在ajax的发展大路上,能始终看见DWR的身影。
程序代码: |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans public "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/TestDB</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value></value> </property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <bean id="userDAO" class="onlyfun.caterpillar.UserDAO"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="transactionManager"> <ref bean="transactionManager"/> </property> </bean> </beans> |
程序代码: |
package onlyfun.caterpillar; import javax.sql.DataSource; import org.springframework.jdbc.core.*; import org.springframework.transaction.*; import org.springframework.transaction.support.*; import org.springframework.dao.*; public class UserDAO { private DataSource dataSource; private PlatformTransactionManager transactionManager; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void insertUser(User user) { .... } } |
程序代码: |
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.update("INSERT INTO USER VALUES('Spring008', 'caterpillar', 'M', 29)"); jdbcTemplate.update("INSERT INTO USER VALUES('Spring009', 'momor', 'F', 26)"); jdbcTemplate.update("INSERT INTO USER VALUES('Spring010, 'beckyday', 'F', 35)"); } catch (DataAccessException ex) { transactionManager.rollback(status); // 也可以執行status.setRollbackOnly(); throw ex; } transactionManager.commit(status); |
程序代码: |
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(new TransactionCallbackWithoutResult() { public void doInTransactionWithoutResult(TransactionStatus status) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.update("INSERT INTO USER VALUES('Spring008', 'caterpillar', 'M', 29)"); jdbcTemplate.update("INSERT INTO USER VALUES('Spring009', 'momor', 'F', 26)"); jdbcTemplate.update("INSERT INTO USER VALUES('Spring010, 'beckyday', 'F', 35)"); } }); |
程序代码: |
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); Object result = transactionTemplate.execute( new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { // 作一些操作 // 收集為結果物件 resultObject return resultObject; }); |
您可以使用JdbcTemplate的execute()方法執行SQL陳述,例如:
代碼:
jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
如果是UPDATE或INSERT,您可以使用update()方法,一個基本的INSERT例子是:
代碼:
jdbcTemplate.update("INSERT INTO USER VALUES('"+ user.getId() + "', '" + user.getName() + "', '"
+ user.getSex() + "', '"
+ user.getAge() + "')");
使用UPDATE時,您可以在欄位上使用 "?",並指定參數陣列給對應的欄位,例如:
代碼:
jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});
在上例中所給定的name與id是String物件,它會取代之前對應順序的 "?",然後執行SQL更新。
使用參數陣列來指定SQL中的變量相當方便,上例中的INSERT範例也可以這麼作,減輕撰寫SQL與Java物件結合時的負擔:
代碼:
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});
使用JdbcTemplate進行查詢時,我們可以使用queryForXXX()等方法,例如下面使用queryForInt()方法傳回USER表格中的資料筆數:
代碼:
int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");
您也可以使用queryForObject()傳回一個查詢後的結果物件,例如下例傳回一個String物件:
代碼:
String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);
上面兩個例子傳回的都是單一筆資料,如果傳回多筆資料,則可以使用queryForList()方法,例如:
代碼:
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
傳回的List中包括的是Map物件,每個Map物件代表查詢結果中的一筆資料,每筆資料包括多個欄位內容,要取得欄位中的值,要使用欄位名稱作為key值,例如:
代碼:
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
Iterator it = rows.iterator();
while(it.hasNext()) {
Map userMap = (Map) it.next();
System.out.print(userMap.get("user_id") + "\t");
System.out.print(userMap.get("name") + "\t");
System.out.print(userMap.get("sex") + "\t");
System.out.println(userMap.get("age") + "\t");
}
事實上,JdbcTemplate只是將我們使用JDBC的流程封裝起來而已,包括了例外捕捉、SQL的執行、查詢結果的轉換與傳回等等,之前所用到的參數設定,也只是在方法中幫您作SQL語句組合的動作而已,Spring大量使用Template Method模式來封裝固定流程的動作,XXXTemplate等類別都是基於這種方式實現的。
除了大量使用Template Method來封裝一些低層操作細節,Spring也大量使用callback方式來呼叫相關類別之方法以提供傳統JDBC相關類別的功能,使得傳統JDBC的使用者也能清楚瞭解Spring所提供的相關封裝類別方法之使用。
例如JDBC的PreparedStatement,我們可以實作PreparedStatementSetter介面來提供:
代碼:
final String id = user.getId();
final String name = user.getName();
final String sex = user.getSex() + "";
final int age = user.getAge();
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
ps.setString(2, name);
ps.setString(3, sex);
ps.setInt(4, age);
}
});
您可以實作RowCallbackHandler介面,在查詢到資料之後先作一些處理再傳回,例如下面的例子實現了手動的O/R Mapping:
代碼:
final User user = new User();
jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
new Object[] {id},
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
}
});
您更可以實作RowMapper介面將手動的O/R Mapping程式碼獨立撰寫,共用在不同的DAO方法之中:
代碼:
class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
return user;
}
}
在findAllByRowMapperResultReader()裡面使用UserRowMapper:
代碼:
public List findAllByRowMapperResultReader() {
String sql = "SELECT * FROM USER";
return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
}
在getUser(id)裡面使用UserRowMapper:
代碼:
public User getUser(final String id) throws DataAccessException {
String sql = "SELECT * FROM USER WHERE user_id=?";
final Object[] params = new Object[] { id };
List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));
return (User) list.get(0);
}
<c:if test="${product.price >= customer.limit}">
...
</c:if>
<c:forEach var="current">
<c:out value="Product-${current}"/>
</c:forEach>
<jsp:useBean id="customer" type="sample.Customer" scope="request"/> ...
Customer Name: <%=customer.getName()%>
...
<% if (customer.getState().equals("CO")){ %>
...
<%}%>
Customer Name: ${ customer. name}
<c:if test="${customer. state == param. state}">
...
</c:if>
${user.address.city}
${products.[product.id]}
${products.["Part-010"]}
<c:set var="city" value="${user.address.city}" default="N/A" />
<c:out value="${customer.name}" default="N/A" />
<c:if test="${user.visitCount == 1}"
Welcome back!
</c:if>
<c:choose>
<c:when test="${count == 0}">
No records matched your selection.
</c:when>
<c:otherwise>
<c:out value="${count}"/> records matched your selection.
</c:otherwise>
</c:choose>
<table>
<c:forEach var="product"
items="${products}"
varStatus="status">
<tr>
<td><c:out value="${status.count}"/></td>
<td><c:out value="${product.name}"/></td>
</tr>
</c:forEach>
</table>
<c:url=http://mysite.com/register var="myUrl">
<c:param name="name" value="${param.name}"/>
</c:url>
<a href='<c:out value="${myUrl}"/>'>Register</a>
<fmt:message key="welcome">
<fmt:param value="${visitCount}" />
<fmt:message/>
<sql:setDataSource var="datasource" driver="org.gjt.mm.mysql.driver" url="jdbc:mysql://localhost/db" />
<sql:query var="customer" datasource="${datasource}"
SELECT * FROM customers WHERE state = 'CO' ORDER BY city
</sql:query>
<table>
<c:forEach var="row" items="${customers.row}">
<tr>
<td><c:out value="${row.custName}" /></td>
<td><c:out value="${row.address}" /></td>
</tr>
</c:forEach>
</table>
<sql:transaction dataSource="${dataSource}">
<sql:update>
UPDATE account SET Balance =Balance -? WHERE accountNo = ?
<sql:param value="${transferAmount}"/>
<sql:param value="${accountFrom}"/>
</sql:update>
</sql:transaction>
<c:import url="http://oreilly.com/book?id=1234" var="xml"/>
<x:parse source="${xml}" var="bookInfo"/>
<x:out select="$bookInfo/title"/>
<x:out select="$bookInfo/author"/>
<c:import url="/books" var="xml"/>
<c:import url="/WEB-INF/xslt/bookDisplay.xsl" var="xslt"/>
<x:transform source="${xml}" xslt="${xslt}"/>