2007年10月2日
volatile:
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。 这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。 而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。 使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。 由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
Synchronized:
防止多个线程同时访问相同的代码段。
Transient:
表明类序列化时,变量不必序列化。
配置一个数据源
Spring在第三方依赖包中包含了两个数据源的实现类包,其一是Apache的DBCP,其二是 C3P0。可以在Spring配置文件中利用这两者中任何一个配置数据源。
DBCP数据源
DBCP类包位于 <spring_home></spring_home>/lib/jakarta-commons/commons-dbcp.jar,DBCP是一个依赖 Jakarta commons-pool对象池机制的数据库连接池,所以在类路径下还必须包括<spring_home></spring_home>/lib/jakarta- commons/commons-pool.jar。下面是使用DBCP配置MySql数据源的配置片断:
xml 代码
- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
- destroy-method="close">
- <property name="driverClassName" value="com.mysql.jdbc.Driver" />
- <property name="url" value="jdbc:mysql://localhost:3309/sampledb" />
- <property name="username" value="root" />
- <property name="password" value="1234" />
- </bean>
BasicDataSource提供了close()方法关闭数据源,所以必须设定destroy-method=”close”属性, 以便Spring容器关闭时,数据源能够正常关闭。除以上必须的数据源属性外,还有一些常用的属性:
defaultAutoCommit:设置从数据源中返回的连接是否采用自动提交机制,默认值为 true;
defaultReadOnly:设置数据源是否仅能执行只读操作, 默认值为 false;
maxActive:最大连接数据库连接数,设置为0时,表示没有限制;
maxIdle:最大等待连接中的数量,设置为0时,表示没有限制;
maxWait:最大等待秒数,单位为毫秒, 超过时间会报出错误信息;
validationQuery:用于验证连接是否成功的查询SQL语句,SQL语句必须至少要返回一行数据, 如你可以简单地设置为:“select count(*) from user”;
removeAbandoned:是否自我中断,默认是 false ;
removeAbandonedTimeout:几秒后数据连接会自动断开,在removeAbandoned为true,提供该值;
logAbandoned:是否记录中断事件, 默认为 false;
C3P0数据源
C3P0是一个开放源代码的JDBC数据源实现项目,它在lib目录中与Hibernate一起发布,实现了JDBC3和JDBC2扩展规范说明的 Connection 和Statement 池。C3P0类包位于<spring_home></spring_home>/lib/c3p0/c3p0-0.9.0.4.jar。下面是使用C3P0配置一个 Oracle数据源:
xml 代码
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
- destroy-method="close">
- <property name="driverClass" value=" oracle.jdbc.driver.OracleDriver "/>
- <property name="jdbcUrl" value=" jdbc:oracle:thin:@localhost:1521:ora9i "/>
- <property name="user" value="admin"/>
- <property name="password" value="1234"/>
- </bean>
ComboPooledDataSource和BasicDataSource一样提供了一个用于关闭数据源的close()方法,这样我们就可以保证Spring容器关闭时数据源能够成功释放。
C3P0拥有比DBCP更丰富的配置属性,通过这些属性,可以对数据源进行各种有效的控制:
acquireIncrement:当连接池中的连接用完时,C3P0一次性创建新连接的数目;
acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30;
acquireRetryDelay:两次连接中间隔时间,单位毫秒,默认为1000;
autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚。默认为false;
automaticTestTable: C3P0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数,那么属性preferredTestQuery将被忽略。你 不能在这张Test表上进行任何操作,它将中为C3P0测试所用,默认为null;
breakAfterAcquireFailure:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调 用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为 false;
checkoutTimeout:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒,默认为0;
connectionTesterClassName: 通过实现ConnectionTester或QueryConnectionTester的类来测试连接,类名需设置为全限定名。默认为 com.mchange.v2.C3P0.impl.DefaultConnectionTester;
idleConnectionTestPeriod:隔多少秒检查所有连接池中的空闲连接,默认为0表示不检查;
initialPoolSize:初始化时创建的连接数,应在minPoolSize与maxPoolSize之间取值。默认为3;
maxIdleTime:最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0;
maxPoolSize:连接池中保留的最大连接数。默认为15;
maxStatements:JDBC的标准参数,用以控制数据源内加载的PreparedStatement数量。但由于预缓存的Statement属 于单个Connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素,如果maxStatements与 maxStatementsPerConnection均为0,则缓存被关闭。默认为0;
maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存Statement数。默认为0;
numHelperThreads:C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为3;
preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null;
propertyCycle: 用户修改系统配置参数执行前最多等待的秒数。默认为300;
testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都 将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
等方法来提升连接测试的性能。默认为false;
testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。
读配置文件的方式引用属性:
- <bean id="propertyConfigurer"
- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="location" value="/WEB-INF/jdbc.properties"/>
- </bean>
- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
- destroy-method="close">
- <property name="driverClassName" value="${jdbc.driverClassName}" />
- <property name="url" value="${jdbc.url}" />
- <property name="username" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- </bean>
在jdbc.properties属性文件中定义属性值:
jdbc.driverClassName= com.mysql.jdbc.Driver
jdbc.url= jdbc:mysql://localhost:3309/sampledb
jdbc.username=root
jdbc.password=1234
提示 经常有开发者在${xxx}的前后不小心键入一些空格,这些空格字符将和变量合并后作为属性的值。如: <property name="username" value=" ${jdbc.username} "></property> 的属性配置项,在前后都有空格,被解析后,username的值为“ 1234 ”,这将造成最终的错误,因此需要特别小心。
获取JNDI数据源
如果应用配置在高性能的应用服务器(如WebLogic或Websphere等)上,我们可能更希望使用应用服务器本身提供的数据源。应用服务器的数据源 使用JNDI开放调用者使用,Spring为此专门提供引用JNDI资源的JndiObjectFactoryBean类。下面是一个简单的配置:
xml 代码
- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
- <property name="jndiName" value="java:comp/env/jdbc/bbt"/>
- </bean>
通过jndiName指定引用的JNDI数据源名称。
Spring 2.0为获取J2EE资源提供了一个jee命名空间,通过jee命名空间,可以有效地简化J2EE资源的引用。下面是使用jee命名空间引用JNDI数据源的配置:
xml 代码
- <beans xmlns=http://www.springframework.org/schema/beans
- xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
- xmlns:jee=http://www.springframework.org/schema/jee
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/jee
- http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
- <jee:jndi-lookup id="dataSource" jndi-name=" java:comp/env/jdbc/bbt"/>
- </beans>
Spring的数据源实现类
Spring本身也提供了一个简单的数据源实现类DriverManagerDataSource ,它位于org.springframework.jdbc.datasource包中。这个类实现了javax.sql.DataSource接口,但 它并没有提供池化连接的机制,每次调用getConnection()获取新连接时,只是简单地创建一个新的连接。因此,这个数据源类比较适合在单元测试 或简单的独立应用中使用,因为它不需要额外的依赖类。
下面,我们来看一下DriverManagerDataSource的简单使用:当然,我们也可以通过配置的方式直接使用DriverManagerDataSource。
java 代码
- DriverManagerDataSource ds = new DriverManagerDataSource ();
- ds.setDriverClassName("com.mysql.jdbc.Driver");
- ds.setUrl("jdbc:mysql://localhost:3309/sampledb");
- ds.setUsername("root");
- ds.setPassword("1234");
- Connection actualCon = ds.getConnection();
小结
不管采用何种持久化技术,都需要定义数据源。Spring附带了两个数据源的实现类包,你可以自行选择进行定义。在实际部署时,我们可能会直接采用应用服 务器本身提供的数据源,这时,则可以通过JndiObjectFactoryBean或jee命名空间引用JNDI中的数据源。
DBCP与C3PO配置的区别:
C3PO :DBCP:
xml 代码
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
- <property name="driverClass">
- <value>oracle.jdbc.driver.OracleDriver</value>
- </property>
- <property name="jdbcUrl">
- <value>jdbc:oracle:thin:@10.10.10.6:1521:DataBaseName</value>
- </property>
- <property name="user">
- <value>testAdmin</value>
- </property>
- <property name="password">
- <value>123456</value>
- </property>
- </bean>
xml 代码
- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName">
- <value>oracle.jdbc.driver.OracleDriver</value>
- </property>
- <property name="url">
- <value>jdbc:oracle:thin:@10.10.10.6:1521:DataBaseName</value>
- </property>
- <property name="username">
- <value>testAdmin</value>
- </property>
- <property name="password">
- <value>123456</value>
- </property>
- </bean>
oracle xe universal默认创建AL32UTF8字符集,这种字符集1个汉字占用3个字节,而
ZHS16GBK字符集1个汉字占用2个字节,为了节约空间,我尝试改变数据库的字符集,
但是失败了,因为新字符集不是旧字符集的超集。即使用restrict模式也不行。
考虑到oracle xe 安装时只是建一个空库,仅包含系统表空间等,设想通过手工执行
创建ZHS16GBK字符集数据库的办法实现。
从sybex的10g ocp教材中复制并修改得下面语句,其中E:\ORADATA\TEST是我们要存放
新数据库物理文件的目录,以和旧数据库区分。
CREATE DATABASE XE
CONTROLFILE REUSE
LOGFILE
GROUP 1 'E:\ORADATA\TEST\REDO01.LOG' SIZE 10M,
GROUP 2 'E:\ORADATA\TEST\REDO02.LOG' SIZE 10M,
GROUP 3 'E:\ORADATA\TEST\REDO03.LOG' SIZE 10M
DATAFILE 'E:\ORADATA\TEST\SYSTEM01.DBF' SIZE 400M
AUTOEXTEND ON NEXT 50M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL
SYSAUX DATAFILE 'E:\ORADATA\TEST\SYSAUX01.DBF' SIZE 250M
AUTOEXTEND ON NEXT 50M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL
DEFAULT TEMPORARY TABLESPACE temp
TEMPFILE 'E:\ORADATA\TEST\TEMP01.DBF' SIZE 100M
AUTOEXTEND ON NEXT 100M MAXSIZE 8000M
UNDO TABLESPACE undo
DATAFILE 'E:\ORADATA\TEST\undo01.DBF' SIZE 200M
AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL
CHARACTER SET ZHS16GBK
NATIONAL CHARACTER SET AL16UTF16
USER SYS IDENTIFIED BY SYS
USER SYSTEM IDENTIFIED BY SYS
;
在执行这个脚本以前,有3点要注意:
1 要修改F:\oraclexe\app\oracle\product\10.2.0\server\config\scripts\init.ora'
到
F:\oraclexe\app\oracle\product\10.2.0\server\config\scripts\test.ora'
把所有路径改成E:\ORADATA\TEST及其子目录,为此我们需要在E:\ORADATA\TEST下建立
bdump等目录,这些目录可以从现有的XE数据库F:\oraclexe\app\oracle\admin\XE复制得到。
2 要去掉EXTENT MANAGEMENT LOCAL语句否则执行出错。(没有找出原因)
CREATE DATABASE XE
*
第 1 行出现错误:
ORA-25146: EXTENT MANAGEMENT ?????
3 test.ora中的db_name不能改变,否则执行出错。(没有找出原因)
ORA-02778: Name given for the log directory is invalid
具体执行步骤(以Windows XP为例)
1在cmd命令行窗口执行 net start oracleservicexe
C:\Documents and Settings\Administrator>net start oracleservicexe
OracleServiceXE 服务正在启动 .........
OracleServiceXE 服务已经启动成功。
2 以sys用户登录
C:\Documents and Settings\Administrator>sqlplus /nolog
SQL*Plus: Release 10.2.0.1.0 - Production on 星期二 5月 30 22:07:45 2006
Copyright (c) 1982, 2005, Oracle. All rights reserved.
SQL> conn / as sysdba
已连接。
3 查看现有的字符集设置
SQL> set lines 140
SQL> select * from nls_database_parameters;
PARAMETER
------------------------------------------------------------
VALUE
-------------------------------------------------------------------
------------------------------------------------------------
NLS_LANGUAGE
AMERICAN
NLS_TERRITORY
AMERICA
NLS_CURRENCY
$
NLS_ISO_CURRENCY
AMERICA
NLS_NUMERIC_CHARACTERS
.,
NLS_CHARACTERSET
AL32UTF8
NLS_CALENDAR
GREGORIAN
NLS_DATE_FORMAT
DD-MON-RR
NLS_DATE_LANGUAGE
AMERICAN
NLS_SORT
BINARY
NLS_TIME_FORMAT
HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT
DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT
HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT
DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY
$
NLS_COMP
BINARY
NLS_LENGTH_SEMANTICS
BYTE
NLS_NCHAR_CONV_EXCP
FALSE
NLS_NCHAR_CHARACTERSET
AL16UTF16
NLS_RDBMS_VERSION
10.2.0.1.0
已选择20行。
SQL> select lengthb('择') from dual;
LENGTHB('择')
-------------
3
已选择 1 行。
我们可以看到,当前AL32UTF8字符集1个汉字占用3个字节
4 关闭数据库,但不停止服务
QL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
5 用修改过的pfile test.ora启动数据库(nomount)
SQL> startup nomount pfile='F:\oraclexe\app\oracle\product\10.2.0\server\config\scripts\test.ora'
ORACLE 例程已经启动。
Total System Global Area 285212672 bytes
Fixed Size 1287016 bytes
Variable Size 92277912 bytes
Database Buffers 188743680 bytes
Redo Buffers 2904064 bytes
6 执行建立数据库脚本
SQL> CREATE DATABASE XE
2 CONTROLFILE REUSE
3 LOGFILE
4 GROUP 1 'E:\ORADATA\TEST\REDO01.LOG' SIZE 10M,
5 GROUP 2 'E:\ORADATA\TEST\REDO02.LOG' SIZE 10M,
6 GROUP 3 'E:\ORADATA\TEST\REDO03.LOG' SIZE 10M
7 DATAFILE 'E:\ORADATA\TEST\SYSTEM01.DBF' SIZE 400M
8 AUTOEXTEND ON NEXT 50M MAXSIZE UNLIMITED
9 SYSAUX DATAFILE 'E:\ORADATA\TEST\SYSAUX01.DBF' SIZE 250M
10 AUTOEXTEND ON NEXT 50M MAXSIZE UNLIMITED
11 DEFAULT TEMPORARY TABLESPACE temp
12 TEMPFILE 'E:\ORADATA\TEST\TEMP01.DBF' SIZE 100M
13 AUTOEXTEND ON NEXT 100M MAXSIZE 8000M
14 UNDO TABLESPACE undo
15 DATAFILE 'E:\ORADATA\TEST\undo01.DBF' SIZE 200M
16 AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED
17 CHARACTER SET ZHS16GBK
18 NATIONAL CHARACTER SET AL16UTF16
19 USER SYS IDENTIFIED BY SYS
20 USER SYSTEM IDENTIFIED BY SYS
21 ;
数据库已创建。
7 执行创建数据字典和程序包的系统SQL脚本
@F:\oraclexe\app\oracle\product\10.2.0\server\RDBMS\ADMIN\catalog.sql
@F:\oraclexe\app\oracle\product\10.2.0\server\RDBMS\ADMIN\catproc.sql
大约需要10几分钟
8 关闭数据库,但不停止服务
SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
9 用修改过的pfile test.ora启动数据库
SQL> startup pfile='F:\oraclexe\app\oracle\product\10.2.0\server\config\scripts\test.ora'
ORACLE 例程已经启动。
Total System Global Area 285212672 bytes
Fixed Size 1287016 bytes
Variable Size 92277912 bytes
Database Buffers 188743680 bytes
Redo Buffers 2904064 bytes
数据库装载完毕。
数据库已经打开。
10 查看新建数据库的字符集设置
SQL> set lines 140
SQL> select * from nls_database_parameters;
PARAMETER VALUE
------------------------------ -------------------------------------------------
-------------------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET ZHS16GBK
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_RDBMS_VERSION 10.2.0.1.0
已选择20行。
SQL> select lengthb('A') from dual;
LENGTHB('A')
------------
1
已选择 1 行。
SQL> select lengthb('择') from dual;
LENGTHB('择')
-------------
2
已选择 1 行。
可见我们新的数据库的确是ZHS16GBK字符集,1个汉字占用2个字节
11 要启动旧数据库(因为新旧数据库同名,而且oracle xe限制同时只能打开一个数据库)
SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup
ORACLE 例程已经启动。
Total System Global Area 285212672 bytes
Fixed Size 1287016 bytes
Variable Size 96472216 bytes
Database Buffers 184549376 bytes
Redo Buffers 2904064 bytes
数据库装载完毕。
数据库已经打开。
----
如果以后要用spfile自动新建数据库
SQL> create spfile from pfile='D:\oraclexe\app\oracle\product\10.2.0\server\config\scripts\test.ora';
文件已创建。
SQL> shutdown immediate;
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup
ORACLE 例程已经启动。
Total System Global Area 285212672 bytes
Fixed Size 1287016 bytes
Variable Size 92277912 bytes
Database Buffers 188743680 bytes
Redo Buffers 2904064 bytes
数据库装载完毕。
数据库已经打开。
SQL> select lengthb('择') from dual;
LENGTHB('择')
-------------
2
已选择 1 行。
远程登录,客户端也需要10g
SQL> conn system/sys@//10.6.xx.xx:1521/XE
已连接。
SQL> conn sys/sys@//10.6.xx.xx:1521/XE as sysdba
已连接。
[url]http://www.itpub.net/558697.html[/url]
的问题也算得到了解决
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/l1t/archive/2006/05/31/765125.aspx
数据字典dict总是属于Oracle用户sys的。
1、用户:
select username from dba_users;
改口令
alter user spgroup identified by spgtest;
2、表空间:
select * from dba_data_files;
select * from dba_tablespaces;//表空间
select tablespace_name,sum(bytes), sum(blocks)
from dba_free_space group by tablespace_name;//空闲表空间
select * from dba_data_files
where tablespace_name='RBS';//表空间对应的数据文件
select * from dba_segments
where tablespace_name='INDEXS';
3、数据库对象:
select * from dba_objects;
CLUSTER、DATABASE LINK、FUNCTION、INDEX、LIBRARY、PACKAGE、PACKAGE BODY、
PROCEDURE、SEQUENCE、SYNONYM、TABLE、TRIGGER、TYPE、UNDEFINED、VIEW。
4、表:
select * from dba_tables;
analyze my_table compute statistics;->dba_tables后6列
select extent_id,bytes from dba_extents
where segment_name='CUSTOMERS' and segment_type='TABLE'
order by extent_id;//表使用的extent的信息。segment_type='ROLLBACK'查看回滚段的空间分配信息
列信息:
select distinct table_name
from user_tab_columns
where column_name='SO_TYPE_ID';
5、索引:
select * from dba_indexes;//索引,包括主键索引
select * from dba_ind_columns;//索引列
select i.index_name,i.uniqueness,c.column_name
from user_indexes i,user_ind_columns c
where i.index_name=c.index_name
and i.table_name ='ACC_NBR';//联接使用
6、序列:
select * from dba_sequences;
7、视图:
select * from dba_views;
select * from all_views;
text 可用于查询视图生成的脚本
8、聚簇:
select * from dba_clusters;
9、快照:
select * from dba_snapshots;
快照、分区应存在相应的表空间。
10、同义词:
select * from dba_synonyms
where table_owner='SPGROUP';
//if owner is PUBLIC,then the synonyms is a public synonym.
if owner is one of users,then the synonyms is a private synonym.
11、数据库链:
select * from dba_db_links;
在spbase下建数据库链
create database link dbl_spnew
connect to spnew identified by spnew using 'jhhx';
insert into acc_nbr@dbl_spnew
select * from acc_nbr where nxx_nbr='237' and line_nbr='8888';
12、触发器:
select * from dba_trigers;
存储过程,函数从dba_objects查找。
其文本:select text from user_source where name='BOOK_SP_EXAMPLE';
建立出错:select * from user_errors;
oracle总是将存储过程,函数等软件放在SYSTEM表空间。
13、约束:
(1)约束是和表关联的,可在create table或alter table table_name add/drop/modify来建立、修改、删除约束。
可以临时禁止约束,如:
alter table book_example
disable constraint book_example_1;
alter table book_example
enable constraint book_example_1;
(2)主键和外键被称为表约束,而not null和unique之类的约束被称为列约束。通常将主键和外键作为单独的命名约束放在字段列表下面,而列约束可放在列定义的同一行,这样更具有可读性。
(3)列约束可从表定义看出,即describe;表约束即主键和外键,可从dba_constraints和dba_cons_columns 查。
select * from user_constraints
where table_name='BOOK_EXAMPLE';
select owner,CONSTRAINT_NAME,TABLE_NAME
from user_constraints
where constraint_type='R'
order by table_name;
(4)定义约束可以无名(系统自动生成约束名)和自己定义约束名(特别是主键、外键)
如:create table book_example
(identifier number not null);
create table book_example
(identifier number constranit book_example_1 not null);
14、回滚段:
在所有的修改结果存入磁盘前,回滚段中保持恢复该事务所需的全部信息,必须以数据库发生的事务来相应确定其大小(DML语句才可回滚,create,drop,truncate等DDL不能回滚)。
回滚段数量=并发事务/4,但不能超过50;使每个回滚段大小足够处理一个完整的事务;
create rollback segment r05
tablespace rbs;
create rollback segment rbs_cvt
tablespace rbs
storage(initial 1M next 500k);
使回滚段在线
alter rollback segment r04 online;
用dba_extents,v$rollback_segs监测回滚段的大小和动态增长。
回滚段的区间信息
select * from dba_extents
where segment_type='ROLLBACK' and segment_name='RB1';
回滚段的段信息,其中bytes显示目前回滚段的字节数
select * from dba_segments
where segment_type='ROLLBACK' and segment_name='RB1';
为事物指定回归段
set transaction use rollback segment rbs_cvt
针对bytes可以使用回滚段回缩。
alter rollback segment rbs_cvt shrink;
select bytes,extents,max_extents from dba_segments
where segment_type='ROLLBACK' and segment_name='RBS_CVT';
回滚段的当前状态信息:
select * from dba_rollback_segs
where segment_name='RB1';
比多回滚段状态status,回滚段所属实例instance_num
查优化值optimal
select n.name,s.optsize
from v$rollname n,v$rollstat s
where n.usn=s.usn;
回滚段中的数据
set transaction use rollback segment rb1;/*回滚段名*/
select n.name,s.writes
from v$rollname n,v$rollstat s
where n.usn=s.usn;
当事务处理完毕,再次查询$rollstat,比较writes(回滚段条目字节数)差值,可确定事务的大小。
查询回滚段中的事务
column rr heading 'RB Segment' format a18
column us heading 'Username' format a15
column os heading 'Os User' format a10
column te heading 'Terminal' format a10
select r.name rr,nvl(s.username,'no transaction') us,s.osuser os,s.terminal te
from v$lock l,v$session s,v$rollname r
where l.sid=s.sid(+)
and trunc(l.id1/65536)=R.USN
and l.type='TX'
and l.lmode=6
order by r.name;
15、作业
查询作业信息
select job,broken,next_date,interval,what from user_jobs;
select job,broken,next_date,interval,what from dba_jobs;
查询正在运行的作业
select * from dba_jobs_running;
使用包exec dbms_job.submit(:v_num,'a;',sysdate,'sysdate + (10/(24*60*60))')加入作业。间隔10秒钟
exec dbms_job.submit(:v_num,'a;',sysdate,'sysdate + (11/(24*60))')加入作业。间隔11分钟使用包exec dbms_job.remove(21)删除21号作业。
一、什么是Java事务
通常的观念认为,事务仅与数据库相关。
事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)的缩写。事务的原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。一致性表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。隔离性表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。持久性表示已提交的数据在事务执行失败时,数据的状态都应该正确。
通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行。
既然事务的概念从数据库而来,那Java事务是什么?之间有什么联系?
实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到Java程序代码中。因此,数据库操作的事务习惯上就称为Java事务。
二、为什么需要事务
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。具一个简单例子:比如银行转帐业务,账户A要将自己账户上的1000元转到B账户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操走就需要事务,将A账户资金减少和B账户资金增加方到一个事务里面,要么全部执行成功,要么操作全部撤销,这样就保持了数据的安全性。
三、Java事务的类型
Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
1、JDBC事务
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交。 java.sql.Connection 提供了以下控制事务的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
2、JTA(Java Transaction API)事务
JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。
JTA允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
如果计划用 JTA 界定事务,那么就需要有一个实现 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。
您将需要用应用服务器的管理工具设置 XADataSource 。从应用服务器和 JDBC 驱动程序的文档中可以了解到相关的指导。
J2EE 应用程序用 JNDI 查询数据源。一旦应用程序找到了数据源对象,它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的连接。
XA 连接与非 XA 连接不同。一定要记住 XA 连接参与了 JTA 事务。这意味着 XA 连接不支持 JDBC 的自动提交功能。同时,应用程序一定不要对 XA 连接调用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() 。相反,应用程序应该使用 UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() 。
3、容器事务
容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。相对编码实现JTA事务管理,我们可以通过EJB容器提供的容器事务管理机制(CMT)完成同一个功能,这项功能由J2EE应用服务器提供。这使得我们可以简单的指定将哪个方法加入事务,一旦指定,容器将负责事务管理任务。这是我们土建的解决方式,因为通过这种方式我们可以将事务代码排除在逻辑编码之外,同时将所有困难交给J2EE容器去解决。使用EJB CMT的另外一个好处就是程序员无需关心JTA API的编码,不过,理论上我们必须使用EJB。
四、三种事务差异
1、JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。
2、JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
3、容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
五、总结
事务控制是构建J2EE应用不可缺少的一部分,合理选择应用何种事务对整个应用系统来说至关重要。一般说来,在单个JDBC 连接连接的情况下可以选择JDBC事务,在跨多个连接或者数据库情况下,需要选择使用JTA事务,如果用到了EJB,则可以考虑使用EJB容器事务。
参考资料:
《Pro Spring》
摘要: 一、cookie机制和session机制的区别
*************************************************************************************
具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态... 阅读全文
在SQL语句优化过程中,经常会用到hint,
以下是在SQL优化过程中常见Oracle中"HINT"的30个用法1. /*+ALL_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化.
例如:
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
2. /*+FIRST_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳响应时间,使资源消耗最小化.
例如:
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
3. /*+CHOOSE*/
表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得最佳的吞吐量;
表明如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;
例如:
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
4. /*+RULE*/
表明对语句块选择基于规则的优化方法.
例如:
SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
5. /*+FULL(TABLE)*/
表明对表选择全局扫描的方法.
例如:
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='SCOTT';
6. /*+ROWID(TABLE)*/
提示明确表明对指定表根据ROWID进行访问.
例如:
SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>='AAAAAAAAAAAAAA'
AND EMP_NO='SCOTT';
7. /*+CLUSTER(TABLE)*/
提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.
例如:
SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS
WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
8. /*+INDEX(TABLE INDEX_NAME)*/
表明对表选择索引的扫描方法.
例如:
SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';
9. /*+INDEX_ASC(TABLE INDEX_NAME)*/
表明对表选择索引升序的扫描方法.
例如:
SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';
10. /*+INDEX_COMBINE*/
为指定表选择位图访问路经,如果INDEX_COMBINE中没有提供作为参数的索引,将选择出位图索引的布尔组合方式.
例如:
SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS
WHERE SAL<5000000 AND HIREDATE<SYSDATE;
11. /*+INDEX_JOIN(TABLE INDEX_NAME)*/
提示明确命令优化器使用索引作为访问路径.
例如:
SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE
FROM BSEMPMS WHERE SAL<60000;
12. /*+INDEX_DESC(TABLE INDEX_NAME)*/
表明对表选择索引降序的扫描方法.
例如:
SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';
13. /*+INDEX_FFS(TABLE INDEX_NAME)*/
对指定的表执行快速全索引扫描,而不是全表扫描的办法.
例如:
SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';
14. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/
提示明确进行执行规划的选择,将几个单列索引的扫描合起来.
例如:
SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO='SCOTT' AND DPT_NO='TDC306';
15. /*+USE_CONCAT*/
对查询中的WHERE后面的OR条件进行转换为UNION ALL的组合查询.
例如:
SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
16. /*+NO_EXPAND*/
对于WHERE后面的OR 或者IN-LIST的查询语句,NO_EXPAND将阻止其基于优化器对其进行扩展.
例如:
SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
17. /*+NOWRITE*/
禁止对查询块的查询重写操作.
18. /*+REWRITE*/
可以将视图作为参数.
19. /*+MERGE(TABLE)*/
能够对视图的各个查询进行相应的合并.
例如:
SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO
AND A.SAL>V.AVG_SAL;
20. /*+NO_MERGE(TABLE)*/
对于有可合并的视图不再合并.
例如:
SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELECT DPT_NO,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO AND A.SAL>V.AVG_SAL;
21. /*+ORDERED*/
根据表出现在FROM中的顺序,ORDERED使ORACLE依此顺序对其连接.
例如:
SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;
22. /*+USE_NL(TABLE)*/
将指定表与嵌套的连接的行源进行连接,并把指定表作为内部表.
例如:
SELECT /*+ORDERED USE_NL(BSEMPMS)*/ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
23. /*+USE_MERGE(TABLE)*/
将指定的表与其他行源通过合并排序连接方式连接起来.
例如:
SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
24. /*+USE_HASH(TABLE)*/
将指定的表与其他行源通过哈希连接方式连接起来.
例如:
SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
25. /*+DRIVING_SITE(TABLE)*/
强制与ORACLE所选择的位置不同的表进行查询执行.
例如:
SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;
26. /*+LEADING(TABLE)*/
将指定的表作为连接次序中的首表.
27. /*+CACHE(TABLE)*/
当进行全表扫描时,CACHE提示能够将表的检索块放置在缓冲区缓存中最近最少列表LRU的最近使用端
例如:
SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
28. /*+NOCACHE(TABLE)*/
当进行全表扫描时,CACHE提示能够将表的检索块放置在缓冲区缓存中最近最少列表LRU的最近使用端
例如:
SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
29. /*+APPEND*/
直接插入到表的最后,可以提高速度.
insert /*+append*/ into test1 select * from test4 ;
30. /*+NOAPPEND*/
通过在插入语句生存期内停止并行模式来启动常规插入.
insert /*+noappend*/ into test1 select * from test4 ;
摘要: 利用xfire编写webservice的例子,内容如下
1. xfire + spring 发布webservice
2. 利用 javascript 调用发布的webservice
使用xfire+spring发布webservice其实很简单,遵循一下几个步骤即可
1. 想要发布成文webservice的类,必须实现接口
... 阅读全文
xfire+spring 实践记录(一)
客户端代码:
1、将服务端的com.sap.jco.ws.WebServiceSAP.java接口copy到本地
2、新增spring的applicationcontext
applicationContext-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-lazy-init="true">
<bean id="sapWebService" class="org.codehaus.xfire.spring.remoting.XFireClientFactoryBean">
<property name="serviceClass">
<value>com.sap.jco.ws.WebServiceSAP</value>
</property>
<property name="wsdlDocumentUrl">
<value>http://127.0.0.1:88/wssap/services/SAPService?WSDL</value>
</property>
</bean>
</beans>
3、applicationContext.xml中增加
<import resource="classpath:org/codehaus/xfire/spring/xfire.xml" />
<import resource="applicationContext-client.xml"/>
4、调用:
TestService client=(TestService)ServiceFactory.getBeanByName("testWebService");
Map map=client.add(new HashMap(), 1, 2);
System.out.println("map value:"+map.get("p3"));
可以返回结果
关键字: oracle的悲观锁和乐观锁
为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。
数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁。什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做。
先从悲观锁开始说。在SqlServer等其余很多数据库中,数据的锁定通常采用页级锁的方式,也就是说对一张表内的数据是一种串行化的更新插入机制,在任何时间同一张表只会插1条数据,别的想插入的数据要等到这一条数据插完以后才能依次插入。带来的后果就是性能的降低,在多用户并发访问的时候,当对一张表进行频繁操作时,会发现响应效率很低,数据库经常处于一种假死状态。而Oracle用的是行级锁,只是对想锁定的数据才进行锁定,其余的数据不相干,所以在对Oracle表中并发插数据的时候,基本上不会有任何影响。
注:对于悲观锁是针对并发的可能性比较大,而一般在我们的应用中用乐观锁足以。
Oracle的悲观锁需要利用一条现有的连接,分成两种方式,从SQL语句的区别来看,就是一种是for update,一种是for update nowait的形式。比如我们看一个例子。首先建立测试用的数据库表。
- CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))AS SELECT deptno, dname, loc, 1 FROM scott.dept
这里我们利用了Oracle的Sample的scott用户的表,把数据copy到我们的test表中。首先我们看一下for update锁定方式。首先我们执行如下的select for update语句。
- select * from test where id = 10 for update
通过这条检索语句锁定以后,再开另外一个sql*plus窗口进行操作,再把上面这条sql语句执行一便,你会发现sqlplus好像死在那里了,好像检索不到数据的样子,但是也不返回任何结果,就属于卡在那里的感觉。这个时候是什么原因呢,就是一开始的第一个Session中的select for update语句把数据锁定住了。由于这里锁定的机制是wait的状态(只要不表示nowait那就是wait),所以第二个Session(也就是卡住的那个sql*plus)中当前这个检索就处于等待状态。当第一个session最后commit或者rollback之后,第二个session中的检索结果就是自动跳出来,并且也把数据锁定住。不过如果你第二个session中你的检索语句如下所示。
- select * from test where id = 10
也就是没有for update这种锁定数据的语句的话,就不会造成阻塞了。另外一种情况,就是当数据库数据被锁定的时候,也就是执行刚才for update那条sql以后,我们在另外一个session中执行for update nowait后又是什么样呢。比如如下的sql语句。由于这条语句中是制定采用nowait方式来进行检索,所以当发现数据被别的session锁定中的时候,就会迅速返回ORA-00054错误,内容是资源正忙, 但指定以 NOWAIT 方式获取资源。所以在程序中我们可以采用nowait方式迅速判断当前数据是否被锁定中,如果锁定中的话,就要采取相应的业务措施进行处理。
- select * from test where id = 10 for update nowait
那这里另外一个问题,就是当我们锁定住数据的时候,我们对数据进行更新和删除的话会是什么样呢。比如同样,我们让第一个Session锁定住id=10的那条数据,我们在第二个session中执行如下语句。
- update test set value=2 where id = 10
这个时候我们发现update语句就好像select for update语句一样也停住卡在这里,当你第一个session放开锁定以后update才能正常运行。当你update运行后,数据又被你update 语句锁定住了,这个时候只要你update后还没有commit,别的session照样不能对数据进行锁定更新等等。
总之,Oracle中的悲观锁就是利用Oracle的Connection对数据进行锁定。在Oracle中,用这种行级锁带来的性能损失是很小的,只是要注意程序逻辑,不要给你一不小心搞成死锁了就好。而且由于数据的及时锁定,在数据提交时候就不呼出现冲突,可以省去很多恼人的数据冲突处理。缺点就是你必须要始终有一条数据库连接,就是说在整个锁定到最后放开锁的过程中,你的数据库联接要始终保持住。与悲观锁相对的,我们有了乐观锁。乐观锁一开始也说了,就是一开始假设不会造成数据冲突,在最后提交的时候再进行数据冲突检测。在乐观锁中,我们有3种
常用的做法来实现。
[1]第一种就是在数据取得的时候把整个数据都copy到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。
[2]第二种乐观锁的做法就是采用版本戳,这个在Hibernate中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table上建立一个新的column,比如为number型,当你数据每更新一次的时候,版本数就会往上增加1。比如同样有2个session同样对某条数据进行操作。两者都取到当前的数据的版本号为1,当第一个session进行数据更新后,在提交的时候查看到当前数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本号增加1,这个时候当前数据的版本为2。当第二个session也更新了数据提交的时候,发现数据库中版本为2,和一开始这个 session取到的版本号不一致,就知道别人更新过此条数据,这个
时候再进行业务处理,比如整个Transaction都Rollback等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过数据库的Trigger的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用 Trigger。
[3]第三种做法和第二种做法有点类似,就是也新增一个Table的Column,不过这次这个column是采用timestamp型,存储数据最后更新的时间。在Oracle9i以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp的数据精度在Oracle的时间类型中是最高的,精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger或者存储过程中。
Oracle锁表(网上抄来的)
锁定类型
行级锁
表级锁
行级锁
---- 行被排他锁定
----在某行的锁被释放之前,其他用户不能修改此行
----使用 commit 或 rollback 命令释放锁
----Oracle 通过使用 INSERT、UPDATE 和 SELECT…FOR UPDATE 语句自动获取行级锁
SELECT…FOR UPDATE 子句
―在表的一行或多行上放置排他锁
―用于防止其他用户更新该行
―可以执行除更新之外的其他操作
―select * from goods where gid=1001
―for update of gname;
―只有该用户提交事务,其他用户才能够更新gname
FOR UPDATE WAIT 子句
―Oracle9i 中的新增功能
―防止无限期地等待锁定的行
―等待间隔必须指定为数值文字
―等待间隔不能是表达式、赋值变量或 PL/SQL
变量
―select * from goods where gid=1001 for update of gname wait 3
―等待用户释放更新锁的时间为3秒,否则超时。
表级锁
―保护表的数据
―在多个用户同时访问数据时确保数据的完整性
―可以设置为三种模式:共享、共享更新和 排他
语法:lock table<table_name>in<mode>;
共享锁
―锁定表
―仅允许其他用户执行查询操作
―不能插入、更新和删除
―多个用户可以同时在同一表中放置此锁
―lock table table_name
―in share mode [nowait];
― rollback 和commit 命令释放锁
― nowait 关键字告诉其他用户不用等待
共享更新锁
―锁定要被更新的行
―允许其他用户同时查询、插入、更新未被锁定的行
―在 SELECT 语句中使用“FOR UPDATE”子句,可以强制使用共享更新锁
―允许多个用户同时锁定表的不同行
加锁的两种方法
1 lock table tab_name in share update mode;
2 select column1,column2
from goods
where goods
where gid=1001
for update of column1,column2
排他锁
―与其他两种锁相比,排他锁是限制性最强的表锁
―仅允许其他用户查询数据
―不允许执行插入、删除和更新操作
―在同一时间仅允许一位用户在表上放置排他锁
―共享锁与此相反
lock table tab_name in exclusive mode;
lock table<表名>[<表名>]...
in share mode [nowait]
lock table< 表名>[<表名>]...
in exclusive mode [nowait]
lock table<表名>[<表名>]...
in share update mode[nowait]
Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程式对Microsoft Office格式档案读和写的功能。
结构:
HSSF - 提供读写Microsoft Excel格式档案的功能。
XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。
HWPF - 提供读写Microsoft Word格式档案的功能。
HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF - 提供读写Microsoft Visio格式档案的功能。
创建Excel 文档
示例1将演示如何利用Jakarta POI API 创建Excel 文档。
示例1程序如下:
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFCell;
import java.io.FileOutputStream;
public class CreateXL {
/** Excel 文件要存放的位置,假定在D盘下*/
public static String outputFile="D:\\test.xls";
public static void main(String argv[]){
try{
// 创建新的Excel 工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
// 在Excel工作簿中建一工作表,其名为缺省值
// 如要新建一名为"效益指标"的工作表,其语句为:
// HSSFSheet sheet = workbook.createSheet("效益指标");
HSSFSheet sheet = workbook.createSheet();
// 在索引0的位置创建行(最顶端的行)
HSSFRow row = sheet.createRow((short)0);
//在索引0的位置创建单元格(左上端)
HSSFCell cell = row.createCell((short) 0);
// 定义单元格为字符串类型
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
// 在单元格中输入一些内容
cell.setCellValue("增加值");
// 新建一输出文件流
FileOutputStream fOut = new FileOutputStream(outputFile);
// 把相应的Excel 工作簿存盘
workbook.write(fOut);
fOut.flush();
// 操作结束,关闭文件
fOut.close();
System.out.println("文件生成...");
}catch(Exception e) {
System.out.println("已运行 xlCreate() : " + e );
}
}
}
读取Excel文档中的数据
示例2将演示如何读取Excel文档中的数据。假定在D盘JTest目录下有一个文件名为test1.xls的Excel文件。
示例2程序如下:
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFCell;
import java.io.FileInputStream;
public class ReadXL {
/** Excel文件的存放位置。注意是正斜线*/
public static String fileToBeRead="D:\\test1.xls";
public static void main(String argv[]){
try{
// 创建对Excel工作簿文件的引用
HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(fileToBeRead));
// 创建对工作表的引用。
// 本例是按名引用(让我们假定那张表有着缺省名"Sheet1")
HSSFSheet sheet = workbook.getSheet("Sheet1");
// 也可用getSheetAt(int index)按索引引用,
// 在Excel文档中,第一张工作表的缺省索引是0,
// 其语句为:HSSFSheet sheet = workbook.getSheetAt(0);
// 读取左上端单元
HSSFRow row = sheet.getRow(0);
HSSFCell cell = row.getCell((short)0);
// 输出单元内容,cell.getStringCellValue()就是取所在单元的值
System.out.println("左上端单元是: " + cell.getStringCellValue());
}catch(Exception e) {
System.out.println("已运行xlRead() : " + e );
}
}
}
设置单元格格式
在这里,我们将只介绍一些和格式设置有关的语句,我们假定workbook就是对一个工作簿的引用。在Java中,第一步要做的就是创建和设置字体和单元格的格式,然后再应用这些格式:
1、创建字体,设置其为红色、粗体:
HSSFFont font = workbook.createFont();
font.setColor(HSSFFont.COLOR_RED);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
2、创建格式
HSSFCellStyle cellStyle= workbook.createCellStyle();
cellStyle.setFont(font);
3、应用格式
HSSFCell cell = row.createCell((short) 0);
cell.setCellStyle(cellStyle);
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
cell.setCellValue("标题 ");
处理WORD文档
import java.io.*;
import org.textmining.text.extraction.WordExtractor;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFCell;
public class TestPoi {
public TestPoi() {
}
public static void main(String args[]) throws Exception
{
FileInputStream in = new FileInputStream ("D:\\a.doc");
WordExtractor extractor = new WordExtractor();
String str = extractor.extractText(in);
//System.out.println("the result length is"+str.length());
System.out.println(str);
}
}
|
1. 参数文件(parameter file, *.ora)
两种类型pfile和spfile,推荐使用spfile,pfile是字符型文件可以使用编辑器编辑,spfile是二进制文件,要使用set parameter参数进行修改。 使用set parameter进行参数修改时可以增加注释,指定修改的作用范围(memory,spfile,或者both),其中both是默认值;使用reset parameter修改原来的参数设置,其中sid选项必选。
spfile和pfile可以互相生成,启动数据库时默认使用spfile,要使用pfile需要使用pfile='pfilename'进行指定; spfile保存在服务器,pfile保存在客户端,使用alter system改变的系统配置写入spfile,不写入pfile。
2. 调试文件(trace file, * .trc)
两种类型,一种是对调试应用有关系的文件,比如打开sql调试选项,就会在这些调试文件中增加信息;一种是内核产生错误时产生的调试文件,供orace支持人员使用。
我们使用的调试文件有两个存放位置,对于专属服务器存放在udump文件夹下,具体可见user_dump_dest参数;共享服务器的调试文件一般存放在bdump文件夹下,具体可见background_dump_dest参数。
从v$process, v$session, v$parameter, v$instance做关联查询可以知道目前session的trace文件。
可以设置参数tracefile_identifier为当前session的trace文件增加tag。
3. 数据文件(data file)
oracle支持的四种文件系统:os file system, raw partitions,automatic storage management,clustered file system;
段(segment),oracle中的每个对象都存储在一个segment之中,在创建对象的同时oracle自动创建不同的segment,每个段由一个或者多个簇(extents)组成;
簇(extent),extent是一个文件内逻辑上连续的存储空间,同一个segment内的extent可以属于不同的文件,最大空间为2G;
块(block),块是oracle中最小的存储空间,是实际存储数据对象的地方;extent由block构成;一般取值为2k/4k/8k/16k,在有些条件下也可以使用32k。
表空间(tablespace),由segment构成,一个segment不能跨越表空间。
4. 临时文件(temp files)
临时文件用于存放中间结果,它的显著特征是一般情况下,对数据对象的操作不产生回滚段,除非显式指定回滚要求。
5. 控制文件(control files)
控制文件是个小文件,最大64M;在参数文件中指明控制文件的位置;控制文件指明数据文件和在线重做日志文件的位置;控制文件记录发生过的检查点,数据库名字,创建数据库时的时间戳,归档日志的历史等;
控制文件应该在不同的磁盘上进行备份。
6. 日志文件(log files)
一般用于数据库恢复,也可用于系统崩溃时的例程恢复,分为在线日志(online log file)和归档日志(archived log file).
online redo log:
有两组或者多组在线重做日志,组内的每一个成员互为影射,组之间循环使用.
在线重做日志组间的切换称为log switch,这个时候如果数据库配置不好容易发生服务暂停的情况,因为数据库在切换日志组之前要确认该日志组已经没有脏数据,如果有脏数据就要先写入磁盘中,再进行切换。
oracle在修改数据后,会先写入SGA的对应区域,当commit的时候写入在线日志文件,在check point或者重新启动时由DBWn将修改写入磁盘。
设置online redo log的大小时要考虑:峰值的时候产生的日志规模;修改相同block的用户数量;对数据库恢复是否有时间要求。
archived redo log:
数据库在日志的处理分为两种类型:archivelog mode,在发生log switch时保存原来日志;noarchivelog,不保存原有日志,直接重用。
生产环境中要使用archivelog mode,保证数据不丢失。
7. 口令文件(password file)
可选文件,为远程启动oracle提供验证。
8. DMP文件(exp/imp files)
dmp文件用于oracle的导入导出,它和平台无关;是二进制文件;通过网络传输时要确保以二进制的格式进行传输,否则可能导致文件被转换;导入时向下兼容即新版本的oracle可导入老版本的dmp文件。
设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据的一致性和准确性。Oracle数据库封锁方式有三种:共享封锁,独占封锁,共享更新封锁
[b:8f4f63b9bb]封锁类型[/b:8f4f63b9bb]
Oracle RDBMS的封锁类型可分为如下三类:
1、内部级封锁
内部级封锁是用于保护ORACLE内部结构,由系统内部实现,用户不能访问,因此我们不必对此做过多的了解。
2、DDL级封锁(字典/语法分析封锁)
DDL级封锁也是由ORACLE RDBMS来控制,它用于保护数据字典和数据定义改变时的一致性和完整性。它是系统在对SQL定义语句作语法分析时自动地加锁,无需用户干予。字典/语法分析封锁共分三类:
(1)、字典操作锁:用于对字典操作时,锁住数据字典,此封锁是独占的,从而保护任何一个时刻仅能对一个字典操作。
(2)、字典定义锁:用于防止在进行字典操作时又进行语法分析,这样可以避免在查询字典的同时改动某个表的结构。
(3)、表定义锁:用于 一个SQL语句正当访问某个表时,防止字典中与该表有关的项目被修改。
3、DML级封锁
DML级封锁用于控制并发事务中的数据操纵,保证数据的一致性和完整性,其封锁对象可以是表或行。
对用户的数据操纵,Oracle可以自动为操纵的数据进行封锁,但如果有操纵授权,则为满足并发操纵的需要另外实施封锁。DML封锁可由一个用户进程以显式的方式加锁,也可通过某些 SQL语句隐含方式实现。
DML锁有如下三种封锁方式:
(1)、共享封锁方式(SHARE)
(2)、独占封锁方式(EXCLUSIVE)
(3)、共享更新封锁(SHARE UPDATE)
其中SHARE,EXCLUSIVE用于表封锁,SHARE UPDATE用于行封锁。
1、共享方式的表封锁
共享方式的表封锁是对表中的所有数据进行封锁,该锁用于保护查询数据的一致性,防止其它用户对已封锁的表进行更更新。其它用户只能对该表再施加共享方式的锁,而不能再对该表施加独占方式的封锁,共享更新锁可以再施加,但不允许持有共享更新封锁的进程做更新。共享该表的所有用户只能查询表中的数据,但不能更新。共享方式的表封锁只能由用户用SQL语句来设置,基语句格式如下:
[quote:04b72348bd]LOCK TABLE <表名>[,<表名>]...
IN SHARE MODE [NOWAIT]
[/quote:04b72348bd]
执行该语句,对一个或多个表施加共享方式的表封锁。当指定了选择项NOWAIT,若该封锁暂时不能施加成功,则返回并由用户决定是进行等待,还是先去执行别的语句。
持有共享锁的事务,在出现如下之一的条件时,便释放其共享锁:
A、执行COMMIT或ROLLBACK语句。
B、退出数据库(LOG OFF)。
C、程序停止运行。
共享方式表封锁常用于一致性查询过程,即在查询数据期间表中的数据不发生改变。
2、独占方式表封锁
独占方式表封锁是用于封锁表中的所有数据,拥有该独占方式表封锁的用户,即可以查询该表,又可以更新该表,其它的用户不能再对该表施加任何封锁(包括共享、独占或共享更新封锁)。其它用户虽然不能更新该表,但可以查询该表。
独占方式的表封锁可通过如下的SQL语句来显示地获得:
LOCK TABLE <表名>[,<表名>]....
IN EXCLUSIVE MODE [NOWAIT]
独占方式的表封锁也可以在用户执行DML语句INSERT、UPDATE、DELETE时隐含获得。
拥有独占方式表封锁的事务,在出现如下条件之一时,便释放该封锁:
(1)、执行COMMIT或ROLLBACK语句。
(2)、退出数据库(LOG OFF)
(3)、程序停止运行。
独占方式封锁通常用于更新数据,当某个更新事务涉及多个表时,可减少发生死锁。
DML锁有如下三种封锁方式:
(1)、共享封锁方式(SHARE)
(2)、独占封锁方式(EXCLUSIVE)
(3)、共享更新封锁(SHARE UPDATE)
其中SHARE,EXCLUSIVE用于表封锁,SHARE UPDATE用于行封锁。
1、共享方式的表封锁
共享方式的表封锁是对表中的所有数据进行封锁,该锁用于保护查询数据的一致性,防止其它用户对已封锁的表进行更更新。其它用户只能对该表再施加共享方式的锁,而不能再对该表施加独占方式的封锁,共享更新锁可以再施加,但不允许持有共享更新封锁的进程做更新。共享该表的所有用户只能查询表中的数据,但不能更新。共享方式的表封锁只能由用户用SQL语句来设置,基语句格式如下:
[quote:04b72348bd]LOCK TABLE <表名>[,<表名>]...
IN SHARE MODE [NOWAIT]
[/quote:04b72348bd]
执行该语句,对一个或多个表施加共享方式的表封锁。当指定了选择项NOWAIT,若该封锁暂时不能施加成功,则返回并由用户决定是进行等待,还是先去执行别的语句。
持有共享锁的事务,在出现如下之一的条件时,便释放其共享锁:
A、执行COMMIT或ROLLBACK语句。
B、退出数据库(LOG OFF)。
C、程序停止运行。
共享方式表封锁常用于一致性查询过程,即在查询数据期间表中的数据不发生改变。
2、独占方式表封锁
独占方式表封锁是用于封锁表中的所有数据,拥有该独占方式表封锁的用户,即可以查询该表,又可以更新该表,其它的用户不能再对该表施加任何封锁(包括共享、独占或共享更新封锁)。其它用户虽然不能更新该表,但可以查询该表。
独占方式的表封锁可通过如下的SQL语句来显示地获得:
LOCK TABLE <表名>[,<表名>]....
IN EXCLUSIVE MODE [NOWAIT]
独占方式的表封锁也可以在用户执行DML语句INSERT、UPDATE、DELETE时隐含获得。
拥有独占方式表封锁的事务,在出现如下条件之一时,便释放该封锁: <BR>(1)、执行 COMMIT或ROLLBACK语句。
(2)、退出数据库(LOG OFF)
(3)、程序停止运行。
独占方式封锁通常用于更新数据,当某个更新事务涉及多个表时,可减少发生死锁。
3、共享更新封锁方式
共享更新封锁是对一个表的一行或多行进行封锁,因而也称作行级封锁。表级封锁虽然保证了数据的一致性,但却减弱了操作数据的并行性。行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的迸发性。
可通过如下的两种方式来获得行级封锁:
(1)、执行如下的SQL封锁语句,以显示的方式获得:
LOCK TABLE <表名>[,<表名>]....
IN SHARE UPDATE MODE [NOWAIT]
(2)、用如下的SELECT ...FOR UPDATE语句获得:
SELECT <列名>[,<列名>]...
FROM <表名>
WHERE <条件>
FOR UPDATE OF <列名>[,<列名>].....[NOWAIT]
一旦用户对某个行施加了行级封锁,则该用户可以查询也可以更新被封锁的数据行,其它用户只能查询但不能更新被封锁的数据行.如果其它用户想更新该表中的数据行,则也必须对该表施加行级锁.即使多个用户对一个表均使用了共享更新,但也不允许两个事务同时对一个表进行更新,真正对表进行更新时,是以独占方式封锁表,一直到提交或复原该事务为止。行锁永远是独占方式锁。
当出现如下之一的条件,便释放共享更新锁:
(1)、执行提交(COMMIT)语句;
(2)、退出数据库(LOG OFF)
(3)、程序停止运行。
执行ROLLBACK操作不能释放行锁。
从上面讲述可见,ORACLE RDBMS的加锁机制,解决了并发事务的相容与互斥问题。相容保证事务的并发性,互斥确保数据的一致性。不同用户锁的相容与互斥关系由下图给出。
其中最后一行最后一列为其它用户提供在不同行上设置SHARE UPDATE锁。但当用户1在某行上进行更新操作时,用户2只有等待用户1提交事务后,才能更新自己所封锁的行。
中最后一行最后一列为其它用户提供在不同行上设置SHARE UPDATE锁。但当用户1在某行上进行更新操作时,用户2只有等待用户1提交事务后,才能更新自己所封锁的行。
死锁
封锁虽然能够有效的解决并发操作,但是任何资源的独占都会有死锁的危险。例如:有两个事务T1,T2,T1对数据A施加独占封锁,T2对数据B施加了独占封锁。再假设T1要对数据B加锁,由于B已被T2独占封锁,因此T1置于等待状态,等待B被释放;现在若T2也要对A进行封锁,由于A已被T1独占封锁,因此T2也被置于等待状态。这样就形成了两个事务相互等待的状态,而且永远不能结束,此种情况称为死锁。
在Oracle系统中能自动发现死锁,并选择代价最小的,即完成工作量最少的事务予以撤消,释放该事务所拥有的全部锁,记其它的事务继续工作下去。
从系统性能上考虑,应该尽可能减少资源竞争,增大吞吐量,因此用户在给并发操作加锁时,应注意以下几点:
1、对于UPDATE和DELETE操作,应只封锁要做改动的行,在完成修改后立即提交。
2、当多个事务正利用共享更新的方式进行更新,则不要使用共享封锁,而应采用共享更新封锁,这样其它用户就能使用行级锁,以增加并行性。
3、尽可能将对一个表的操作的并发事务施加共享更新锁,从而可提高并行性。
4、在应用负荷较高的期间,不宜对基础数据结构(表、索引、簇和视图)进行修改
java中从oracle中取数据,转化为String和Double显示:
String------->.11
Double------->1.23456446412E10
decode(substr(to_char(CHARGE_AMOUNT/100),1,1),'.','0'||to_char(CHARGE_AMOUNT/100),to_char(CHARGE_AMOUNT/100)) chargeAmountYuan
create table test(name varchar2(8), password varchar2(12));
comment on table test is '表备注';
comment on column test.name is '字段备注';
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
关联的两张表,当数据少的那张要在数据多的那张表字段里,显示为NULL时,就需要使用外连接。
在讲外连接之前,先举例介绍内连接,也就是一般的相等连接。
点击下载:理解oracle中的外连接.doc
select * from a, b where a.id = b.id;
对于外连接,Oracle中可以使用“(+)”来表示,9i可以使用LEFT/RIGHT/FULL OUTER JOIN,下面将配合实例一一介绍。
1. LEFT OUTER JOIN:左外关联
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id);
等价于
SELECT e.last_name, e.department_id, d.department_name
FROM employees e, departments d
WHERE e.department_id=d.department_id(+);
结果为:所有员工及对应部门的记录,包括没有对应部门编号department_id的员工记录。
2. RIGHT OUTER JOIN:右外关联
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
RIGHT OUTER JOIN departments d
ON (e.department_id = d.department_id);
等价于
SELECT e.last_name, e.department_id, d.department_name
FROM employees e, departments d
WHERE e.department_id(+)=d.department_id;
结果为:所有员工及对应部门的记录,包括没有任何员工的部门记录。
3. FULL OUTER JOIN:全外关联
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
FULL OUTER JOIN departments d
ON (e.department_id = d.department_id);
结果为:所有员工及对应部门的记录,包括没有对应部门编号department_id的员工记录和没有任何员工的部门记录。
有关oracle 9i中的内连接,左外连接,右外连接问题
1. 内连接很简单
select A.*, B.* from A,B where A.id = B.id
select A.*, B.* from A inner join B on A.id = B.id
以上两句是完全等价的
2. 左外连接
select distinct(p.person_id) from t_pbase_info p, t_pcontact_info c where p.person_id = c.person_id(+)
select distinct(p.person_id) from t_pbase_info p left join t_pcontact_info c on p.person_id = c.person_id
以上两句是完全等价的
3. 右外连接
select distinct(p.person_id) from t_pbase_info p, t_pcontact_info c where p.person_id(+) = c.person_id
select distinct(p.person_id) from t_pbase_info p right join t_pcontact_info c on p.person_id = c.person_id
以上两句是完全等价的
也就是说在oracle中+号放在=右边相当于左连接,而+号放在=左边相当于右连接
在处理数据时发现一个问题,Oracle对于小于1的小数,小数点前面的0是不显示的。
SQL> create table t1_number (num number);
Table created
SQL> insert into t1_number values(0.3268);
1 row inserted
SQL> insert into t1_number values(0.57965686);
1 row inserted
SQL> insert into t1_number values(52345234.5686);
1 row inserted
SQL> insert into t1_number values(4.552686);
1 row inserted
SQL> commit;
Commit complete
SQL> column num format 99999999999.9999999999999
SQL> select * from t1_number;
NUM
--------------------------
.3268000000000
.5796568600000
52345234.5686000000000
4.5526860000000
使用PL/SQL DEV工具查看时是有前面的0的
通过column num format 000000000000.99999999999设置也可以显示前面的0
但是这些都只是显示的结果,而不是数据库保存结果
SQL> column num format 00000000000.9999999999999
SQL> select * from t1_number;
NUM
--------------------------
00000000000.3268000000000
00000000000.5796568600000
00052345234.5686000000000
00000000004.5526860000000
00000000005.2920000000000
SQL> select substr(num,1,1) from t1_number;
SU
--
.
.
5
4
5
可以看到,无论前面出现多少位,通过substr函数获取的第一位任然是'.'
无论怎么转换,只要最终输出结果是数值型的,小数点前的0必然被丢弃,所以只有将数值转换为字符型,大致有两种方法:
1、直接使用to_char函数
首先实验一下将小数点前和小数点后都加上99999999的格式:
SQL> select to_char(num,'999999999.999999999') from t1_number;
TO_CHAR(NUM,'9999999
--------------------
.326800000
.579656860
52345234.568600000
4.552686000
5.292000000
发现小数点前还是没有0,而小数点后的0多出来了
再实验一下小数点前和小数点后都加上0000000000格式:
SQL> select to_char(num,'00000000000.000000000') from t1_number;
TO_CHAR(NUM,'000000000
----------------------
00000000000.326800000
00000000000.579656860
00052345234.568600000
00000000004.552686000
00000000005.292000000
发现小数点前是有0了,但是两边的0都多出来了,使用FM来去掉多于的0:
SQL> select to_char(num,'fm00000000000.000000000') from t1_number;
TO_CHAR(NUM,'FM0000000
----------------------
00000000000.326800000
00000000000.579656860
00052345234.568600000
00000000004.552686000
00000000005.292000000
没有效果,说明FM只能去掉用9表示的格式中产生的0:
SQL> select to_char(num,'fm999999999.999999999') from t1_number;
TO_CHAR(NUM,'FM99999
--------------------
.3268
.57965686
52345234.5686
4.552686
5.292
但是这样还是没有0,最后发现可以将小数点前的第一位置为0即可(注意9的个数要大于数值的位数):
SQL> select to_char(num,'fm999999990.999999999') from t1_number;
TO_CHAR(NUM,'FM99999
--------------------
0.3268
0.57965686
52345234.5686
4.552686
5.292
检查一下第一位,没有问题:
SQL> select substr(to_char(num,'fm999999990.999999999'),1,1) from t1_number;
SU
--
0
0
5
4
5
2、使用decode函数
既然小于1的小数首位必然是'.',那就判断首位是否为'.',是则在前面加上'0'即可
SQL> select decode(substr(num,1,1),'.','0'||num,num) from t1_number
DECODE(SUBSTR(NUM,1,1),'.','0'||NUM,NUM)
-----------------------------------------
0.3268
0.57965686
52345234.5686
4.552686
5.292
同样检查一下第一位,没有问题
SQL> select substr(decode(substr(num,1,1),'.','0'||num,num),1,1) fro
m t1_number;
SU
--
0
0
5
4
5
也可以写个函数来判断一下,不过可以用decode直接出来,感觉也没什么必要,暂时就这些吧。
declare
i integer;
begin
i := 1;
loop
insert into trade_operation_check
(ID, pay_charge, pay_manner, operate_sign)
values
(i,4,'SY','N');
i := i + 1;
exit when i > 10;
end loop;
commit;
end;
数据库中 用户a 访问 用户b 中表t_department
首先在 库a中建立DBLinks 来链接两个库具体如下:
create database link TESTLINK1
connect to ACSYS//库b的某个用户名
identified by "djzcq"//对应的密码
using 'b';//所在服务器的库命名
然后建立同义词 具体如下:
CREATE SYNONYM user1.t_dpt_01 FOR acsys.t_department@TESTLINK1;//t_department是库b中的表名
commit;
最后可以在库a中访问到库b中的表了,具体如下:
select * from t_dpt_01 ;
值得主意的是:只能访问指定的用户所拥有访问权限的表,例如库a的用户user1只能访问库b中用户acsys所拥有权限的表或其它对象。
object.equals比较的是两个object的引用是否相等。
string用==比较的是引用是否相等。
string.equals比较的是两个字符串的值是否相等.
1.首先需要在web.xml中添加tld
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/gfs.tld</taglib-uri>
<taglib-location>/WEB-INF/gfs.tld</taglib-location>
</taglib>
2.其次需要新建 gfs.tld
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>hyt</shortname>
<uri>http://jakarta.apache.org/struts/tags-html</uri>
<tag>
<name>optionDict</name>
<tagclass>com.alipay.gfs.web.jsptag.OptionDictValue</tagclass>
<bodycontent>JSP</bodycontent>
<attribute>
<name>codeId</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>subcodeId</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>subsubcodeId</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>optionEaccount</name>
<tagclass>com.alipay.gfs.web.jsptag.OptionEaccountValue</tagclass>
<bodycontent>JSP</bodycontent>
</tag>
</taglib>
3.再次新建OptionDictValue
package com.alipay.gfs.web.jsptag;
import java.util.List;
import javax.servlet.jsp.JspException;
import org.apache.struts.taglib.html.Constants;
import org.apache.struts.taglib.html.OptionsCollectionTag;
import org.apache.struts.taglib.html.SelectTag;
import org.apache.struts.util.RequestUtils;
import org.apache.struts.util.ResponseUtils;
import com.alipay.gfs.common.GlobalVar;
import com.alipay.gfs.dao.DictDao;
import com.alipay.gfs.domain.Dict;
public class OptionDictValue extends OptionsCollectionTag {
protected String codeId = "";
protected String subcodeId = null;
protected String subsubcodeId = null;
public String getCodeId() {
return codeId;
}
public void setCodeId(String codeId) {
this.codeId = codeId;
}
public String getSubcodeId() {
return subcodeId;
}
public void setSubcodeId(String subcodeId) {
this.subcodeId = subcodeId;
}
public String getSubsubcodeId() {
return subsubcodeId;
}
public void setSubsubcodeId(String subsubcodeId) {
this.subsubcodeId = subsubcodeId;
}
public int doStartTag() throws JspException {
// Acquire the select tag we are associated with
SelectTag selectTag = (SelectTag) pageContext
.getAttribute(Constants.SELECT_KEY);
if (selectTag == null) {
JspException e = new JspException(messages
.getMessage("optionsCollectionTag.select"));
RequestUtils.saveException(pageContext, e);
throw e;
}
DictDao dao = (DictDao)GlobalVar.wac.getBean("dictDao");
Dict dict = new Dict();
dict.setCodeId(codeId);
dict.setSubcodeId(subcodeId);
dict.setSubsubcodeId(subsubcodeId);
List lstCode = dao.getCodes(dict);
StringBuffer sb = new StringBuffer();
for(int i=0; i<lstCode.size();i++)
{
Dict dct = (Dict)lstCode.get(i);
addOption(sb, dct.getCodeName(), dct.getCodeValue(), selectTag
.isMatched(dct.getCodeValue()));
}
ResponseUtils.write(pageContext, sb.toString());
return SKIP_BODY;
}
}
4.使用标签
<html:select property="revenueTypeTwo">
<html:option value="">请选择</html:option>
<gfs:optionDict codeId="102" subsubcodeId="notnull"></gfs:optionDict>
</html:select>
其实以前也有写过自定义标签, 但是没有注意到过<rtexprvalue>的用法, 最近这几天又用上自定义标签了, 突然发现<rtexprvalue>的用法是有讲究的.
rtexprvalue的全称是 Run-time Expression Value, 它用于表示是否可以使用JSP表达式.
当在<attribute>标签里指定<rtexprvalue>true</rtexprvalue>时, 表示该自定义标签的某属性的值可以直接指定或者通过动态计算指定, example as follow:
<sql:query var="result" >
select * from mytable order by nameid
</sql:query>
<%request.setAttribute("nameid", "2"); %>
<myTag:cupSize cupSize="1" cupSizes="${result}"></myTag:cupSize>
<myTag:cupSize cupSize="${nameid}" cupSizes="${result}"></myTag:cupSize>
当在<attribute>标签里指定<rtexprvalue>false</rtexprvalue>时, 表示该自定义标签的某属性的值只能直接指定, example as follow:
<myTag:cupSize cupSize="1" cupSizes="${result}"></myTag:cupSize>
1. 在struts-config.xml里,以插件的形式
xml 代码
- < plug-in className="org.springframework.web.struts.ContextLoaderPlugIn" / >
- < set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" / >
- < / plug-in >
这种方式如果没有配置contextConfigLocation的值,则会自动加载xx-servlet.xml.
xx的值是和web.xml里的配置org.apache.struts.action.ActionServlet的servlet-name的值一样
如下:xx的值也就是 action,所以会自动加载action-servlet.xml
xml 代码
- < servlet >
- < servlet-name >action< / servlet-name >
- < servlet-class >org.apache.struts.action.ActionServlet< / servlet-class >
- < load-on-startup >1< / load-on-startup >
- < / servlet >
- < servlet-mapping >
- < servlet-name >action< / servlet-name >
- < url-pattern >*.do< / url-pattern >
- < / servlet-mapping >
如果sturts-config.xml里配置了contextConfigLocation的值,那么就不会自动加载xx-servlet.xml了,而只会加载contextConfigLocation所指定的xml.
2. 第2种方式
在web.xml里配置Listener
xml 代码
- <listener>
- < listener-class>org.springframework.web.context.ContextLoaderListener<listener-class>
- < / listener>
如果在web.xml里给该Listener指定要加载的xml,如:
xml 代码
- <context-param>
- <param-name>contextConfigLocationparam-name>
- <param-value>classpath*:spring/*.xmlparam-value>
- context-param>
则会去加载相应的xml,而不会去加载/WEB-INF/下的applicationContext.xml。。但是,如果没有指定的话,默认会去/WEB-INF/下加载applicationContext.xml。
3. 第三种方式:ContextLoaderServlet
xml 代码
- < servlet>
- < servlet-name>context< / servlet-name>
- < servlet-class>org.springframework.web.context.ContextLoaderServlet< / servlet-class>
- < load-on-startup>1< / load-on-startup>
- < / servlet>
这种方式和第二种Listener方式一样,唯一的区别就是用Listener方式初始化ApplicationContext,可以和用第一种方式(struts-config.xml里 plugin方式)同时存在,而ContextLoaderServlet则不可以和第一种方式同时存在
总结:
ContextLoaderServlet已经不推荐用了,它只是为了兼容低版本的servlet.jar才用的。
总的来说:Listerner要比Servlet更好一些,而且Listerner监听应用的启动和结束,而Servlet启动要稍微延迟一些。
用以下方法取得ApplicationContext:
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
在文件 tnsnames.ora 下增加GFS =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.2.4.95)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = pay)
)
)
此文件在%oracle_home%\product\10.1.0\Db_1\NETWORK\ADMIN
服务启动的是后台进程,这类似 于Unix中的守护进程。当Oracle数据库服务启动时,系统中没有典型的Oracle进程运行。相反,该服务进程等待来自SQL*Plus的最初连 接,这引起一个前台进程启动并创建该系统全局区域SGA的后台进程。当Oracle数据库关闭时,所有创建的进程被终止。但是该进程自身仍在继续运行,等 待下一个连接请求和启动命令。这就是当我们通过Windows中的Oracle Service启动了数据库实 例,但在SQL*Plus或Oracle企业管理器中关闭了数据库实例后,然而Windows中的OracleService服务仍处于“已启动”状态的 原因。只有停止Windows中的Oracle Service服务(即进程),才能保证不启动数据库。当然,当OracleService服务停止后,还可通过其他方式启动数据库实例。有多种方式可实 现对Windows中有关Oracle服务的管理,具体内容可详见后面相关章节。
Oracle 网络监听器(Net Listener)是另一个重要的服务。该服务必须在用户能连接到Oracle数据库之前启动运行。该进程关闭与否并不影响已经在客户端与数据库之间建立 起的连接和用户对数据库的使用。以SQL*Plus为例,当Oracle网络监听器的服务OracleTNSListener启动之前,启动 SQL*Plus并连接至数据库,系统出现错误信息“ORA-12541:TNS:nolistener”。原因是没有启动监听服务或者监听器损坏;当 Oracle网络监听器的服务启动SQL*Plus并连接至数据库后,若停止OracleTNSListener服务,则对SQL*Plus没有任何影 响,只要没有断开连接,SQL*Plus仍可与数据库进行数据交互。其中,TNS(Transparent Network Substrate)代表透明网络层。
2.可用的Oracle服务
根据安装的产品不同,在Windows中产生的Oracle服务也不同,其实也正是这一点说明了数据库服务器与其所使用的操作系统是紧密相关的。
Oracle Database 10g安装后生成多个服务,这些都是Oracle Database 10g运行中所必需的。
通常当创建数据库并完成安装后,以下两个主要服务会自动启动:
·Oracle Service (Oracle数据库服务)
·OracleTNSListener (Oracle数据库监听服务)
如果安装了Oracle Enterprise Manager Database
Control,则OracleDBConsole服务自动启动。若配置了Automatic Storage
Management,则OracleCSService和OracleASMService+ASM服务也会出现在服务列表中。
与Oracle 10g不同,Oracle9i安装完成后产生12个与其有关的服务,为便于比较,下面是几个与Oracle9i数据库相关的服务:
(1)Oracle Service
数据库服务。该服务为数据库实例系统标识符SID而创建,SID是Oracle安装期间输入的数据库服务名字(如OracleServiceTEST)。该服务是强制性的,它担负着启动数据库实例的任务。
如果没有启动该服务,则当使用任何Oracle工具如SQL*Plus时,将出现ORA-12560的错误信息提示。该信息内容是“ORA-12560 TNS:
protocol adapter error”,这也意味着数据库管理系统的管理对象没有启动,即数据库没有工作。当系统中安装了多个数据库时,会有多个Oracle Service,SID会因数据库不同而不同。一般将服务的启动类型设置为“自动”,这样,当计算机系统启动后该服务自动启动。
(2)OracleTNSListener
监听器服务。例如,Oracle
OraHome92TNSListener 承担着监听并接受来自客户端应用程序的连接请求的任务。当Windows计算机重新启动后,该服务将自动启动。如果该服务没有启动,那么当你使用 Oracle企业管理器控制台或一些图形化的工具进行连接时,将出现错误信息“ORA-12541 TNS: no listener”。但对一般的连接并无影响。例如,在MS-DOS提示符中用sqlplus/ @net_service_name形式启动SQL*Plus并进行连接时,则不会出现错误信息提示。一般将该服务的启动类型设置为“自动”,这样,当计 算机系统启动后该服务自动启动。也可通过手动方式启动服务:C:\>net start OracleTNSListener。
注意 在连接上出现的问题,多数都与监听器有关。
(3)OracleAgent
代理服务。该服务是Oracle企业管理器产品的一部分。执行作业和监视Oracle服务性能及监听器、数据库、Oracle HTTP
Server和Oracle应用程序等目标需要使用智能代理(Intelligent Agent)。智能代理还为Capacity
Planner和Performance Manager等收集静态数据。Capacity Planner和Performance
Manager是用于Oracle诊断包的数据收集应用程序。一般将该服务的启动类型设置为“自动”,这样,当计算机系统启动后该服务自动启动。
如 果该代理服务没有启动,则在启动OEM Console时,系统无法通过OracleAgent找到数据库所在的节点。因此,在使用Enterprise Manager Console打开控制台时,因无法找到数据库所在的节点而不能显示该数据库。OracleAgent是否启动也影响着数据库的远程连接。
在网络中,Oracle Intelligent
Agent是一个在远程节点上的自治进程。代理作为服务与Oracle数据库服务器驻留在同一个节点上,并提供和完成下列功能:
·提供本地服务或调用依赖于操作系统的服务,以便与本地管理的目标节点交互。
·检查事件,向OEM报告排队的结果事件。
·运行Oracle Enterprise Manager作业,搜集其结果和输出,并为结果排队。
·处理数据集合。
·取消那些由控制台或其他应用程序控制的作业或事件等。
(4)OracleHTTPServer
该服务使用端口号3339,为基于浏览器的企业管理器及资料档案库启动Oracle HTTP Server。它对应于Apache Server,即Web
Server。 它也是运行iSQL*Plus所必需的中间层。可根据实际情况将该服务的启动类型设置为“自动”或“手动”。当设置为“自动”后,Oracle HTTPServer将随着计算机的启动而自动启动;否则,可通过菜单组中的“Start HTTP Server powered by Apache”来启动Oracle HTTP Server。
(5)OracleManagementServer OMS(Oracle Management Server)服务在客户端与所管理目标之间起着集中管理和分布式的控制作用,与代理协同工作,处理监视信息和作业信息并使用管理资料档案库存储其管理数据。
1.修改C:\jboss-4.0.5.GA\server\default\deploy\jbossweb-tomcat55.sar\server.xml
在</host>前面加上<Context path="/gfs" docBase="D:/workspace/GFS/WebContent" reloadable="true"/>
2.删除工程的web-inf/lib下和jboss冲突的包,只要重新更新一下svn就ok
3.更新src目录,增加了com/alipay/gfs/resource包,下面放了spring和ibatis的xml和消息文件
4.删除web-inf目录需按的3中的xml文件,只要更新svn
5.修改web.xml 只要更新svn
6.去掉eclipse启动jboss的时候自动部署
以后,修改jsp,java或者xml文件都必须要重新部署
Windows下常见Oracle服务介绍:
(1)OracleServiceSID
数据库服务,这个服务会自动地启动和停止数据库。如果安装了一个数据库,它的缺省启动类型为自动。服务进程为ORACLE.EXE,参数文件initSID.ora,日志文件SIDALRT.log,控制台SVRMGRL.EXE、SQLPLUS.EXE。
(2)OracleHOME_NAMETNSListener
监听器服务,服务只有在数据库需要远程访问时才需要(无论是通过另外一台主机还是在本地通过 SQL*Net 网络协议都属于远程访问),不用这个服务就可以访问本地数据库,它的缺省启动类型为自动。服务进程为TNSLSNR.EXE,参数文件 Listener.ora,日志文件listener.log,控制台LSNRCTL.EXE,默认端口1521、1526。
(3)OracleHOME_NAMEAgent
OEM代理服务,接收和响应来自OEM控制台的任务和事件请求,只有使用OEM管理数据库时才需要,它的缺省启动类型为自动。服务进程为DBSNMP.EXE,参数文件snmp_rw.ora,日志文件nmi.log,控制台LSNRCTL.EXE,默认端口1748。
(4)OracleHOME_NAMEClientCache
名字缓存服务,服务缓存用于连接远程数据库的Oracle Names 数据。它的缺省启动类型是手动。然而,除非有一台Oracle Names 服务器,否则没有必要运行这个服务。服务进程为ONRSD.EXE,参数文件NAMES.ORA,日志文件ONRSD.LOG,控制台 NAMESCTL.EXE。
(5)OracleHOME_NAMECMAdmin
连接管理服务,是构建Connection Manager服务器所用,只有服务器作为Connection Manager才需要,它的缺省启动类型是手动。服务进程为CMADMIN.EXE,参数文件CMAN.ORA,日志文件CMADM_PID.TRC,控制台CMCTL.EXE,默认端口1830。
(6)OracleHOME_NAMECMan
连接网关服务,是构建Connection Manager服务器所用,只有服务器作为Connection Manager才需要,它的缺省启动类型是手动。服务进程为CMGW.EXE,参数文件CMAN.ORA,日志文件CMAN_PID.TRC,控制台 CMCTL.EXE,默认端口1630。
(7)OracleHOME_NAMEDataGatherer
性能包数据采集服务,除非使用Oracle Capacity Planner 和 Oracle Performance Manager,否则不需要启动,它的缺省启动类型是手动。服务进程为VPPDC.EXE,日志文件alert_dg.log,控制台 vppcntl.exe。
(8)OracleHOME_NAMEHTTPServer
Oracle 提供的WEB服务器,一般情况下我们只用它来访问Oracle Apache 目录下的Web 页面,比如说JSP 或者modplsql 页面。除非你使用它作为你的HTTP服务,否则不需要启动(若启动它会接管IIS的服务),它的缺省启动类型是手动。服务进程为APACHE.EXE,参数文件 httpd.conf,默认端口80。
(9)OracleHOME_NAMEPagingServer
通过一个使用调制解调器的数字传呼机或者电子邮件发出警告(没试过),它的缺省启动类型是手动。服务进程PAGNTSRV.EXE,日志文件paging.log。
(10)OracleHOME_NAMENames
Oracle Names服务,只有服务器作为Names Server才需要,它的缺省启动类型是手动。服务进程NAMES.EXE,参数文件NAMES.ORA,日志文件NAMES.LOG,控制台NAMESCTL.EXE,默认端口1575。
(11)OracleSNMPPeerMasterAgent
SNMP服务代理,用于支持SNMP的网管软件对服务器的管理,除非你使用网管工具监控数据库的情况,否则不需要启动,它的缺省启动类型是手动。服务进程为AGNTSVC.EXE,参数文件MASTER.CFG,默认端口161。
(12)OracleSNMPPeerEncapsulater
SNMP协议封装服务,用于SNMP协议转换,除非你使用一个不兼容的SNMP代理服务,否则不需要启动,它的缺省启动类型是手动。服务进程为ENCSVC.EXE,参数文件ENCAPS.CFG,默认端口1161。
(13)OracleHOME_NAMEManagementServer
OEM管理服务,使用OEM时需要,它的缺省启动类型是手动。服务进程为OMSNTSVR.EXE,日志文件oms.nohup。
Oracle中提供了sequence对象,由系统提供自增长的序列号,通常用于生成数据库数据记录的自增长主键或序号的地方.下面就主要介绍一下关于sequence对象的生成,修改,删除等常用的操作:
1. 生成 Sequence
首先用户要有CREATE SEQUENCE或者CREATE ANY SEQUENCE权限.然后使用下面命令生成sequence对象:
CREATE SEQUENCE emp_sequence
INCREMENT BY 1 -- 每次加几个
START WITH 1 -- 从1开始计数
NOMAXVALUE -- 不设置最大值
NOCYCLE -- 一直累加,不循环
CACHE 10 ;
[说明]
如果指定CACHE值,ORACLE就可以预先在内存里面放置一些sequence,这样存取的快些。cache里面的取完后,oracle自动再取一组到cache。 使用cache或许会跳号, 比如数据库突然不正常down掉(shutdown abort),cache中的sequence就会丢失. 所以可以在create sequence的时候用nocache防止这种情况。
一旦定义了emp_sequence,你就可以用CURRVAL,NEXTVAL 来使用 sequence:
sequence.CURRVAL -- 返回 sequence的当前值
sequence.NEXTVAL -- 增加sequence的值,然后返回 sequence 值
[说明]
第一次NEXTVAL返回的是初始值;随后的NEXTVAL会自动增加你定义的INCREMENT BY值,然后返回增加后的值。CURRVAL 总是返回当前SEQUENCE的值,但是在第一次NEXTVAL初始化之后才能使用CURRVAL,否则会出错。一次NEXTVAL会增加一次 SEQUENCE的值,所以如果你在同一个语句里面使用多个NEXTVAL,其值就是不一样的。
2.修改 Sequence
用户或者是该sequence的owner,或者有ALTER ANY SEQUENCE 权限才能改动sequence. 可以alter除start至以外的所有sequence参数.
如果想要改变start值,必须 drop sequence 再 re-create.
命令格式如下:
ALTER SEQUENCE emp_sequence
INCREMENT BY 10
MAXVALUE 10000
CYCLE -- 到10000后从头开始
NOCACHE ;
3. 删除 Sequence
DROP SEQUENCE order_seq;
1. 基本语法
CSS的定义是由三个部分构成:选择符(selector),属性(properties)和属性的取值(value)。
基本格式如下:
selector {property: value}
(选择符 {属性:值})
选择符是可以是多种形式,一般是你要定义样式的HTML标记,例如BODY、P、TABLE……,你可以通过此方法定义它的属性和值,属性和值要用冒号隔开:
body {color: black}
选择符body是指页面主体部分,color是控制文字颜色的属性,black是颜色的值,此例的效果是使页面中的文字为黑色。
如果属性的值是多个单词组成,必须在值上加引号,比如字体的名称经常是几个单词的组合:
p {font-family: "sans serif"}
(定义段落字体为sans serif)
如果需要对一个选择符指定多个属性时,我们使用分号将所有的属性和值分开:
p {text-align: center; color: red}
(段落居中排列;并且段落中的文字为红色)
为了使你定义的样式表方便阅读,你可以采用分行的书写格式:
p
{
text-align: center;
color: black;
font-family: arial
}
(段落排列居中,段落中文字为黑色,字体是arial)
2. 选择符组
你可以把相同属性和值的选择符组合起来书写,用逗号将选择符分开,这样可以减少样式重复定义:
h1, h2, h3, h4, h5, h6 { color: green }
(这个组里包括所有的标题元素,每个标题元素的文字都为绿色)
p, table{ font-size: 9pt }
(段落和表格里的文字尺寸为9号字)
效果完全等效于:
p { font-size: 9pt }
table { font-size: 9pt }
3. 类选择符
用类选择符你能够把相同的元素分类定义不同的样式,定义类选择符时,在自定类的名称前面加一个点号。假如你想要两个不同的段落,一个段落向右对齐,一个段落居中,你可以先定义两个类:
p.right {text-align: right}
p.center {text-align: center}
然后用不在不同的段落里,只要在HTML标记里加入你定义的class参数:
<p class="right"> 这个段落向右对齐的</p>
<p class="center">这个段落是居中排列的</p>
注意:类的名称可以是任意英文单词或以英文开头与数字的组合,一般以其功能和效果简要命名。
类选择符还有一种用法,在选择符中省略HTML标记名,这样可以把几个不同的元素定义成相同的样式:
.center {text-align: center}
(定义.center的类选择符为文字居中排列)
这样的类可以被应用到任何元素上。下面我们使h1元素(标题1)和p元素(段落)都归为“center”类,这使两个元素的样式都跟随“.center”这个类选择符:
<h1 class="center">
这个标题是居中排列的
</h1>
<p class="center">
这个段落也是居中排列的
</p>
注意:这种省略HTML标记的类选择符是我们经后最常用的CSS方法,使用这种方法,我们可以很方便的在任意元素上套用预先定义好的类样式。
4. ID选择符
在HTML页面中ID参数指定了某个单一元素,ID选择符是用来对这个单一元素定义单独的样式。
ID选择符的应用和类选择符类似,只要把CLASS换成ID即可。将上例中类用ID替代:
<p id="intro">
这个段落向右对齐
</p>
定义ID选择符要在ID名称前加上一个“#”号。和类选择符相同,定义ID选择符的属性也有两种方法。下面这个例子,ID属性将匹配所有id="intro"的元素:
#intro
{
font-size:110%;
font-weight:bold;
color:#0000ff;
background-color:transparent
}
(字体尺寸为默认尺寸的110%;粗体;蓝色;背景颜色透明)
下面这个例子,ID属性只匹配id="intro"的段落元素:
p#intro
{
font-size:110%;
font-weight:bold;
color:#0000ff;
background-color:transparent
}
注意:ID选择符局限性很大,只能单独定义某个元素的样式,一般只在特殊情况下使用。
5. 包含选择符
可以单独对某种元素包含关系定义的样式表,元素1里包含元素2,这种方式只对在元素1里的元素2定义,对单独的元素1或元素2无定义,例如:
table a
{
font-size: 12px
}
在表格内的链接改变了样式,文字大小为12象素,而表格外的链接的文字仍为默认大小。
6. 样式表的层叠性
层叠性就是继承性,样式表的继承规则是外部的元素样式会保留下来继承给这个元素所包含的其他元素。事实上,所有在元素中嵌套的元素都会继承外层元素指定的属性值,有时会把很多层嵌套的样式叠加在一起,除非另外更改。例如在DIV标记中嵌套P标记:
div { color: red; font-size:9pt}
……
<div>
<p>
这个段落的文字为红色9号字
</p>
</div>
(P元素里的内容会继承DIV定义的属性)
注意:有些情况下内部选择符不继承周围选择符的值,但理论上这些都是特殊的。例如,上边界属性值是不会继承的,直觉上,一个段落不会同文档BODY一样的上边界值。
另外,当样式表继承遇到冲突时,总是以最后定义的样式为准。如果上例中定义了P的颜色:
div { color: red; font-size:9pt}
p {color: blue}
……
<div>
<p>
这个段落的文字为蓝色9号字
</p>
</div>
我们可以看到段落里的文字大小为9号字是继承div属性的,而color属性则依照最后定义的。
不同的选择符定义相同的元素时,要考虑到不同的选择符之间的优先级。ID选择符,类选择符和HTML标记选择符,因为ID选择符是最后加上元素上的,所以优先级最高,其次是类选择符。如果想超越这三者之间的关系,可以用!important提升样式表的优先权,例如:
p { color: #FF0000!important }
.blue { color: #0000FF}
#id1 { color: #FFFF00}
我们同时对页面中的一个段落加上这三种样式,它最后会依照被!important申明的HTML标记选择符样式为红色文字。如果去掉!important,则依照优先权最高的ID选择符为黄色文字。
7. 注释
你可以在CSS中插入注释来说明你代码的意思,注释有利于你或别人以后编辑和更改代码时理解代码的含义。在浏览器中,注释是不显示的。CSS注释以"/*" 开头,以"*/" 结尾,如下:
/* 定义段落样式表 */
p
{
text-align: center; /* 文本居中排列 */
color: black; /* 文字为黑色 */
font-family: arial /* 字体为arial */
}
资料引用:http://www.knowsky.com/441346.html
首先需要以下这些jar包:commons-dbcp.jar, commons-logging.jar, commons-pool.jar, ibatis-2.3.0.677.jar, ojdbc14_g.jar, spring.jar
1.sqlMapConfig.xml文件代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<sqlMap resource="user.xml" />
</sqlMapConfig>
2.applicationContext.xml文件代码
<?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.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>oracle.jdbc.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:thin:@10.2.46.41:1521:orcl</value>
</property>
<property name="username">
<value>jpet</value>
</property>
<property name="password">
<value>1234</value>
</property>
</bean>
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>SqlMapConfig.xml</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="userDAO" class="UserDAO">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="sqlMapClient">
<ref local="sqlMapClient" />
</property>
</bean>
<bean id="userDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="userDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
</beans>
3.user.xml文件代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap>
<cacheModel id="oneDayProduct" type="MEMORY">
<flushInterval hours="24" />
<property name="reference-type" value="WEAK" />
</cacheModel>
<resultMap id="result" class="User">
<result property="productId" column="productid" columnIndex="1" />
<result property="name" column="name" columnIndex="2" />
<result property="description" column="descn" columnIndex="3" />
<result property="categoryId" column="category" columnIndex="4" />
</resultMap>
<select id="getUserByName" resultMap="result">
select productid, name,
descn, category from product where productid = #value#
</select>
</sqlMap>
4.User.java文件代码如下
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String productId;
private String categoryId;
private String name;
private String description;
/* JavaBeans Properties */
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId.trim();
}
public String getCategoryId() {
return categoryId;
}
public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
/* Public Methods */
public String toString() {
return getName();
}
}
5.UserDAO.java文件代码
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
public class UserDAO extends SqlMapClientDaoSupport {
public User getUser(String username) throws DataAccessException {
return (User) getSqlMapClientTemplate().queryForObject(
"getUserByName", username);
}
}
6.Test.java文件代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext(
"applicationContext.xml");
UserDAO userDAO = (UserDAO) app.getBean("userDAO");
User user = userDAO.getUser("FI-SW-01");
System.out.println(user);
}
}
摘要: Spring对IBatis提供了完善的内建支持。使用Spring提供的IBatis辅助类,可以大大简化原有的IBatis访问代码。这些辅助类位于org.springframework.orm.ibatis包下,目前Spring可同时支持IBatis1.3.x和2.0。 此外,针对IBatis,Spring也提供了和JdbcTemplate一致的异常处理方式
10.3.1 &... 阅读全文
1.首先必须要有spring.jar, mail.jar, commons-logging.jar, activation.jar这些jar包
2.其次applicationContext.xml配置文件如下:
<?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="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host">
<value>smtp.163.com</value>
</property>
<property name="username">
<value>lihaijun_ssd@163.com</value>
</property>
<property name="password">
<value>******</value>
</property>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.timeout">25000</prop>
</props>
</property>
</bean>
<bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="from">
<value>lihaijun_ssd@163.com</value>
</property>
<property name="to">
<value>lihaijun_ssd@163.com</value>
</property>
<property name="subject">
<value>标题</value>
</property>
<property name="text">
<value>内容</value>
</property>
</bean>
<bean id="orderManager" class="OrderManagerImp">
<property name="mailsender">
<ref bean="mailSender" />
</property>
<property name="message">
<ref bean="mailMessage" />
</property>
</bean>
</beans>
3.OrderManagerImpl代码如下:
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
public class OrderManagerImpl{
private JavaMailSender mailsender;
private SimpleMailMessage message;
public void setMessage(SimpleMailMessage message) {
this.message = message;
}
public void setMailsender(JavaMailSender mailsender) {
this.mailsender = mailsender;
}
public void placeOrder() {
try {
mailsender.send(message);
} catch (MailException ex) {
System.err.println(ex.getMessage());
}
}
}
4.SpringMailTest文件代码如下:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringMailTest {
public static void main(String[] args) {
ApplicationContext bf = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderManager mail = (OrderManager) bf.getBean("orderManager");
mail.placeOrder();
}
}
5.最后要把杀毒软件和防火墙关掉才能正常发送邮件(我的机子要把McAfee的“访问保护”关掉)
一般来说,我们在使用Struts时,如果要在JSP隐式的传值给Action有两种情况:
1、要传的值是FromBean中的一个字段,你说的情况应该就是这种情况,例如需要在Edit页面中保存theID,在Action中执行Update操作时根据ID来更新数据库的值,你可以这样做:
Jsp中的代码为:<html:hidden property="theID" />
提交后,theID的值就会放到FormBean中的theID中,你就可以通过getTheID()来获得这个值。
2、要传的值不是FromBean中的一个字段:
Jsp中的代码为:
<input type="hidden" name="XXX" value="<%=request.getAttribute(XXX)%>">
当然,你应该在Action中就已经就这个值放到了request中,request.setAttribute("XXX",value);,
然后在Action中你才可以通过request.getParameter("XXX");来取得这个值。
补充一点,request.setAttribute("XXX",value);中,value应该是个String,还有,
<input type="hidden" name="XXX" value="<%=request.getAttribute(XXX)%>">
应该改为
<input type="hidden" name="XXX" value="<%=(String)request.getAttribute(XXX)%>">
摘要: Java 语言提供了许多处理日期的工具。其中一些工具在其他语言中使用起来更困难一些。尽管如此,Java 语言提供的那些工具几乎可以做创建日期方面的任何事情,并完全根据您想要的格式对这些日期进行格式化。
当 Java 语言出错时,它会包含一个叫做 Date 的类,这个类对创建和处理日期很有帮助。不幸的是,这个类在支持同步方面表现不是很好... 阅读全文
我记得看的阎宏发表在天极网上的文章上举的例子挺好的,他以一个后花园为例讲解的:
(工厂模式有简单工厂模式,工厂方法模式和抽象工厂模式几种形态。)
一开始只在后花园中种蔬菜类的时候可以用简单工厂模式,由工厂负责生成具体的蔬菜类,
但是如果后花园要引进水果类的时候简单模式就行不通了,因此需要使用工厂方法模式,将产品类族分开。
但是如果后花园的规模继续扩大到地域范围的分割时,比如说一个在北京,一个在上海的时候,工厂方法模式就不够了,因为对两个后花园来说,每个后花园的植物是要被种在一起的,并且两个后花园用工厂方法模式是无法体现其区别的。
我个人认为抽象工厂和工厂方法的最大区别应该是抽象工厂提取了各个工厂方法的共性,并且能够确保工厂方法中产生的产品类能够在一起工作。
在OO设计领域,我们知道前人总结了不少的经验,许多的经验在现代软件工程过程中已经被认为是原则来遵守。下面笔者摘抄几项下文涉及到的OO原则的定义。
OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭。我的理解是,对于一个已有的软件,如果需要扩展,应当在不需修改已有代码的基础上进行。
DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程。我的理解是,对于不同层次的编程,高层次暴露给低层次的应当只是接口,而不是它的具体类。
LoD (迪米特法则,Law of Demeter):只与你直接的朋友通信,而避免和陌生人通信。众所周知类(或模块)之间的通信越少,耦合度就越低,从而更有利于我们对软件的宏观管理。 老子论“圣人之治”有相同的思想,《老子》云:“是以圣人之治,虚其心,实其腹,弱其志,常使民无知无欲。”,又云:“小国寡民,邻国相望,鸡犬之声相 闻,民至老死,不相往来。”。佩服我们的老祖宗,N千年前就想到了西方N千年后才想到的东西,同时也佩服《java与模式》的作者阎宏,可以用中国传统哲 学思想这么生动的说明这一软件设计原则。
简单工厂模式及实例
简单工厂模式又叫静态工厂模式,顾名思义,它是用来实例化目标类的静态类。下面我主要通过一个简单的实例说明简单工厂及其优点。
比 如有个国家的运动员协会,他们是负责登记与注册职业运动员的(就好像我们国家的体育总局,呵呵,无论足球篮球还是乒乓球的运动员都必须在这里注册才能拿到 我们国家职业运动员牌照)。一家体育俱乐部(比如篮球的广东宏远,足球的深圳健力宝)想获得球员为自己俱乐部效力,就必须通过这个运动员协会。
根据DIP我们可以设计一个“运动员”接口,“足球运动员”和“篮球运动员”(还有其他运动员)都实现“运动员”这个接口。而“运动员协会”就是一个简单工 厂类,它负责实例化“运动员”。我们这里的“俱乐部”就是一个客户端(Client),不同的“俱乐部”就是不同的客户端。
对于不同的俱乐部对象(无论是八一还是深圳健力宝),他们都是面向“运动员”接口编程,而不用管是“足球运动员”还是“篮球运动员”,也就是说实现了“运动 员”接口的具体类“足球运动员”无需暴露给客户端。这也满足了DIP。但具体的俱乐部(比如足球的深圳健力宝)如何确保自己获取的是自己想要的运动员(健 力宝俱乐部需要的当然是足球运动员)呢?这就需要“运动员协会”这一工厂类了。俱乐部通过调用“运动员协会”的具体方法,返回不同的实例。这同时也满足了 LoD,也就是“深圳健力宝足球俱乐部”对象不直接与“足球运动员:李毅”对象通信,而是通过他们共同的“朋友”——“国家体育总局”通信。
下面给出各个类的程序,会有助于读者更好的了解笔者之前的介绍。
Code: |
[Copy to clipboard] |
运动员.java
public interface 运动员 {
public void 跑();
public void 跳();
}
足球运动员.java
public class 足球运动员 implements 运动员 {
public void 跑(){
//跑啊跑
}
public void 跳(){
//跳啊跳
}
}
篮球运动员.java
public class 篮球运动员 implements 运动员 {
public void 跑(){
//do nothing
}
public void 跳(){
//do nothing
}
}
体育协会.java
public class 体育协会 {
public static 运动员 注册足球运动员(){
return new 足球运动员();
}
public static 运动员 注册篮球运动员(){
return new 篮球运动员();
}
}
俱乐部.java
public class 俱乐部 {
private 运动员 守门员;
private 运动员 后卫;
private 运动员 前锋;
public void test() {
this.前锋 = 体育协会.注册足球运动员();
this.后卫 = 体育协会.注册足球运动员();
this.守门员 = 体育协会.注册足球运动员();
守门员.跑();
后卫.跳();
}
}
|
|
以上就是简单工厂模式的一个简单实例,读者应该想象不用接口不用工厂而把具体类暴露给客户端的那种混乱情形吧(就好像没了体育总局,各个俱乐部在市场上自己胡乱的寻找仔细需要的运动员),简单工厂就解决了这种混乱。
我 们用OCP看看简单工厂,会发现如果要对系统进行扩展的话治需要增加实现产品接口的产品类(上例表现为“足球运动员”,“篮球运动员”类,比如要增加个 “乒乓球运动员”类),而无需对原有的产品类进行修改。这咋一看好像满足OCP,但是实际上还是需要修改代码的——对,就是修改工厂类。上例中如果增加 “乒乓球运动员”产品类,就必须相应的修改“体育协会”工厂类,增加个“注册乒乓球运动员”方法。所以可以看出,简单工厂模式是不满足OCP的。
工厂方法模式及其实例
谈 了简单工厂模式,下面继续谈谈工厂方法模式。前一节的最末点明了简单工厂模式最大的缺点——不完全满足OCP。为了解决这一缺点,设计师们提出了工厂方法 模式。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接 口的工厂类。下面我们通过修改上一节的实例来介绍工厂方法模式。
我们在不改变产品类(“足球运动员”类和“篮球运动员”类)的情况下,修改下工厂类的结构,如下图所示:
相关代码如下:
Code: |
[Copy to clipboard] |
运动员.java
public interface 运动员 {
public void 跑();
public void 跳();
}
足球运动员.java
public class 足球运动员 implements 运动员 {
public void 跑(){
//跑啊跑
}
public void 跳(){
//跳啊跳
}
}
篮球运动员.java
public class 篮球运动员 implements 运动员 {
public void 跑(){
//do nothing
}
public void 跳(){
//do nothing
}
}
体育协会.java
public interface 体育协会 {
public 运动员 注册();
}
足球协会.java
public class 足球协会 implements 体育协会 {
public 运动员 注册(){
return new 足球运动员();
}
}
篮球协会.java
public class 篮球协会 implements 体育协会 {
public 运动员 注册(){
return new 篮球运动员();
}
}
俱乐部.java
public class 俱乐部 {
private 运动员 守门员;
private 运动员 后卫;
private 运动员 前锋;
public void test() {
体育协会 中国足协 = new 足球协会();
this.前锋 = 中国足协.注册();
this.后卫 = 中国足协.注册();
守门员.跑();
后卫.跳();
}
}
|
|
很明显可以看到,“体育协会”工厂类变成了“体育协会”接口,而实现此接口的分别是“足球协会”“篮球协会”等等具体的工厂类。
这样做有什么好处呢?很明显,这样做就完全OCP了。如果需要再加入(或扩展)产品类(比如加多个“乒乓球运动员”)的话就不再需要修改工厂类了,而只需相应的再添加一个实现了工厂接口(“体育协会”接口)的具体工厂类。
从以上对两种模式的介绍可以了解到,工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。但是,工厂方法模式就一定比简单工厂模式好呢?笔者的答案是不一定。下面笔者将详细比较两种模式。
1. 结构复杂度
从这个角度比较,显然简单工厂模式要占优。简单工厂模式只需一个工厂类,而工厂方法模式的工厂类随着产品类个数增加而增加,这无疑会使类的个数越来越多,从而增加了结构的复杂程度。
2.代码复杂度
代码复杂度和结构复杂度是一对矛盾,既然简单工厂模式在结构方面相对简洁,那么它在代码方面肯定是比工厂方法模式复杂的了。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。
3.客户端编程难度
工厂方法模式虽然在工厂类结构中引入了接口从而满足了OCP,但是在客户端编码中需要对工厂类进行实例化。而简单工厂模式的工厂类是个静态类,在客户端无需实例化,这无疑是个吸引人的优点。
4.管理上的难度
这是个关键的问题。
我 们先谈扩展。众所周知,工厂方法模式完全满足OCP,即它有非常良好的扩展性。那是否就说明了简单工厂模式就没有扩展性呢?答案是否定的。简单工厂模式同 样具备良好的扩展性——扩展的时候仅需要修改少量的代码(修改工厂类的代码)就可以满足扩展性的要求了。尽管这没有完全满足OCP,但笔者认为不需要太拘 泥于设计理论,要知道,sun提供的java官方工具包中也有想到多没有满足OCP的例子啊(java.util.Calendar这个抽象类就不满足 OCP,具体原因大家可以分析下)。
然后我们从维护性的角度分析下。假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时 需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦(对号入座已经是个问题了)。反而简单工厂没有这些麻烦,当多个产品类需要修改是,简单工厂模式 仍然仅仅需要修改唯一的工厂类(无论怎样都能改到满足要求吧?大不了把这个类重写)。
由以上的分析,笔者认为简单工厂模式更好用更方便些。当然这只是笔者的个人看法而已,毕竟公认的,工厂方法模式比简单工厂模式更“先进”。但有时过于先进的东西未必适合自己,这个见仁见智吧。
一、 前言
用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object Module)称为DOM。Sun公司提供了Java API for XML Parsing(JAXP)接口来使用SAX和DOM,通过JAXP,我们可以使用任何与JAXP兼容的XML解析器。
JAXP接口包含了三个包:
(1) org.w3c.dom W3C推荐的用于XML标准规划文档对象模型的接口。
(2) org.xml.sax 用于对XML进行语法分析的事件驱动的XML简单API(SAX)
(3) javax.xml.parsers解析器工厂工具,程序员获得并配置特殊的特殊语法分析器。
二、前提
DOM编程不要其它的依赖包,因为JDK里自带的JDK里含有的上面提到的org.w3c.dom、org.xml.sax 和javax.xml.parsers包就可以满意条件了。
三、使用DOM解析XML文档
我们现在来看看DOM是如何解析XML的吧!同样的,我将从一个简单的不能再简单的例子来说明DOM是如何解析XML文档的,先让我们看看XML是什么内容吧:
<?xml version="1.0" encoding="gb2312"?>
<books>
<book email="zhoujunhui">
<name>rjzjh</name>
<price>jjjjjj</price>
</book>
</books>
简单的不能再简单了。但是该有的都有了,根元素、属性、子节点。好了,能反应问题就行了,下面来看看解析这个XML文件的Java代码吧!
1 public class DomParse {
2 public DomParse(){
3 DocumentBuilderFactory domfac=DocumentBuilderFactory.newInstance();
4 try {
5 DocumentBuilder dombuilder=domfac.newDocumentBuilder();
6 InputStream is=new FileInputStream("bin/library.xml");
7 Document doc=dombuilder.parse(is);
8
9 Element root=doc.getDocumentElement();
10 NodeList books=root.getChildNodes();
11 if(books!=null){
12 for(int i=0;i<books.getLength();i++){
13 Node book=books.item(i);
14 if(book.getNodeType()==Node.ELEMENT_NODE){
15 String email=book.getAttributes().getNamedItem("email").getNodeValue();
16 System.out.println(email);
17 for(Node node=book.getFirstChild();node!=null;node=node.getNextSibling()){
18 if(node.getNodeType()==Node.ELEMENT_NODE){
19 if(node.getNodeName().equals("name")){
20 String name=node.getNodeValue();
21 String name1=node.getFirstChild().getNodeValue();
22 System.out.println(name);
23 System.out.println(name1);
24 }
25 if(node.getNodeName().equals("price")){
26 String price=node.getFirstChild().getNodeValue();
27 System.out.println(price);
28 }
29 }
30 }
31 }
32 }
33 }
34 } catch (ParserConfigurationException e) {
35 e.printStackTrace();
36 } catch (FileNotFoundException e) {
37 e.printStackTrace();
38 } catch (SAXException e) {
39 e.printStackTrace();
40 } catch (IOException e) {
41 e.printStackTrace();
42 }
43 }
44 public static void main(String[] args) {
45 new DomParse();
46 }
47 }
四、 代码解释
先看看这个程序引用类:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
//下面主要是org.xml.sax包的类
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
上面那么简单的代码一看就明白了,但是为了介绍个DOM编程的大概还是来看看这个程序吧:
(1) 得到DOM解析器的工厂实例
DocumentBuilderFactory domfac=DocumentBuilderFactory.newInstance();
得到javax.xml.parsers.DocumentBuilderFactory;类的实例就是我们要的解析器工厂
(2) 从DOM工厂获得DOM解析器
DocumentBuilder dombuilder=domfac.newDocumentBuilder();
通过javax.xml.parsers.DocumentBuilderFactory实例的静态方法newDocumentBuilder()得到DOM解析器
(3) 把要解析的XML文档转化为输入流,以便DOM解析器解析它
InputStream is=new FileInputStream("bin/library.xml");
InputStream是一个接口。
(4) 解析XML文档的输入流,得到一个Document
Document doc=dombuilder.parse(is);
由XML文档的输入流得到一个org.w3c.dom.Document对象,以后的处理都是对Document对象进行的
(5) 得到XML文档的根节点
Element root=doc.getDocumentElement();
在DOM中只有根节点是一个org.w3c.dom.Element对象。
(6)得到节点的子节点
NodeList books=root.getChildNodes();
for(int i=0;i<books.getLength();i++){
Node book=books.item(i);
}
这是用一个org.w3c.dom.NodeList接口来存放它所有子节点的,还有一种轮循子节点的方法,后面有介绍
(7) 取得节点的属性值
String email=book.getAttributes().getNamedItem("email").getNodeValue();
System.out.println(email);
注意,节点的属性也是它的子节点。它的节点类型也是Node.ELEMENT_NODE
(8)轮循子节点
for(Node node=book.getFirstChild();node!=null;node=node.getNextSibling()){
if(node.getNodeType()==Node.ELEMENT_NODE){
if(node.getNodeName().equals("name")){
String name=node.getNodeValue();
String name1=node.getFirstChild().getNodeValue();
System.out.println(name);
System.out.println(name1);
}
if(node.getNodeName().equals("price")){
String price=node.getFirstChild().getNodeValue();
System.out.println(price);
}
}
这段代码的打印输出为:
null
alterrjzjh
jjjjjj
从上面可以看出
String name=node.getNodeValue(); 是一个空值。而
String name1=node.getFirstChild().getNodeValue(); 才是真正的值,这是因为DOM把<name>rjzjh</name>也当作是两层结构的节点,其父节点为<name>节点本身,且它只有一个子节点(如果有属性的话就不止一个了!),子节点是它的值“rjzjh”,所以我们看到上面的结果。
还有,子节点的节点类型也是Node.ELEMENT_NODE型的,node.getNextSibling()方法是取下一个相邻的节点。
五、 DOM结点
DOM是一些节点的集合,由于文档中可能包含有不同类型的信息,所以定义了几种不同类型的节点。DOM中最常见的节点类型有:
(1)元素:
元素是XML的基本构件。元素的子节点可以是其它元素、文本节点或两者都有。元素节点还可以只含有属性这一唯一类型的节点。
(2)属性:
属性节点包含关于元素节点的信息,但它不是元素的子节点
(3)文本:
文本节点文本信息,或干脆是空白的文本。
(4)文档:
文档节点是整个文档中所有其它节点的父节点
元素是一种很重要的类型节点,元素节点可以是其他节点的容器。
|
|
CALENDER
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
留言簿
随笔档案(54)
文章分类
搜索
最新评论
Powered By: 博客园 模板提供:沪江博客
|