當(dāng)前位置:工程項目OA系統(tǒng) > 泛普各地 > 陜西OA系統(tǒng) > 西安OA系統(tǒng) > 西安OA快博
熱門數(shù)據(jù)庫JDBC驅(qū)動試用心得
一、引言
無論是初級還是中高級技術(shù)人員,面對著各式各樣的數(shù)據(jù)庫平臺層出不窮和眾多的操作系統(tǒng)功能不斷升級,難免會眼花繚亂。特別是當(dāng)系統(tǒng)面臨升級,無論操作平臺還是數(shù)據(jù)庫平臺,甚至架構(gòu)都可能需要更替的時候,如何才能抵住眾說紛紜,把握好你的選擇。幸運的是,利用Java技術(shù)可以將這些不同種別的數(shù)據(jù)庫平臺和操作系統(tǒng)無縫地連接起來,真正地做到“集百家之長而為我所用”。
本文將通過一組真實的案例來向讀者介紹如何做到簡單地使用JDBC驅(qū)動來實現(xiàn)在不同的操作系統(tǒng)下存取幾款較為熱門的數(shù)據(jù)庫平臺。
特別是對JavaDB這款支持嵌入式模式的純Java數(shù)據(jù)庫的開發(fā)過程進(jìn)行了詳細(xì)分析和展望。希望讀者能做到舉一反三,引入更多的數(shù)據(jù)庫平臺的應(yīng)用。
二、評測框架
1.操作系統(tǒng)平臺和數(shù)據(jù)庫平臺
實例涉及到的操作系統(tǒng)是MS Windows XP + SP2和SUN Solaris 8,數(shù)據(jù)庫平臺有:MS Access 2000(以下簡稱Access),MS SQL Server 2000(以下簡稱SQL Server),My SQL,Oracle和Java DB(J2SE 1.6.0中綁定)。
對于XP平臺,可以安裝以上5種數(shù)據(jù)庫平臺。而對于Solaris,只可以安裝My SQL和Java DB兩種。
2.使用平臺搭建
(1)安裝支持對應(yīng)操作系統(tǒng)的JDK(http://java.sun.com/javase/downloads/index.jsp)。注意:如果是Solaris操作系統(tǒng)還必須選擇對應(yīng)的CPU類型,本案例中選用的是支持SPARC的JDK版本(jdk-6-solaris-sparc.sh)。在XP系統(tǒng)中安裝的JDK Update3版本的JDK(jdk-6u3-windows-i586-p.exe),保證該版本中已經(jīng)綁定Java DB。
(2)設(shè)置JAVA_HOME,PATH和CLASSPATH等環(huán)境變量。以便正常編譯和運行Java代碼。
(3)下載My SQL Connector/J驅(qū)動,并將其中的mysql-connector-java-5.1.0-bin.jar文件(其中5.1.0為驅(qū)動版本號)添加到CLASSPATH變量中。需要說明的是,該驅(qū)動文件中包含兩種JDBC驅(qū)動,一種是mm.mysql,一種是mysql普通JDBC驅(qū)動。兩者都可以使用。
(4)將包含Java DB和Oracle的驅(qū)動文件加入到CLASSPATH中。分別為derby.jar和classes12.jar。都可以在相應(yīng)的產(chǎn)品安裝目錄中找到。
通過上述的配置之后,我們就可以開始在XP系統(tǒng)和Solaris系統(tǒng)中對各類數(shù)據(jù)庫平臺進(jìn)行使用了。
三、試用準(zhǔn)備
1.簡化JDBC函數(shù)
為了方便開發(fā)人員的使用,作者提煉出以下簡化后的常用JDBC函數(shù):
這些函數(shù)基本上已經(jīng)滿足大部分的使用,初級開發(fā)人員按照函數(shù)的調(diào)用步驟就可以實現(xiàn)通過JDBC驅(qū)動與各種數(shù)據(jù)庫平臺進(jìn)行交互了。
如果用戶對數(shù)據(jù)庫操作的效率比較關(guān)注,那么還有3組比較重要的,也是常用的JDBC函數(shù),分別是:
(1)事務(wù)處理函數(shù):setAutoCommit/commit/rollback
(2)批處理函數(shù):addBatch/execBatch
(3)語句預(yù)處理函數(shù):prepareStatement
對于大多數(shù)開發(fā)人員,只需要知道其用法含義即可,深層次的探索和分析可能需要另外的篇幅來說明,因此作者在此不再贅述。
實際上,上述基本函數(shù)的定義,大部分都是對JDK中JDBC函數(shù)的封裝,讀者也可以通過JDBC的文檔進(jìn)行相關(guān)的查閱。關(guān)鍵代碼參見全文末尾附錄部分。
2.?dāng)?shù)據(jù)庫表定義
作為試用,作者并沒有定義很復(fù)雜的數(shù)據(jù)庫表,以下是測試用數(shù)據(jù)表結(jié)構(gòu):
create table user_info(
ui_id varchar(64),
ui_passwd varchar(64),
ui_real_name varchar(64), primary key(ui_id));
3.試用思路
比較簡單,就是通過上述不同類型的JDBC驅(qū)動來連接各種數(shù)據(jù)庫平臺,然后向已經(jīng)初始化的數(shù)據(jù)表插入10000條記錄,再逐條讀取,并記錄其各個步驟的執(zhí)行耗費。
四、試用過程
1.平臺選擇
作者選擇了MS Windows XP和Solaris兩種平臺。其中只有MySQL,JavaDB和Oracle(連接遠(yuǎn)程服務(wù)器)既可在Windows平臺進(jìn)行了測試,也可在Solaris平臺下進(jìn)行了測試。兩個系統(tǒng)下的測試代碼和框架完全相同。
2.使用JDBC驅(qū)動連接數(shù)據(jù)庫
(1)Access數(shù)據(jù)庫
通過JDBC驅(qū)動連接Access數(shù)據(jù)庫最常用的是采用建立ODBC數(shù)據(jù)源(DSN)的方式進(jìn)行,但是本測試中采用的是通過數(shù)據(jù)庫連接字符串避開了手工建立DSN的部分。以下是關(guān)鍵代碼:
final String connectStr =
“jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ=./TestDB.mdb”;
……
if( (conn = FoolDB.openDB(connectStr, null, null)) == null)
用過ADO的讀者可能一眼就看出來了,上述的連接字符串中的內(nèi)容和ADO很相似。事實上,作者就是為了避開建立DNS而嘗試套用了ADO的連接字符串,結(jié)果嘗試通過了!
(2)SQL Server數(shù)據(jù)庫
需要說明的,對于SQL Server的測試沒有采用Microsoft提供的SQL Server專用的JDBC驅(qū)動,還是通過借鑒ADO的連接字符串形式,沿用了JDK自帶的JdbcOdbc驅(qū)動。
final String connectStr =注意:用戶名和密碼已經(jīng)包含到連接字符串中。“jdbc:odbc:Driver={SQL Server};Server=.;Database=master;UID=sa;PWD=121fs”;
……
if( (conn = FoolDB.openDB(connectStr, null, null)) == null)
SUN公司提供的SQL Server的JDBC驅(qū)動的版本應(yīng)該比較陳舊,所以可能導(dǎo)致在操作數(shù)據(jù)庫功能支持和效率方面比當(dāng)前新的JDBC驅(qū)動要差一些。Microsoft提供的SQL Server JDBC驅(qū)動類名為:“com.microsoft.jdbc.sqlserver.SQLServerDriver”,連接字符串形如:“jdbc:microsoft:sqlserver://hostname:port;DataBaseName=dbname”。
(3)MySQL數(shù)據(jù)庫
眾所周知,MySQL數(shù)據(jù)庫既可以在Windows可以在Solaris平臺運行,而且執(zhí)行效率也深得業(yè)界的好評。本試?yán)羞B接MySQL使用的是MySQL專用驅(qū)動(在第一部分已經(jīng)詳述,在MySQL官方網(wǎng)頁有很多的支持文檔),以下是關(guān)鍵代碼:
final String connectStr = “jdbc:mysql://localhost/phome”;在本地連接時,主機名可以使用“l(fā)ocalhost”,但如果連接遠(yuǎn)程主機時,必須換成該遠(yuǎn)程主機的IP,而phome是MySQL數(shù)據(jù)庫名稱。final String userName = “root”; //
final String passwd = ““;
……
if( (conn = FoolDB.openDB(connectStr, userName, passwd)) == null)
(4)Oracle數(shù)據(jù)庫
Oracle提供了thin和oci兩種類型的驅(qū)動,本測試中使用thin類型JDBC驅(qū)動。
final String connectStr = “jdbc:oracle:thin:@WBS:1521/oracle088”;其中“WBS:1521/oracle088”為Oracle服務(wù)器的SID。final String userName = “test”;
final String passwd = “test”;
(5)Java DB
值得一提的,Java DB是由Apache Software Foundation主要參與開發(fā)的一個數(shù)據(jù)庫(DB)項目,SUN在JDK1.6.0中將其進(jìn)行了綁定,它是一款名副其實的純Java代碼開發(fā)的數(shù)據(jù)庫平臺。可以支持Server/Client也可以支持嵌入式運行模式,本實例中主要采用了嵌入式模式(Embedded)進(jìn)行操作:
final String connectStr = “jdbc:derby:FoolDB”;其中FoolDB是在初始化過程中,使用連接字符串“jdbc:derby:FoolDB;create=true”進(jìn)行創(chuàng)建的。創(chuàng)建的結(jié)果是:在當(dāng)前目錄中創(chuàng)建一個名為FoolDB的目錄,該目錄又中包含log,seg0和tmp這3個文件夾,而數(shù)據(jù)庫內(nèi)容以多文件的方式存放于seg0目錄中。……
if( (conn = FoolDB.openDB(connectStr, null, null)) == null)
注意:數(shù)據(jù)庫一旦創(chuàng)建之后,就無需再次創(chuàng)建了。
3.設(shè)置數(shù)據(jù)庫事務(wù)方式
當(dāng)數(shù)據(jù)庫連接成功之后,需要設(shè)定連接的事務(wù)方式(前提是該數(shù)據(jù)庫平臺支持事務(wù)處理)為非自動提交,因為JDBC函數(shù)默認(rèn)為自動提交。
提交過于頻繁,就會使數(shù)據(jù)庫操作的效率較低。特別是對于Oracle這種絕對支持事務(wù)處理的數(shù)據(jù)平臺而言,設(shè)置是否自動提交對系統(tǒng)的效率將有很大的影響。
4.采用批處理方式插入記錄
同樣一個考慮效率的操作,循環(huán)地執(zhí)行操作語句(Insert,Update)也會增加很多不必要地開銷。試?yán)胁捎昧伺刻幚淼姆绞?,通過這種方式將要進(jìn)行的操作先進(jìn)行匯總,再批量提交執(zhí)行。這樣就可以獲得較高的執(zhí)行效率。
//Insert records這種批處理的方式,可以視同預(yù)處理,通過統(tǒng)合批量的操作來減少與數(shù)據(jù)庫的交互頻率,也減少數(shù)據(jù)庫訪問IO設(shè)備的頻率,從而也可獲得較高的效率。for(int i = 0; i < RECORD_COUNT; ++i)
{
//Add each operation to batch
if(FoolDB.addBatch(execStat, “insert into user_info values('guest”
+ Integer.toString(i + 1)
+ “', '666666', '我是中國人')”) == false)
……
FoolDB.execBatch(execStat); //Execute batch operations
FoolDB.commit(conn);
FoolDB.setAutoCommit(conn, true);
5.使用行游標(biāo)讀取記錄字段
記錄插入完畢之后,通過執(zhí)行查詢語句來獲取數(shù)據(jù)集。然后再通過簡化函數(shù)moveNext來逐行讀取結(jié)果集的記錄行:
//Select from table實際上,moveNext的函數(shù)名也來源于ADO(在作者看來,JDBC和ADO的應(yīng)用方式是相同的)。String sql = "select * from user_info";
if( (queryRS = FoolDB.openQuery(queryStat, sql)) == null)
{
…
}
…
while(FoolDB.moveNext(queryRS) == true)
{
//Field ui_id, text(64) type
FoolDB.getFieldByName(queryRS, “ui_id”);
//Field ui_passwd, text(64) type
FoolDB.getFieldByName(queryRS, “ui_passwd”);
//Field ui_real_name, text(64) type
FoolDB.getFieldByName(queryRS, “ui_real_name”);
}
6.關(guān)閉數(shù)據(jù)庫
這里主要對以嵌入式模式運行的JavaDB的關(guān)閉進(jìn)行說明,其他數(shù)據(jù)庫的關(guān)閉,直接采用簡化后的關(guān)閉函數(shù)(closeDB)即可。
在嵌入式模式,應(yīng)用程序退出時就必須關(guān)閉數(shù)據(jù)庫。但是如果應(yīng)用程序關(guān)閉數(shù)據(jù)庫失敗,當(dāng)JVM退出時不會對該未被關(guān)閉的連接進(jìn)行檢查,這樣就占用了數(shù)據(jù)庫的連接資源,就會影響后續(xù)的連接執(zhí)行效率。所以Apache Derby的建議是采用URL的方式顯示地關(guān)閉數(shù)據(jù)庫。以下是關(guān)鍵代碼:
FoolDB.closeDB(conn); //Close connectionif(framework.equals("embedded") == true) //If in embedded mode
{
//Use URL to shutdown Derby
if(FoolDB.openDB("jdbc:derby:;shutdown=true", null, null) == null)
{
//If shutdown failure means that Derby already shutdown
isShutdownOk = true;
}
if (isShutdownOk == false) //Not shutdown normally
{
System.out.println("Derby shutdown NG.");
}
}
注意:關(guān)閉和判斷Derby數(shù)據(jù)庫是否正常關(guān)閉的連接字符串中是不包含數(shù)據(jù)庫名的。
7.記錄各步驟操作的時間戳
以上的操作中,在各個步驟之間添加了時間戳,以此來記錄各個數(shù)據(jù)庫平臺的執(zhí)行耗費。
五、結(jié)果及分析
在對試用結(jié)果進(jìn)行評價之前,讀者需要考慮各款數(shù)據(jù)庫平臺的特點,而不能簡單地從執(zhí)行時間的長短來進(jìn)行判斷,以下是筆者根據(jù)開發(fā)經(jīng)驗總結(jié)出的需要注意的地方:
(1)要保證JDBC驅(qū)動和數(shù)據(jù)庫平臺的連接是無縫的。例如Oracle和JavaDB數(shù)據(jù)庫本身就是由Java開發(fā),其JDBC驅(qū)動和數(shù)據(jù)庫平臺的連接可以做到完全無縫,這樣可以充分體現(xiàn)出該款數(shù)據(jù)庫平臺的特性。反之,Access和SQL Server與對應(yīng)的JdbcOdbc驅(qū)動之間的耦合可能就不如與ADO驅(qū)動那么吻合,那么這些數(shù)據(jù)平臺的很多特性就無法通過這些JDBC驅(qū)動得以體現(xiàn)。之前我們也提到,Microsoft專門有提供MS SQL Server的JDBC驅(qū)動,數(shù)據(jù)庫與這種“對口”的JDBC驅(qū)動的耦合肯定要超過JdbcOdbc這種通用型的驅(qū)動。
(2)數(shù)據(jù)庫平臺要有良好的可移植性。換句話說就是數(shù)據(jù)庫跨操作系統(tǒng)的性能。在這些方面Oracle,MySQL和JavaDB就要比Access和SQL Server有明顯優(yōu)勢,它們不僅可以支持Windows平臺而且也支持Solaris和Linux平臺。而且數(shù)據(jù)庫平臺的可移植很大程度也決定了應(yīng)用系統(tǒng)的可移植性。
(3)數(shù)據(jù)庫平臺要支持應(yīng)用的多樣化(需求彈性)。數(shù)據(jù)庫平臺應(yīng)該不僅可以對應(yīng)傳統(tǒng)的C/S,B/S模式,而且還可以擴展為三層的,甚至是多層的模式,或者支持嵌入式系統(tǒng)的應(yīng)用。顯而易見,本身使用Java開發(fā)的Oracle和JavaDB在這些方面就具有得天獨厚的優(yōu)勢。而且JavaDB還支持嵌入式應(yīng)用,這樣以來數(shù)據(jù)庫的應(yīng)用空間就更為廣闊了。
所以基于以上幾點的考慮,我們就可以得出初步的意向:
(1)對于一般的中小型的C/S和B/S架構(gòu),MySQL可以說是我們當(dāng)前首選。它在跨平臺和執(zhí)行效率方面表現(xiàn)得相當(dāng)?shù)某錾?/p>
(2)對于那些對系統(tǒng)構(gòu)架彈性要求比較高的系統(tǒng),可以選擇Oracle平臺,并結(jié)合EJB規(guī)范進(jìn)行搭建系統(tǒng)構(gòu)架無疑將是很好的選擇。
(3)選用JavaDB進(jìn)行嵌入式平臺開發(fā)似乎是不錯的主意。由于現(xiàn)在很多嵌入式設(shè)備中都嵌入了Java內(nèi)核,JavaDB也就可以得以應(yīng)用了,嵌入式設(shè)備與其他系統(tǒng)共享數(shù)據(jù)的接口就變得及其簡單。
(4)如果只在Windows平臺進(jìn)行開發(fā)的中小型系統(tǒng),用Access或者SQL Server平臺也不妨是一種簡單快捷的途徑。
六、結(jié)束語
通過前兩部分的說明,相信大家對JDBC的使用應(yīng)該有相當(dāng)部分的了解和收獲。即使作為初學(xué)者,通過簡化后的JDBC函數(shù)和固定的試用方式,就可以實現(xiàn)通過JSP頁面,Java Applet或者是Java Application等程序來訪問各種類型的數(shù)據(jù)庫平臺了。即使是在本文中沒有提到的其他數(shù)據(jù)庫平臺,例如:IBM的DB2,Solaris 10中綁定的PostgreSQL數(shù)據(jù)庫。
尤其的,對于中高級技術(shù)人員而言,也可以通過這幾種熱門的數(shù)據(jù)庫平臺在不同操作系統(tǒng)下的使用案例得到一定的參考。
其中,特別是介紹了對嵌入式數(shù)據(jù)庫的使用,相信也一定開闊了大家的視野和應(yīng)用范圍,畢竟嵌入式系統(tǒng)的應(yīng)用在目前也是如火如荼。在后續(xù)的實際開發(fā)中筆者會將更多的心得體會和大家一起分享。
七、附錄:
1.主要FoolDB函數(shù)參考
//Get a conn to special database.public static Connection openDB(final String url, final String user, final String passwd)
{
try
{
return (DriverManager.getConnection(url, user, passwd) );
}
catch (SQLException CONNECT_FAILURE)
{
…
}
}
//Get a statement object that can be used for query (read only)
public static Statement getQueryStat(final Connection conn)
{
try
{
return (conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY) );
}
catch(SQLException CREATE_QUERY_STATEMENT)
{
…
}
}
//Get a statement object that can be used for update (can write)
public static Statement getExecStat(final Connection conn)
{
try
{
return (conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE) );
}
catch(SQLException CREATE_EXEC_STATEMENT)
{
…
}
}
//Execute SQL statement, and get the result set.
public static ResultSet openQuery(final Statement stat, final String sql)
{
try
{
return (stat.executeQuery(sql) );
}
catch(SQLException OPEN_QUERY)
{
…
}
}
//Get the rows cout of result set.
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
public static int getRowsCount(ResultSet rs)
{
try
{
int rowsCount = 0;
//Backup current row no.
int rowNo = rs.getRow();
//Locate last row
rs.last();
//Get the rows count
rowsCount = rs.getRow();
//Return back original row
if(rowNo < 1) //before first row
{
rs.beforeFirst();
}
else //
{
rs.absolute(rowNo);
}
return (rowsCount);
}
catch(SQLException GET_ROWS_COUNT_FAILURE)
{
…
}
}
//Get the columns count of resut set.
public static int getColsCount(final ResultSet rs)
{
try
{
ResultSetMetaData rsmd = rs.getMetaData();
return (rsmd.getColumnCount() );
}
catch(SQLException GET_COLS_COUNT_FAILURE)
{
…
}
}
//Get special column name.
//Note: The index of column base 1, but not 0.
public static String getColName(final ResultSet rs, final int colIndex)
{
try
{
ResultSetMetaData rsmd = rs.getMetaData();
return (rsmd.getColumnName(colIndex) );
}
catch(SQLException GET_COL_NAME_FAILURE)
{
…
}
}
//Move the cursor of result set to next row
public static boolean moveNext(ResultSet rs)
{
try
{
return (rs.next() );
}
catch(SQLException MOVE_NEXT_FAILURE)
{
…
}
}
//Get the retValue of cell by special row number and column number
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getValueAt(ResultSet rs, final int rowIndex, final int colIndex)
{
if( (rowIndex < 1) || (colIndex < 1) )
{
return (null);
}
try
{
//Backup current row no.
int rowNo = rs.getRow();
Object retValue = null;
//Locate to special row
rs.absolute(rowIndex);
//Get retValue
retValue = rs.getObject(colIndex);
//Return back origianl row
rs.absolute(rowNo);
return (retValue);
}
catch(SQLException GET_VALUE_FAILURE)
{
…
}
}
//Get the retValue of cell by special row number and field name
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getFieldByName(ResultSet rs, final int rowIndex, final String fieldName)
{
if( (rowIndex < 1) || (fieldName.equals("") == true) )
{
return (null);
}
try
{
//Backup current row no.
int rowNo = rs.getRow();
Object retValue = null;
//Locate to special row
rs.absolute(rowNo);
//Get retValue
retValue = rs.getObject(fieldName);
//Return back origianl row no.
rs.absolute(rowNo);
return (retValue);
}
catch(SQLException GET_FIELD_BY_NAME_FAILURE)
{
…
}
}
//Get the retValue of cell within current row by special field name
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getFieldByName(final ResultSet rs, final String fieldName)
{
if( (isBOF(rs) == true) || (isEOF(rs) == true) )
{
return (null);
}
try
{
return (rs.getObject(fieldName) );
}
catch(SQLException GET_FIELD_BY_NAME_FAILURE)
{
…
}
}
//Get the retValue of cell within current row by special column index
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getFieldByIndex(final ResultSet rs, final int columnIndex)
{
if( (columnIndex < 1) || (isBOF(rs) == true) || (isEOF(rs) == true) )
{
return (null);
}
try
{
return (rs.getObject(columnIndex) );
}
catch(SQLException GET_FIELD_BY_INDEX_FAILURE)
{
…
}
}
(it168)
- 1局域網(wǎng)加快網(wǎng)速妙招
- 2五大技術(shù)改變未來
- 3XML數(shù)據(jù)庫應(yīng)用現(xiàn)狀
- 4深圳大運會投140億元收12億元 審計發(fā)現(xiàn)問題
- 5讓路由器擺脫安全困擾的十個技巧
- 6云存儲后年將與電子商務(wù)比肩勢必流行
- 7記者臥底遭暴打 這是救助站還是黑心店
- 8郭德綱首次應(yīng)邀參加央視春晚 哈文秘密與其面談
- 9誰是最大的信息安全漏洞?
- 10協(xié)同OA軟件成為公司真實存在的資產(chǎn)記錄
- 11網(wǎng)絡(luò)社區(qū)安全難題待解
- 12樓市現(xiàn)狀誰都不敢動 明年剛需濤聲依舊
- 13內(nèi)網(wǎng)安全市場的渠道利益模型探討
- 14九大技巧助力管理員設(shè)置高效UTM
- 15OA系統(tǒng)已經(jīng)默認(rèn)將雙休日不需要簽到
- 16知識管理六大失敗案例剖析
- 17京東惡意訂單解釋權(quán)遭質(zhì)疑 嚴(yán)重侵犯客戶權(quán)益
- 18如何保障虛擬服務(wù)器的安全
- 19網(wǎng)站如何防范“上傳漏洞”入侵
- 20日媒稱日本著手制定對付中國奪島戰(zhàn)略
- 212008年最熱7大軟件技能
- 22十大戰(zhàn)略性技術(shù)問鼎2008
- 23Windows Vista服務(wù)安全強化的十大策略
- 24全球兩大陣營ERP系統(tǒng)的技術(shù)分析
- 252013年南京家裝市場剛需裝修仍占主導(dǎo)地位
- 26XML數(shù)據(jù)庫在中國的應(yīng)用狀況
- 27數(shù)據(jù)災(zāi)難,就在我們身邊
- 28通向一流軟件設(shè)計的12級階梯
- 29OA辦公軟件可以直接點擊這個模板名稱鏈接即可
- 30西安本土OA協(xié)同管理軟件廠商都有哪些?
成都公司:成都市成華區(qū)建設(shè)南路160號1層9號
重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務(wù)大廈18樓