1.1 概述
c#程序開發中,數據庫操作無疑是舉足輕重的,數據庫部分的技術點可能占整個c#技術點的1/4。這幾天我一直在研究System.Data.OracleClient.dll反編譯之后的.CS,放棄c#的心都有了,底層代碼不僅全是英文注釋,而且有很多東西看都看不懂,讓我深刻體會封裝的重要性!此外在做sql語句參數化拼接時,我想在c#中效仿java中的PreparedStatement,但是實現起來困難重重,花了很多時間,最后效果也不理想!放棄繼續深入!這就說明用語言開發和開發語言是兩個不同層次的!
fhbds!
1.2 操作oracle數據庫
任何操作都是建立在先連接上數據庫,連接數據庫需要第三方類庫System.Data.OracleClient.dll,(我已經上傳至文件),添加引用即可使用其中的類和方法。可惜沒有找到關於這個類庫的API,要不然學習這個類庫的阻力就會小很多,雖然微軟官網有一個c#的幫助文檔,但是我看后感覺幫助不大,就像我之前說的不要糾結別人提供給我們的類庫底層到底如何實現的,只要會用就ok了!java中連接數據庫也要導包,連接數據庫的語句在java中叫注冊數據庫驅動!
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Configuration; 11 using Oracle.ManagedDataAccess.Client; 12 using System.Collections; 13 14 15 public static bool ConnectOracle() 16 { 17 bool linkFlag = false; 18 try 19 { 20 //注冊驅動 21 string dataSource = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=2013-20130829TH)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=oracle)));"; 22 string persistSecurityInfo = "Persist Security Info=True;"; 23 string userID = "User ID=SYSTEM;"; 24 string password = "Password=admin;"; 25 OracleConnection con = new OracleConnection(dataSource + persistSecurityInfo + userID + password); 26 con.Open(); 27 linkFlag = true; 28 return linkFlag; 29 } 30 catch (Exception ex) 31 { 32 ex.ToString(); 33 return linkFlag; 34 35 } 36 }
dataSource中一般只需要根據oracle安裝目錄下的tnsnames.ora文件修改HOST名和SERVICE_NAME就可以了,這個因人而異。第二個連接字符串不需要修改,usrid和password也是根據實際要
連接哪個數據庫來寫,以后可以寫到配置文檔中。
c#中操作數據庫主要用到以下三個類及其其中的方法,分別是:OracleConnection、 OracleDataAdapter、OracleCommand。此外還有一個OracleDataReader,基本上這四個對象就能完成我們
最基本的數據庫操作。
OracleConnection是用來連接數據庫的對象,最重要的2個方法就是open()和close()了。 OracleDataAdapter主要使用是用來執行select查詢語句的,返回時一個查詢的結果集。
下面這個方法就是通過返回結果集,來獲取數據庫中的數據,c#中獲取數據的方式有點特殊,它是使用了二維數組的方式來存取的,很有一種坐標的感覺!
1 public static string dataSource = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=2013-20130829TH)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=oracle)));"; 2 public static string persistSecurityInfo = "Persist Security Info=True;"; 3 public static string userID = "User ID=SYSTEM;"; 4 public static string password = "Password=admin;"; 5 public static string connectionString = dataSource + persistSecurityInfo + userID + password; 6 public static DataSet Query(string connectionString, string SQLString) 7 { 8 using (OracleConnection connection = new OracleConnection(connectionString)) 9 { 10 DataSet ds = new DataSet(); 11 try 12 { 13 connection.Open(); 14 OracleDataAdapter command = new OracleDataAdapter(SQLString, connection); 15 command.Fill(ds, "ds"); 16 } 17 catch (OracleException ex) 18 { 19 throw new Exception(ex.Message); 20 } 21 finally 22 { 23 if (connection.State != ConnectionState.Closed) 24 { 25 connection.Close(); 26 } 27 } 28 return ds; 29 } 30 }
測試腳本:
1 SET DEFINE OFF; 2 Insert into STUDENT 3 (SID, SNAME, SAGE, SPASS) 4 Values 5 ('100', '李四', '18', '123456'); 6 Insert into STUDENT 7 (SID, SNAME, SAGE, SPASS) 8 Values 9 ('003', '王五', '18', '123456'); 10 Insert into STUDENT 11 (SID, SNAME, SAGE, SPASS) 12 Values 13 ('004', '趙六', '18', '123456'); 14 Insert into STUDENT 15 (SID, SNAME, SAGE, SPASS) 16 Values 17 ('005', '張光烈', '18', '123456'); 18 Insert into STUDENT 19 (SID, SNAME, SAGE, SPASS) 20 Values 21 ('006', '魏大勇', '18', '123456'); 22 Insert into STUDENT 23 (SID, SNAME, SAGE, SPASS) 24 Values 25 ('007', '朱永博', '18', '123456'); 26 Insert into STUDENT 27 (SID, SNAME, SAGE, SPASS) 28 Values 29 ('001', '張三', '18', '123456'); 30 Insert into STUDENT 31 (SID, SNAME, SAGE, SPASS) 32 Values 33 ('520', '李雲龍', '100', '1314'); 34 COMMIT;
測試代碼:
1 private void button2_Click(object sender, EventArgs e) 2 { 3 string sql1 = "select * from student where sid='520'"; 4 DataSet ds1 = new DataSet(); 5 ds1 = Query(connectionString, sql1); 6 textBox1.Text += "此查詢語句一共查到["+ds1.Tables[0].Rows.Count.ToString()+"]條記錄!"; 7 textBox1.Text += "此查詢語句一共查到[" + ds1.Tables[0].Columns.Count.ToString() + "]個欄位!"; 8 textBox1.Text += "此查詢語句查到第一張表,第一條記錄的欄位“SNAME”的值是[" + ds1.Tables[0].Rows[0]["SNAME"].ToString().Trim()+"]!"; 9 textBox1.Text += "此查詢語句查到的第二個欄位是[" + ds1.Tables[0].Columns[1].ToString() + "]!"; 10 11 12 }
輸出:
其中ds1.Tables[0].Rows.Count就能實現查詢語句返回的記錄數,不需要另外想什么方式!
OracleCommand是用來執行insert 、update、delete語句的,返回值是影響的行數!主要發揮作用的是OracleCommand的ExecuteNonQuery()方法。
1 //執行update insert delete語句,失敗了返回-1,成功了返回影響的行數,注意:自動commit 2 public static int ExecuteNonQuery(string connectionString, string SQLString) 3 { 4 5 using (OracleConnection connection = new OracleConnection(connectionString)) 6 { 7 int val = -1; 8 try 9 { 10 connection.Open(); 11 OracleCommand cmd = new OracleCommand(SQLString, connection); 12 val = cmd.ExecuteNonQuery(); 13 cmd.Parameters.Clear(); 14 } 15 catch (OracleException ex) { 16 throw new Exception(ex.Message); 17 } 18 finally 19 { 20 if (connection.State != ConnectionState.Closed) 21 { 22 connection.Close(); 23 } 24 } 25 return val; 26 } 27 }
測試代碼:
1 private void button3_Click(object sender, EventArgs e) 2 { 3 4 string sid = "520"; 5 string sname = "李雲龍"; 6 int age = 100; 7 string password = "1314"; 8 9 try 10 { 11 string sql = @"Insert into STUDENT(SID, SNAME, SAGE, SPASS)Values('001', '張三', '18', '123456')"; 12 //string sql = @"update student set sname='韓信' where sid='001'"; 13 //string sql = @"delete student where sid='001'"; 14 // string sql = "insert into student(SID,SNAME,SAGE,SPASS) values('" + sid + "','" + sname + "','" + age + "','" +password+ "')"; 15 int count = ExecuteNonQuery(connectionString, sql); 16 textBox1.Text = count.ToString(); 17 } catch(Exception ex){ 18 textBox1.Text= ex.Message; 19 } 20 }
此外OracleCommand還有一個ExecuteScalar()方法,返回的是object對象,返回的是查詢到的第一條記錄的第一個欄位的值,這個作用主要是用來精確查找的,也就是不會出現select *,
否則意義何在?
1 //返回查詢到第一個對象 2 public static object GetCount(string connectionString, string SQLString) 3 { 4 using (OracleConnection connection = new OracleConnection(connectionString)) 5 { 6 using (OracleCommand cmd = new OracleCommand(SQLString, connection)) 7 { 8 try 9 { 10 connection.Open(); 11 object obj = cmd.ExecuteScalar(); 12 if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value))) 13 { 14 return null; 15 } 16 else 17 { 18 return obj; 19 } 20 } 21 catch (OracleException ex) 22 { 23 throw new Exception(ex.Message); 24 } 25 finally 26 { 27 if (connection.State != ConnectionState.Closed) 28 { 29 connection.Close(); 30 } 31 } 32 } 33 } 34 }
測試代碼:
1 string sql1 = "select spass from student"; 2 object count = GetCount(connectionString,sql1); 3 textBox1.Text = count.ToString();
有時候我們並不在意查到的值是什么,在意查不查的到只要稍微修改就可以了!
1 //判斷某個對象是否存在 2 public static bool Exists(string connectionString, string strSql) 3 { 4 object obj =GetCount(connectionString, strSql); 5 int cmdresult; 6 if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value))) 7 { 8 cmdresult = 0; 9 } 10 else 11 { 12 cmdresult = int.Parse(obj.ToString()); 13 } 14 if (cmdresult == 0) 15 { 16 return false; 17 } 18 else 19 { 20 return true; 21 } 22 }
測試代碼:
1 string sql1 =@"select spass from student where sname='劉邦'"; 2 bool flag = Exists(connectionString, sql1); 3 textBox1.Text = flag.ToString();
1.3 參數化拼接SQL語句
由於實際的開發中很少有sql語句是固定寫死的,比如select * from person where name='某個對象的名字',實際中對象的名字很多,不可能為每個對象寫一條sql語句,這樣就需要sql語句的拼接,但是實際操作中拼接的sql語句中單引號和雙引號我往往有時候看着這些點時間長了,就感覺他們在亂動,不說錯覺,這樣拼接在執行中很容易發生sql異常!
java中為此引入 ? 占位符來取代拼接。但是c#中沒有,想要實現也是困難重重!
1 import java.sql.Connection; 2 import java.sql.PreparedStatement; 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.ArrayList; 6 import java.util.List; 7 8 Connection conn = DBTool.getConnection(); 9 PreparedStatement ps = null; 10 ResultSet rs = null; 11 12 public int savaEmployee(Employee e) { 13 Connection conn =DBTool.getConnection(); 14 PreparedStatement ps=null; 15 int count=0; 16 String sql="insert into employee (empNum,empName,sex,salary,intoTime,outTime,statu,depNum)values(?,?,?,?,SYSDATE(),null,0,?)"; 17 try { 18 ps=conn.prepareStatement(sql); 19 ps.setString(1,e.getEmpNum()); 20 ps.setString(2, e.getEmpName()); 21 ps.setString(3, e.getSex()); 22 ps.setDouble(4, e.getSalary()); 23 ps.setString(5, e.getDepNum()); 24 count=ps.executeUpdate(); 25 } catch (SQLException e1) { 26 // TODO Auto-generated catch block 27 e1.printStackTrace(); 28 } finally{ 29 DBTool.closeAll(conn, ps, null); 30 } 31 32 return count; 33 }
java中是這樣實現的,上面代碼只是部分。我看了一些資料,發現c#中很多人是用@去是實現參數化拼接,但是我嘗試了很久,也沒調試成功!而用:可以實現參數化拼接!
因為OracleDataAdapter()所含的四個構造方法都不能傳入數組,只能利用傳入Oraclecommand對象的屬性間接實現!這樣的話,首先需要一個傳輸參數的方法。這個方法作用就是把一些參數硬塞給Oraclecommand一個對象!
1 //傳入參數 2 private static void PrepareCommand(OracleCommand cmd, OracleConnection conn, OracleTransaction trans, string cmdText, OracleParameter[] cmdParms) 3 { 4 if (conn.State != ConnectionState.Open) 5 conn.Open(); 6 cmd.Connection = conn; 7 cmd.CommandText = cmdText; 8 if (trans != null) 9 cmd.Transaction = trans; 10 cmd.CommandType = CommandType.Text; 11 if (cmdParms != null) 12 { 13 foreach (OracleParameter parameter in cmdParms) 14 { 15 if ((parameter.Direction == ParameterDirection.InputOutput || parameter.Direction == ParameterDirection.Input) && 16 (parameter.Value == null)) 17 { 18 parameter.Value = DBNull.Value; 19 } 20 cmd.Parameters.Add(parameter); 21 } 22 } 23 }
把之前的返回結果集的方法稍作修改!
1 //執行帶參數的sql查詢語句返回DataSet結果集 2 public static DataSet QueryWithP(string connectionString, string SQLString, params OracleParameter[] cmdParms) 3 { 4 using (OracleConnection connection = new OracleConnection(connectionString)) 5 { 6 OracleCommand cmd = new OracleCommand(); 7 PrepareCommand(cmd, connection, null, SQLString, cmdParms); 8 using (OracleDataAdapter da = new OracleDataAdapter(cmd)) 9 { 10 DataSet ds = new DataSet(); 11 try 12 { 13 da.Fill(ds, "ds"); 14 cmd.Parameters.Clear(); 15 } 16 catch (OracleException ex) 17 { 18 throw new Exception(ex.Message); 19 } 20 finally 21 { 22 if (connection.State != ConnectionState.Closed) 23 { 24 connection.Close(); 25 } 26 } 27 return ds; 28 } 29 } 30 }
測試代碼:
1 private void button8_Click(object sender, EventArgs e) 2 { 3 string t_sid = "520"; 4 string t_sname = "李雲龍"; 5 string t_age = "100"; 6 string t_password = "1314"; 7 8 string sql = "select * from student where sid=:t_sid and sname=:t_sname and sage=:t_age and spass=:t_password"; 9 OracleParameter[] parameters = { 10 new OracleParameter("t_sid ",OracleDbType.Varchar2), 11 new OracleParameter("t_sname",OracleDbType.Varchar2), 12 new OracleParameter("t_age",OracleDbType.Varchar2), 13 new OracleParameter("t_password",OracleDbType.Varchar2) 14 }; 15 parameters[0].Value = t_sid; 16 parameters[1].Value = t_sname; 17 parameters[2].Value = t_age; 18 parameters[3].Value = t_password; 19 DataSet ds1 = new DataSet(); 20 try 21 { 22 ds1 = QueryWithP(connectionString, sql, parameters); 23 }catch(Exception ex){ 24 textBox1.Text = ex.Message; 25 } 26 textBox1.Text += "此查詢語句一共查到[" + ds1.Tables[0].Rows.Count.ToString() + "]條記錄!"; 27 textBox1.Text += "此查詢語句查到[" + ds1.Tables[0].Rows[0]["SNAME"].ToString().Trim() + "]!"; 28 }
輸出:
把之前的執行變更數據庫內部數據的方法稍作修改!
1 public static int ExecuteNonQueryWithP(string connectionString, string SQLString, OracleParameter[] cmdParms) 2 { 3 using (OracleConnection connection = new OracleConnection(connectionString)) 4 { 5 OracleCommand cmd = new OracleCommand(); 6 7 PrepareCommand(cmd, connection, null, SQLString, cmdParms); 8 int val = cmd.ExecuteNonQuery(); 9 cmd.Parameters.Clear(); 10 return val; 11 } 12 }
測試代碼:
1 private void button9_Click(object sender, EventArgs e) 2 { 3 4 string t_sid = "20171116"; 5 string t_sname = "測試"; 6 string t_age = "100"; 7 string t_password = "1314520ln"; 8 // string sql = "insert into student(SID,SNAME,SAGE,SPASS) values('" + sid + "','" + sname + "','" + age + "','" +password+ "')"; 9 string sql = "insert into student(SID,SNAME,SAGE,SPASS) values(:t_sid,:t_sname, :t_age,:t_password)"; 10 OracleParameter[] parameters = { 11 new OracleParameter("t_sid ",OracleDbType.Varchar2), 12 new OracleParameter("t_sname",OracleDbType.Varchar2), 13 new OracleParameter("t_age",OracleDbType.Varchar2), 14 new OracleParameter("t_password",OracleDbType.Varchar2) 15 }; 16 parameters[0].Value = t_sid; 17 parameters[1].Value = t_sname; 18 parameters[2].Value = t_age; 19 parameters[3].Value = t_password; 20 int i=-1; 21 string flag = string.Empty; 22 try 23 { 24 i= ExecuteNonQueryWithP(connectionString, sql, parameters); 25 } 26 catch (Exception ex) 27 { 28 textBox1.Text = ex.Message; 29 } 30 if (i == -1) 31 { 32 flag = "插入失敗!"; 33 } 34 else { 35 flag = "插入成功!"; 36 } 37 textBox1.Text += "插入結果是:[" + flag + "]!"; 38 39 }
輸出:
之前的方法也都可稍作修改,相必也看到了,本來一句拼接語句,為了躲避拼接的麻煩,但是似乎制造了更大的“麻煩”,一行代碼變成了幾十行代碼,這就是代價。至於值不值,自在個人心中!
其實很多很簡單的方法后面都隱藏了很多東西,為了簡單,往往要付出一定的代價,舉個例子,java中的system.out.println();為什么什么都能打印出來嗎?如果去翻看底層代碼,你會發現為了讓你實現system.out.println()什么都能打印,底層支撐這個方法的代碼至少超過1000行! 再bb一句,java的system.out.println()的確比c#中Console.WriteLine(" ")吊,支持的更廣!
1.4 OracleDataReader使用
OracleDataReader一般是用於用來操作多條記錄的,可以用OracleDataReader對一個查詢語句的所有記錄做一個遍歷!OracleDataReader的遍歷要利用它的一些屬性和方法。它的方法和屬性對應表如下:
以上來自微軟官網,我挑了幾個重要的方法和屬性進行了嘗試!一些重要的屬性:
實現返回OracleDataReader的方法如下:(參數化,傳入數組的可稍作修改!)
1 //返回OracleDataReader 2 public static OracleDataReader ExecuteReader(string connectionString, string strsql) 3 { 4 OracleConnection conn = new OracleConnection(connectionString); 5 conn.Open(); 6 OracleCommand cmd = new OracleCommand(strsql, conn); 7 try 8 { 9 //CommandBehavior是一個枚舉類型 10 OracleDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection); 11 12 return rdr; 13 } 14 catch 15 { 16 conn.Close(); 17 throw; 18 } 19 }
測試代碼:
1 private void button6_Click(object sender, EventArgs e) 2 { 3 try 4 { 5 string sql1 = @"select * from student where sid='100' "; 6 OracleDataReader odr = ExecuteReader(connectionString, sql1); 7 textBox1.Text = odr.GetName(2).ToString()+"---";//返回第三列的列名 8 textBox1.Text += odr.HasRows + "---";//返回是否查詢到數據 9 textBox1.Text += odr.FieldCount + "---";//返回查詢到結果每行的欄位數 10 odr.Close(); 11 } 12 catch (Exception ex) 13 { 14 textBox1.Text += ex.Message; 15 } 16 17 }
輸出:
掌握了這舉個簡單的屬性之后,就能完成一些最簡單的業務了,比如遍歷查詢記錄集,也就將查詢到一條或n條數據全顯示出來:
1 private void button10_Click(object sender, EventArgs e) 2 { 3 try 4 { 5 string sql1 = @"select * from student "; 6 OracleDataReader odr = ExecuteReader(connectionString, sql1); 7 8 for (int j = 0; j < odr.FieldCount;j++ ) { 9 textBox1.Text += odr.GetName(j).ToString() + "\t"; 10 } 11 textBox1.Text += "\r\n"; 12 while(odr.Read()){ 13 for(int i=0;i<odr.FieldCount;i++){ 14 textBox1.Text += odr[i] + "\t"; 15 } 16 textBox1.Text += "\r\n";//換行 17 18 19 } 20 odr.Close();//OracleDataReade用完一定要關閉 21 } 22 catch (Exception ex) 23 { 24 textBox1.Text += ex.Message; 25 } 26 27 }
輸出:
看到這個數據表設置,很自然就想到一定要實現一個登陸的業務了,要實現登陸要不將用戶輸入的sname和spass逐條與數據庫中的記錄集做比對,一旦符合就跳出循環,要不就把SQL語句條件限定死,看是否可以read到數據,即HasRows數據,這個寫沒多大意義。一般比較復雜的業務都有可能涉及到在遍歷中作比較,即第一種方式。實現如下:
1 private void button11_Click(object sender, EventArgs e) 2 { 3 bool flag = false; 4 5 if (textBox2.Text == string.Empty || textBox3.Text == string.Empty) 6 { 7 MessageBox.Show("賬戶和密碼不能為空!"); 8 } 9 else { 10 11 try 12 { 13 string sql1 = @"select * from student "; 14 OracleDataReader odr = ExecuteReader(connectionString, sql1); 15 16 while (odr.Read()) 17 { 18 19 if (odr["sname"].ToString() == textBox2.Text && odr["spass"].ToString() == textBox3.Text) { 20 21 textBox1.Text= "你好!" + odr["sname"].ToString() + "你今年" + odr["sage"].ToString(); 22 flag = true; 23 break; 24 } 25 26 } 27 if(flag==false){ 28 textBox1.Text = "請先注冊!"; 29 } 30 flag = false; 31 odr.Close();//OracleDataReade用完一定要關閉 32 } 33 catch (Exception ex) 34 { 35 textBox1.Text += ex.Message; 36 } 37 38 39 }
輸出:
到此有關c#操作oracle最最基本簡單的操作就結束了,之所以是最最,因為這些只是冰山一角,學會了這些就能完成什么課程設計,畢業設計以及公司的什么實習生級別的業務什么的了!
《暫時 end》