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() + " "; 10 } 11 textBox1.Text += " "; 12 while(odr.Read()){ 13 for(int i=0;i<odr.FieldCount;i++){ 14 textBox1.Text += odr[i] + " "; 15 } 16 textBox1.Text += " ";//换行 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》