<
c3p0-config>
<default-config>
<!--當(dāng)連接池中的連接耗盡的時候
c3p0一次同時獲取的連接數(shù)。Default: 3 -->
<property name="acquireIncrement">3</property>
<!--定義在從數(shù)據(jù)庫獲取新連接失敗后重復(fù)嘗試的次數(shù)。Default: 30 -->
<property name="acquireRetryAttempts">30</property>
<!--兩次連接中間隔時間,單位毫秒。Default: 1000 -->
<property name="acquireRetryDelay">1000</property>
<!--連接關(guān)閉時默認(rèn)將所有未提交的操作回滾。Default: false -->
<property name="autoCommitOnClose">false</property>
<!--
c3p0將建一張名為Test的空表,并使用其自帶的查詢語句進(jìn)行測試。如果定義了這個參數(shù)那么
屬性preferredTestQuery將被忽略。你不能在這張Test表上進(jìn)行任何操作,它將只供
c3p0測試
使用。Default: null-->
<property name="automaticTestTable">Test</property>
<!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數(shù)據(jù)源仍有效
保留,并在下次調(diào)用getConnection()的時候繼續(xù)嘗試獲取連接。如果設(shè)為true,那么在嘗試
獲取連接失敗后該數(shù)據(jù)源將申明已斷開并永久關(guān)閉。Default: false-->
<property name="breakAfterAcquireFailure">false</property>
<!--當(dāng)連接池用完時客戶端調(diào)用getConnection()后等待獲取新連接的時間,超時后將拋出
SQLException,如設(shè)為0則無限期等待。單位毫秒。Default: 0 -->
<property name="checkoutTimeout">100</property>
<!--通過實(shí)現(xiàn)ConnectionTester或QueryConnectionTester的類來測試連接。類名需制定全路徑。
Default: com.mchange.v2.
c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>
<!--指定
c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那么無需設(shè)置,默認(rèn)null即可
Default: null-->
<property name="factoryClassLocation">null</property>
<!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
(文檔原文)作者強(qiáng)烈建議不使用的一個屬性-->
<property name="forceIgnoreUnresolvedTransactions">false</property>
<!--每60秒檢查所有連接池中的空閑連接。Default: 0 -->
<property name="idleConnectionTestPeriod">60</property>
<!--初始化時獲取三個連接,取值應(yīng)在minPoolSize與maxPoolSize之間。Default: 3 -->
<property name="initialPoolSize">3</property>
<!--最大空閑時間,60秒內(nèi)未使用則連接被丟棄。若為0則永不丟棄。Default: 0 -->
<property name="maxIdleTime">60</property>
<!--連接池中保留的最大連接數(shù)。Default: 15 -->
<property name="maxPoolSize">15</property>
<!--JDBC的標(biāo)準(zhǔn)參數(shù),用以控制數(shù)據(jù)源內(nèi)加載的PreparedStatements數(shù)量。但由于預(yù)緩存的statements
屬于單個connection而不是整個連接池。所以設(shè)置這個參數(shù)需要考慮到多方面的因素。
如果maxStatements與maxStatementsPerConnection均為0,則緩存被關(guān)閉。Default: 0-->
<property name="maxStatements">100</property>
<!--maxStatementsPerConnection定義了連接池內(nèi)單個連接所擁有的最大緩存statements數(shù)。Default: 0 -->
<property name="maxStatementsPerConnection"></property>
<!--
c3p0是異步操作的,緩慢的JDBC操作通過幫助進(jìn)程完成。擴(kuò)展這些操作可以有效的提升性能
通過多線程實(shí)現(xiàn)多個操作同時被執(zhí)行。Default: 3-->
<property name="numHelperThreads">3</property>
<!--當(dāng)用戶調(diào)用getConnection()時使root用戶成為去獲取連接的用戶。主要用于連接池連接非
c3p0
的數(shù)據(jù)源時。Default: null-->
<property name="overrideDefaultUser">root</property>
<!--與overrideDefaultUser參數(shù)對應(yīng)使用的一個參數(shù)。Default: null-->
<property name="overrideDefaultPassword">password</property>
<!--密碼。Default: null-->
<property name="password"></property>
<!--定義所有連接測試都執(zhí)行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意:
測試的表必須在初始數(shù)據(jù)源的時候就存在。Default: null-->
<property name="preferredTestQuery">select id from test where id=1</property>
<!--用戶修改系統(tǒng)配置參數(shù)執(zhí)行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>
<!--因性能消耗大請只在需要的時候使用它。如果設(shè)為true那么在每個connection提交的
時候都將校驗(yàn)其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
等方法來提升連接測試的性能。Default: false -->
<property name="testConnectionOnCheckout">false</property>
<!--如果設(shè)為true那么在取得連接的同時將校驗(yàn)連接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>
<!--用戶名。Default: null-->
<property name="user">root</property>
<!--早期的
c3p0版本對JDBC接口采用動態(tài)反射代理。在早期版本用途廣泛的情況下這個參數(shù)
允許用戶恢復(fù)到動態(tài)反射代理以解決不穩(wěn)定的故障。最新的非反射代理更快并且已經(jīng)開始
廣泛的被使用,所以這個參數(shù)未必有用。現(xiàn)在原先的動態(tài)反射與新的非反射代理同時受到
支持,但今后可能的版本可能不支持動態(tài)反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">25</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">0</property>
<user-overrides user="swaldman">
</user-overrides>
</default-config>
<named-config name="dumbTestConfig">
<property name="maxStatements">200</property>
<user-overrides user="poop">
<property name="maxStatements">300</property>
</user-overrides>
</named-config>
</
c3p0-config>
最近的一個項目在Hibernate使用C3P0的連接池,數(shù)據(jù)庫為Mysql。開發(fā)測試沒有問題,在運(yùn)行中每個一段長的空閑時間就出現(xiàn)異常:
- org.hibernate.exception.JDBCConnectionException: could not execute query
- at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:74)
- at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
- .......
- Caused by: com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: No operations allowed after connection closed.Connection was implicitly closed due to underlying exception/error:
-
-
- ** BEGIN NESTED EXCEPTION **
-
- com.mysql.jdbc.CommunicationsException
- MESSAGE: Communications link failure due to underlying exception:
-
- ** BEGIN NESTED EXCEPTION **
-
- java.net.SocketException
- MESSAGE: Broken pipe
-
- STACKTRACE:
-
- java.net.SocketException: Broken pipe
- at java.net.SocketOutputStream.socketWrite0(Native Method)
- ......
- ** END NESTED EXCEPTION **
查看了Mysql的文檔,以及Connector/J的文檔以及在線說明發(fā)現(xiàn),出現(xiàn)這種異常的原因是:
Mysql服務(wù)器默認(rèn)的“wait_timeout”是8小時,也就是說一個connection空閑超過8個小時,Mysql將自動斷開該connection。這就是問題的所在,在C3P0 pools中的connections如果空閑超過8小時,Mysql將其斷開,而C3P0并不知道該connection已經(jīng)失效,如果這時有Client請求connection,C3P0將該失效的Connection提供給Client,將會造成上面的異常。
解決的方法有3種:
- 增加wait_timeout的時間。
- 減少Connection pools中connection的lifetime。
- 測試Connection pools中connection的有效性。
當(dāng)然最好的辦法是同時綜合使用上述3種方法,下面就DBCP和C3P0分別做一說明,假設(shè)wait_timeout為默認(rèn)的8小時
DBCP增加以下配置信息:
validationQuery = "SELECT 1"
testWhileIdle = "true"
timeBetweenEvictionRunsMillis = 3600000
minEvictableIdleTimeMillis = 18000000
testOnBorrow = "true"
C3P0增加以下配置信息:
testConnectionOnCheckin = true
//自動測試的table名稱
automaticTestTable=C3P0TestTable
idleConnectionTestPeriod = 18000
maxIdleTime = 25000
testConnectionOnCheckout = true
在配置文件中要寫成 <property name="minPoolSize"><value>1</value></property> 格式
不能寫成 這樣<property name="properties">
<props>
<prop key="c3p0.initialPoolSize">1</prop>
</props>
</property>
c3p0不能完全識別!!
摘要: import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Fi... 閱讀全文
當(dāng)某個數(shù)據(jù)庫用戶在數(shù)據(jù)庫中插入、更新、刪除一個表的數(shù)據(jù),或者增加一個表的主鍵時或者表的索引時,常常會出現(xiàn)ora-00054:resource busy and acquire with nowait specified這樣的錯誤。
主要是因?yàn)橛惺聞?wù)正在執(zhí)行(或者事務(wù)已經(jīng)被鎖),所有導(dǎo)致執(zhí)行不成功。
1、用dba權(quán)限的用戶查看數(shù)據(jù)庫都有哪些鎖
select t2.username,t2.sid,t2.serial#,t2.logon_time
from v$locked_object t1,v$session t2
where t1.session_id=t2.sid order by t2.logon_time;
如:testuser 339 13545 2009-3-5 17:40:05
知道被鎖的用戶testuser,sid為339,serial#為13545
2、根據(jù)sid查看具體的sql語句,如果sql不重要,可以kill
select sql_text from v$session a,v$sqltext_with_newlines b
where DECODE(a.sql_hash_value, 0, prev_hash_value, sql_hash_value)=b.hash_value
and a.sid=&sid order by piece;
查出來的sql,如: begin :id := sys.dbms_transaction.local_transaction_id; end;
3、kill該事務(wù)
alter system kill session '339,13545';
4、這樣就可以執(zhí)行其他的事務(wù)sql語句了
如增加表的主鍵:
alter table test
add constraint PK_test primary key (test_NO);
Oracle數(shù)據(jù)庫系統(tǒng)根據(jù)初始化參數(shù)文件init.ora中設(shè)置的參數(shù)來配置自身的啟動,每個實(shí)例在啟動之前,首先讀取這些參數(shù)文件中設(shè)置的不同參數(shù)。Oracle系統(tǒng)中的參數(shù),根據(jù)系統(tǒng)使用情況可以簡單分為兩大類,普通參數(shù),也就是Oracle系統(tǒng)正常使用的一些參數(shù),另外一類就是特殊參數(shù),包括三種,過時參數(shù)、強(qiáng)調(diào)參數(shù)和隱藏參數(shù)。隨著Oracle數(shù)據(jù)庫新版本的發(fā)布,相應(yīng)每次都會增加或者刪除一些參數(shù)。如何查詢當(dāng)前版本數(shù)據(jù)庫系統(tǒng)的參數(shù)情況以及當(dāng)前系統(tǒng)版本相對于以前版本增加或者丟棄的那些參數(shù)呢?本文將詳細(xì)介紹如何查詢當(dāng)前系統(tǒng)版本中的各種參數(shù)情況。
一、過時參數(shù)和強(qiáng)調(diào)參數(shù)
Oracle數(shù)據(jù)庫中,系統(tǒng)提供了幾個視圖可以查看系統(tǒng)參數(shù)的情況。視圖V$OBSOLETE_PARAMETER中含有所有的過時(obsolete)和強(qiáng)調(diào)(underscored)參數(shù)。這里首先說明一下什么是Oracle的過時(obsolote)和強(qiáng)調(diào)(underscored)參數(shù),過時參數(shù),顧名思義就是在Oracle以前的版本中存在,但在新版本中已經(jīng)淘汰了的參數(shù),已經(jīng)不再使用;而強(qiáng)調(diào)參數(shù),是指那些在新版本中保留了下來,但是除非特殊需要不希望用戶使用的那些參數(shù)。在視圖V$OBSOLETE_PARAMETER中,包含這些參數(shù)的名稱和一個標(biāo)志字ISSPECIFIED,該標(biāo)志字用來指出這個參數(shù)是否在init.ora文件中已實(shí)際設(shè)置。
下面的SQL腳本列出了當(dāng)前系統(tǒng)中所有的過時參數(shù)名稱以及它們是否在當(dāng)前系統(tǒng)中設(shè)定。
SQL> COL name format a50;
SQL> SELECT name, isspecified FROM v$obsolete_parameter; |
上面談到,Oracle系統(tǒng)并沒有將V$OBSOLETE_PARAMETER視圖中的所有參數(shù)均丟棄,而是將其中的一部分轉(zhuǎn)換為強(qiáng)調(diào)參數(shù),下面就來討論如何查看這些參數(shù)是已被丟棄還是被轉(zhuǎn)換。這可以通過系統(tǒng)視圖X$KSPPO來查看,該視圖中包含一個名為KSPPOFLAG的字段,用來指明該參數(shù)在當(dāng)前版本中是被丟棄還是被強(qiáng)調(diào),如果該值為1,則表示該參數(shù)已被丟棄,該值為2,則表明該參數(shù)現(xiàn)為強(qiáng)調(diào)參數(shù)。
SELECT kspponm,
DECODE(ksppoflg, 1,'Obsolete', 2, 'Underscored')
FROM x$ksppo
ORDER BY kspponm; |
注:該視圖只在sys用戶下可以看到。
二、隱藏參數(shù)
Oracle系統(tǒng)中還有一類參數(shù)稱之為隱藏參數(shù)(hidden parameters),是系統(tǒng)中使用,但Oracle官方?jīng)]有公布的參數(shù),這些參數(shù)可能是那些還沒有成熟或者是系統(tǒng)開發(fā)中使用的參數(shù)。這些參數(shù)在所有Oracle 8i中的_trace_files_public和_lock_sga_areas等等。
下面的查詢可以得到當(dāng)前系統(tǒng)中的所有隱藏參數(shù)(以sys身份登錄):
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx
AND translate(ksppinm,'_','#') like '#%'; |
三、系統(tǒng)當(dāng)前參數(shù)
下面的腳本以英文字母順序列出了系統(tǒng)當(dāng)前使用的所有參數(shù)。在列出的參數(shù)中,如果參數(shù)名稱前面有#這個符號,則表示該參數(shù)沒有明確指定,采用了系統(tǒng)中的默認(rèn)參數(shù)。一般在一個新的Oracle版本安裝完成后,首先運(yùn)行該腳本,則可以生成該版本數(shù)據(jù)庫的標(biāo)準(zhǔn)init.ora文件。
SET pagesize 9000
SET head OFF
SET term OFF
SELECT
DECODE(isdefault, 'TRUE', '# ') ||
DECODE(isdefault, 'TRUE', RPAD(name,43), RPAD(name,45)) ||
' = ' || value
FROM v$parameter
ORDER BY name; |
注意:上面的SQL腳本沒有列出系統(tǒng)中的隱藏參數(shù)
SVRMGR> select * from dba_jobs;
初始化相關(guān)參數(shù)job_queue_processes
alter system set job_queue_processes=39 scope=spfile;//最大值不能超過1000 ;job_queue_interval = 10 //調(diào)度作業(yè)刷新頻率秒為單位
DBA_JOBS describes all jobs in the database.
USER_JOBS describes all jobs owned by the current user
1 select job,what,to_char(last_date,'yyyy-mm-dd HH24:mi:ss'),to_char(next_date,'yyyy-mm-dd HH24:m),interval from dba_jobs where job in (325,295)
2 select job,what,last_date,next_date,interval from dba_jobs where job in (1,3);
查詢job的情況。
show paramter background_dump_dest.
看alter.log 和trace
SVRMGR> select * from dba_jobs;
初始化相關(guān)參數(shù)job_queue_processes
alter system set job_queue_processes=39 scope=spfile;//最大值不能超過1000
job_queue_interval = 10 //調(diào)度作業(yè)刷新頻率秒為單位
DBA_JOBS describes all jobs in the database.
USER_JOBS describes all jobs owned by the current user
1 select job,what,to_char(last_date,'yyyy-mm-dd HH24:mi:ss'),to_char(next_date,'yyyy-mm-dd HH24:m),interval from dba_jobs where job in (325,295)
2 select job,what,last_date,next_date,interval from dba_jobs where job in (1,3);
查詢job的情況。
show paramter background_dump_dest.
看alter.log 和trace
請問我如何停止一個JOB
SQL> exec dbms_job.broken(1,true)
PL/SQL 過程已成功完成。
SQL>commit //必須提交否則無效
啟動作業(yè)
SQL> exec dbms_job.broken(1,false)
PL/SQL 過程已成功完成。
停其他用戶的job
SQL>exec sys.dbms_ijob.broken(98,true);
SQL>commit;
============================
exec dbms_job.broken(:job) 停止
exec dbms_job.broken(186,true) //標(biāo)記位broken
exec dbms_job.broken(186,false)//標(biāo)記為非broken
exec dbms_job.broken(186,false,next_day(sysdate,'monday')) //標(biāo)記為非broken,指定執(zhí)行時間
exec dbms_job.remove(:job);刪除
exec dbms_job.remove(186);
commit;
把一個broken job重新運(yùn)行
三、查看相關(guān)job信息
1、相關(guān)視圖
dba_jobs
all_jobs
user_jobs
dba_jobs_running 包含正在運(yùn)行job相關(guān)信息
創(chuàng)建JOB
variable jobno number;
begin
dbms_job.submit(:jobno, 'statspack.snap;', trunc(sysdate+1/24,'HH'), 'trunc(SYSDATE+1/24,''HH'')', TRUE, :instno);
commit;
end;
print jobno
例如,我們已經(jīng)建立了一個存儲過程,其名稱為my_job,在sql/plus中以scott用戶身份登錄,執(zhí)行如下命令:
sql> variable n number;
sql> begin
dbms_job.submit(:n‘my_job;’,sysdate,
‘sysdate+1/360’);
commit;
end;
Sql> print :n;
系統(tǒng)提示執(zhí)行成功。
Sql> print :n;
系統(tǒng)打印此任務(wù)的編號,例如結(jié)果為300。
簡單例子
一個簡單例子:
創(chuàng)建測試表
SQL> create table TEST(a date);
表已創(chuàng)建。
創(chuàng)建一個自定義過程
SQL> create or replace procedure MYPROC as
2 begin
3 insert into TEST values(sysdate);
4 end;
5 /
過程已創(chuàng)建。
創(chuàng)建JOB
SQL> variable job1 number;
SQL>
SQL> begin
2 dbms_job.submit(:job1,'MYPROC;',sysdate,'sysdate+1/1440'); --每天1440分鐘,即一分鐘運(yùn)行test過程一次
3 end;
4 /
PL/SQL 過程已成功完成。
運(yùn)行JOB
SQL> begin
2 dbms_job.run(:job1);
3 end;
4 /
PL/SQL 過程已成功完成。
SQL> select to_char(a,'yyyy/mm/dd hh24:mi:ss') 時間 from TEST;
時間
-------------------
2001/01/07 23:51:21
2001/01/07 23:52:22
2001/01/07 23:53:24
刪除JOB
SQL> begin
2 dbms_job.remove(:job1);
3 end;
4 /
PL/SQL 過程已成功完成。
=======================================
a、利用dbms_job.run()立即執(zhí)行該job
sql>begin
sql>dbms_job.run(:jobno) 該jobno為submit過程提交時返回的job number
sql>end;
sql>/
b、利用dbms_job.broken()重新將broken標(biāo)記為false
sql>begin
sql>dbms_job.broken (:job,false,next_date)
sql>end;
sql>/
========================================
SQL> create table a(a date);
Table created
創(chuàng)建一個過程
SQL> create or replace procedure test as
2 begin
3 insert into a values(sysdate);
4 end;
5 /
Procedure created
提交作業(yè)
SQL> declare
2 job1 number; //定義一個數(shù)字型變量
3 begin
4 dbms_job.submit(:job1,'test;',sysdate,'sysdate+1/1440'); //按分鐘算一天1440分鐘
5 end;
6 /
PL/SQL procedure successfully completed
job1
---------
4
SQL> commit;
Commit complete
運(yùn)行作業(yè)
SQL> begin
2 dbms_job.run(4);
3 end;
4 /
PL/SQL procedure successfully completed
刪除作業(yè)
SQL> begin
2 dbms_job.remove(4);
3 end;
4 /
PL/SQL procedure successfully completed
SQL> commit;
Commit complete
job change//修改作業(yè)
execute dbms_job.change(186,null,null,'sysdate+3');
execute dbms_job.change(186,'scott.test(update)');
DBA_JOBS
===========================================
字段(列) 類型 描述
JOB NUMBER 任務(wù)的唯一標(biāo)示號
LOG_USER VARCHAR2(30) 提交任務(wù)的用戶
PRIV_USER VARCHAR2(30) 賦予任務(wù)權(quán)限的用戶
SCHEMA_USER VARCHAR2(30) 對任務(wù)作語法分析的用戶模式
LAST_DATE DATE 最后一次成功運(yùn)行任務(wù)的時間
LAST_SEC VARCHAR2(8) 如HH24:MM:SS格式的last_date日期的小時,分鐘和秒
THIS_DATE DATE 正在運(yùn)行任務(wù)的開始時間,如果沒有運(yùn)行任務(wù)則為null
THIS_SEC VARCHAR2(8) 如HH24:MM:SS格式的this_date日期的小時,分鐘和秒
NEXT_DATE DATE 下一次定時運(yùn)行任務(wù)的時間
NEXT_SEC VARCHAR2(8) 如HH24:MM:SS格式的next_date日期的小時,分鐘和秒
TOTAL_TIME NUMBER 該任務(wù)運(yùn)行所需要的總時間,單位為秒
BROKEN VARCHAR2(1) 標(biāo)志參數(shù),Y標(biāo)示任務(wù)中斷,以后不會運(yùn)行
INTERVAL VARCHAR2(200) 用于計算下一運(yùn)行時間的表達(dá)式
FAILURES NUMBER 任務(wù)運(yùn)行連續(xù)沒有成功的次數(shù)
WHAT VARCHAR2(2000) 執(zhí)行任務(wù)的PL/SQL塊
CURRENT_SESSION_LABEL RAW MLSLABEL 該任務(wù)的信任Oracle會話符
CLEARANCE_HI RAW MLSLABEL 該任務(wù)可信任的Oracle最大間隙
CLEARANCE_LO RAW MLSLABEL 該任務(wù)可信任的Oracle最小間隙
NLS_ENV VARCHAR2(2000) 任務(wù)運(yùn)行的NLS會話設(shè)置
MISC_ENV RAW(32) 任務(wù)運(yùn)行的其他一些會話參數(shù)
描述 INTERVAL參數(shù)值
每天午夜12點(diǎn) 'TRUNC(SYSDATE + 1)'
每天早上8點(diǎn)30分 'TRUNC(SYSDATE + 1) + (8*60+30)/(24*60)'
每星期二中午12點(diǎn) 'NEXT_DAY(TRUNC(SYSDATE ), ''TUESDAY'' ) + 12/24'
每個月第一天的午夜12點(diǎn) 'TRUNC(LAST_DAY(SYSDATE ) + 1)'
每個季度最后一天的晚上11點(diǎn) 'TRUNC(ADD_MONTHS(SYSDATE + 2/24, 3 ), 'Q' ) -1/24'
每星期六和日早上6點(diǎn)10分 'TRUNC(LEAST(NEXT_DAY(SYSDATE, ''SATURDAY"), NEXT_DAY(SYSDATE, "SUNDAY"))) + (6×60+10)/(24×60)'
Oracle9i 中job_queue_interval已經(jīng)廢棄(obsoleted),job_queue_processes=10默認(rèn),實(shí)際上,job_queue_interval的值為5,假如實(shí)在要修改interval的值,可以使用_job_queue_interval的隱藏參數(shù)。
轉(zhuǎn):http://java.ccidnet.com/art/3539/20070802/1164285_1.html
在Hibernate中處理批量更新和批量刪除
發(fā)布時間:2007.08.03 06:06 來源:賽迪網(wǎng) 作者:dxaw
批量更新是指在一個事務(wù)中更新大批量數(shù)據(jù),批量刪除是指在一個事務(wù)中刪除大批量數(shù)據(jù)。以下程序直接通過Hibernate API批量更新CUSTOMERS表中年齡大于零的所有記錄的AGE字段:
tx = session.beginTransaction();
Iterator customers=session.find("from Customer c where c.age>0").iterator();
while(customers.hasNext()){
Customer customer=(Customer)customers.next();
customer.setAge(customer.getAge()+1);
}
tx.commit();
session.close();
如果CUSTOMERS表中有1萬條年齡大于零的記錄,那么Session的find()方法會一下子加載1萬個Customer對象到內(nèi)存。當(dāng)執(zhí)行tx.commit()方法時,會清理緩存,Hibernate執(zhí)行1萬條更新CUSTOMERS表的update語句:
update CUSTOMERS set AGE=? …. where ID=i;
update CUSTOMERS set AGE=? …. where ID=j;
……
update CUSTOMERS set AGE=? …. where ID=k;
以上批量更新方式有兩個缺點(diǎn):
(1) 占用大量內(nèi)存,必須把1萬個Customer對象先加載到內(nèi)存,然后一一更新它們。
(2) 執(zhí)行的update語句的數(shù)目太多,每個update語句只能更新一個Customer對象,必須通過1萬條update語句才能更新一萬個Customer對象,頻繁的訪問數(shù)據(jù)庫,會大大降低應(yīng)用的性能。
為了迅速釋放1萬個Customer對象占用的內(nèi)存,可以在更新每個Customer對象后,就調(diào)用Session的evict()方法立即釋放它的內(nèi)存:
tx = session.beginTransaction();
Iterator customers=session.find("from Customer c where c.age>0").iterator();
while(customers.hasNext()){
Customer customer=(Customer)customers.next();
customer.setAge(customer.getAge()+1);
session.flush();
session.evict(customer);
}
tx.commit();
session.close();
在以上程序中,修改了一個Customer對象的age屬性后,就立即調(diào)用Session的 flush()方法和evict()方法,flush()方法使Hibernate立刻根據(jù)這個Customer對象的狀態(tài)變化同步更新數(shù)據(jù)庫,從而立即執(zhí)行相關(guān)的update語句;evict()方法用于把這個Customer對象從緩存中清除出去,從而及時釋放它占用的內(nèi)存。
但evict()方法只能稍微提高批量操作的性能,因?yàn)椴还苡袥]有使用evict()方法,Hibernate都必須執(zhí)行1萬條update語句,才能更新1萬個Customer對象,這是影響批量操作性能的重要因素。假如Hibernate能直接執(zhí)行如下SQL語句:
update CUSTOMERS set AGE=AGE+1 where AGE>0;
那么以上一條update語句就能更新CUSTOMERS表中的1萬條記錄。但是Hibernate并沒有直接提供執(zhí)行這種update語句的接口。應(yīng)用程序必須繞過Hibernate API,直接通過JDBC API來執(zhí)行該SQL語句:
tx = session.beginTransaction();
Connection con=session.connection();
PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "
+"where AGE>0 ");
stmt.executeUpdate();
tx.commit();
以上程序演示了繞過Hibernate API,直接通過JDBC API訪問數(shù)據(jù)庫的過程。應(yīng)用程序通過Session的connection()方法獲得該Session使用的數(shù)據(jù)庫連接,然后通過它創(chuàng)建 PreparedStatement對象并執(zhí)行SQL語句。值得注意的是,應(yīng)用程序仍然通過Hibernate的Transaction接口來聲明事務(wù)邊界。
如果底層數(shù)據(jù)庫(如Oracle)支持存儲過程,也可以通過存儲過程來執(zhí)行批量更新。存儲過程直接在數(shù)據(jù)庫中運(yùn)行,速度更加快。在Oracle數(shù)據(jù)庫中可以定義一個名為batchUpdateCustomer()的存儲過程,代碼如下:
create or replace procedure batchUpdateCustomer(p_age in number) as
begin
update CUSTOMERS set AGE=AGE+1 where AGE>p_age;
end;
以上存儲過程有一個參數(shù)p_age,代表客戶的年齡,應(yīng)用程序可按照以下方式調(diào)用存儲過程:
tx = session.beginTransaction();
Connection con=session.connection();
String procedure = "{call batchUpdateCustomer(?) }";
CallableStatement cstmt = con.prepareCall(procedure);
cstmt.setInt(1,0); //把年齡參數(shù)設(shè)為0
cstmt.executeUpdate();
tx.commit();
從上面程序看出,應(yīng)用程序也必須繞過Hibernate API,直接通過JDBC API來調(diào)用存儲過程。
Session的各種重載形式的update()方法都一次只能更新一個對象,而delete()方法的有些重載形式允許以HQL語句作為參數(shù),例如:
session.delete("from Customer c where c.age>0");
如果CUSTOMERS表中有1萬條年齡大于零的記錄,那么以上代碼能刪除一萬條記錄。但是Session的delete()方法并沒有執(zhí)行以下delete語句:
delete from CUSTOMERS where AGE>0;
Session的delete()方法先通過以下select語句把1萬個Customer對象加載到內(nèi)存中:
select * from CUSTOMERS where AGE>0;
接下來執(zhí)行一萬條delete語句,逐個刪除Customer對象:
delete from CUSTOMERS where ID=i;
delete from CUSTOMERS where ID=j;
……
delete from CUSTOMERS where ID=k;
由此可見,直接通過Hibernate API進(jìn)行批量更新和批量刪除都不值得推薦。而直接通過JDBC API執(zhí)行相關(guān)的SQL語句或調(diào)用相關(guān)的存儲過程,是批量更新和批量刪除的最佳方式,這兩種方式都有以下優(yōu)點(diǎn):
(1) 無需把數(shù)據(jù)庫中的大批量數(shù)據(jù)先加載到內(nèi)存中,然后逐個更新或修改它們,因此不會消耗大量內(nèi)存。
(2) 能在一條SQL語句中更新或刪除大批量的數(shù)據(jù)。
1、Windows NT4.0+ORACLE 8.0.4
2、ORACLE安裝路徑為:C:ORANT
含義解釋:
問:什么是NULL?
答:在我們不知道具體有什么數(shù)據(jù)的時候,也即未知,可以用NULL,我們稱它為空,ORACLE中,含有空值的表列長度為零。
ORACLE允許任何一種數(shù)據(jù)類型的字段為空,除了以下兩種情況:
1、主鍵字段(primary key),
2、定義時已經(jīng)加了NOT NULL限制條件的字段
說明:
1、等價于沒有任何值、是未知數(shù)。
2、NULL與0、空字符串、空格都不同。
3、對空值做加、減、乘、除等運(yùn)算操作,結(jié)果仍為空。
4、NULL的處理使用NVL函數(shù)。
5、比較時使用關(guān)鍵字用“is null”和“is not null”。
6、空值不能被索引,所以查詢時有些符合條件的數(shù)據(jù)可能查不出來,count(*)中,用nvl(列名,0)處理后再查。
7、排序時比其他數(shù)據(jù)都大(索引默認(rèn)是降序排列,小→大),所以NULL值總是排在最后。
使用方法:
SQL> select 1 from dual where null=null;
沒有查到記錄
SQL> select 1 from dual where null='';
沒有查到記錄
SQL> select 1 from dual where ''='';
沒有查到記錄
SQL> select 1 from dual where null is null;
1
---------
1
SQL> select 1 from dual where nvl(null,0)=nvl(null,0);
1
---------
1
對空值做加、減、乘、除等運(yùn)算操作,結(jié)果仍為空。
SQL> select 1+null from dual;
SQL> select 1-null from dual;
SQL> select 1*null from dual;
SQL> select 1/null from dual;
查詢到一個記錄.
注:這個記錄就是SQL語句中的那個null
設(shè)置某些列為空值
update table1 set 列1=NULL where 列1 is not null;
現(xiàn)有一個商品銷售表sale,表結(jié)構(gòu)為:
month char(6) --月份
sell number(10,2) --月銷售金額
create table sale (month char(6),sell number);
insert into sale values('200001',1000);
insert into sale values('200002',1100);
insert into sale values('200003',1200);
insert into sale values('200004',1300);
insert into sale values('200005',1400);
insert into sale values('200006',1500);
insert into sale values('200007',1600);
insert into sale values('200101',1100);
insert into sale values('200202',1200);
insert into sale values('200301',1300);
insert into sale values('200008',1000);
insert into sale(month) values('200009');(注意:這條記錄的sell值為空)
commit;
共輸入12條記錄
SQL> select * from sale where sell like '%';
MONTH SELL
------ ---------
200001 1000
200002 1100
200003 1200
200004 1300
200005 1400
200006 1500
200007 1600
200101 1100
200202 1200
200301 1300
200008 1000
查詢到11記錄.
結(jié)果說明:
查詢結(jié)果說明此SQL語句查詢不出列值為NULL的字段
此時需對字段為NULL的情況另外處理。
SQL> select * from sale where sell like '%' or sell is null;
SQL> select * from sale where nvl(sell,0) like '%';
MONTH SELL
------ ---------
200001 1000
200002 1100
200003 1200
200004 1300
200005 1400
200006 1500
200007 1600
200101 1100
200202 1200
200301 1300
200008 1000
200009
查詢到12記錄.
Oracle的空值就是這么的用法,我們最好熟悉它的約定,以防查出的結(jié)果不正確。
以上來自http://www.cnoug.org/viewthread.php?tid=15087
但對于char 和varchar2類型的數(shù)據(jù)庫字段中的null和空字符串是否有區(qū)別呢?
作一個測試:
create table test (a char(5),b char(5));
SQL> insert into test(a,b) values('1','1');
SQL> insert into test(a,b) values('2','2');
SQL> insert into test(a,b) values('3','');--按照上面的解釋,b字段有值的
SQL> insert into test(a) values('4');
SQL> select * from test;
A B
---------- ----------
1 1
2 2
3
4
SQL> select * from test where b='';----按照上面的解釋,應(yīng)該有一條記錄,但實(shí)際上沒有記錄
未選定行
SQL> select * from test where b is null;----按照上面的解釋,應(yīng)該有一跳記錄,但實(shí)際上有兩條記錄。
A B
---------- ----------
3
4
SQL>update table test set b='' where a='2';
SQL> select * from test where b='';
未選定行
SQL> select * from test where b is null;
A B
---------- ----------
2
3
4
測試結(jié)果說明,對char和varchar2字段來說,''就是null;但對于where 條件后的'' 不是null。
對于缺省值,也是一樣的!
http://yseraphi.itpub.net/post/720/114646
當(dāng)頁面上的控件同名且多個的時候,你首先做的是什么?判斷長度?的確,從程序的嚴(yán)密角度出發(fā),我們是需要判斷長度,而且有長度和沒長度是兩種引用方法.我們來看:
oEle= document.all.aaa ;//這里有一個aaa的對象,但我們不知道它現(xiàn)在長度是多少,所以沒辦法對它操作.因此,我們要先做判斷長度的過程.如下:
if(oEle.length){}else{};
在兩種情況下,花括號里面的內(nèi)容寫法也是不一樣的:
if(oEle.length){
for(var i = 0 ;i<oEle.length;i++){
oEle[i].value........
}
}
else{
oEle.value........
};
但是這樣寫是不是太復(fù)雜了點(diǎn)?而且當(dāng)花括號里面的代碼比較多的時候,我們要寫兩遍代碼,暈了先~
還好有document.getElementsByName()這個方法.它對一個和多個的處理是一樣的,我們可以用:
oEle = document.getElementsByName('aaa')來引用
當(dāng)oEle只有1個的時候,那么就是oEle[0],有多個的時候,用下標(biāo)法oEle[i]循環(huán)獲取,是不是很簡單?
值得一提的是它對Name和ID的同樣有效的.
但是它只能應(yīng)用到document對象.相對應(yīng)的,還有另一個方法,可以應(yīng)用的對象會更廣一點(diǎn):
getElementsByTagName,比如我知道了一個<DIV ID='aaa'><input/><input/>......</DIV>
我要取DIV里面的所有input,這樣寫就可以了:aaa.getElementsByTagName('INPUT'),這樣就有效的可以和別的DIV(比如說有個叫bbb的DIV,里面的也是一樣的input)相區(qū)別.
同getElementsByTagName相對應(yīng),還有一個document.body.all.tags(),能用這個方法的對象比getElementsByTagName要小得多.但比getElementsByName要多.
到這里我們還要提一下getElementById,它也是只有document對象才能使用,而且返回的是數(shù)組的第一個元素,呵呵,它的方法名都寫明了是getElement而不是getElements,所以,千萬不要搞渾了.