如鹏网学习笔记(六)ADO.Net基础

2.sql 语句和 sql 参数

带有参数的
sql 语句和 sql 参数会分开传入数据库服务器中,服务器先将 sql
语句进行编译,然后将 sql 参数导入编译后的 sql
语句中(在此过程中自动对特殊字符进行转义),从而从根源上防止了 sql
注入的发生。

string connStr = "server=127.0.0.1; port=3306; user id=user1; Password=pass1; database=data1; pooling = true";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "select  count(*) from student where Name = @Name;";
MySqlParameter[] sps = new MySqlParameter[1];
sps[0] = new MySqlParameter("@Name", "Tom");
cmd.Parameters.AddRange(sps);
object obj = cmd.ExecuteScalar(); //可能未null
int count = 0;
if (!obj.Equals(DBNull.Value))
{
    count = Convert.ToInt32(obj);
}   
conn.Close();  

  7,public static DataTable ExecuteQuery(string sql, params
MySqlParameter[] parameters)

2.引用 mysql.data.dll ,在此基础上进行开发,推荐。

完全由代码完成通讯过程。

优点:可以方便的使用各种功能,

缺点:在使用的过程中要注意许多坑,如:异常的捕获,sql
注入 , 非托管资源的释放等。


 


 

  3,可以Insert、LAST_INSERT_ID()在同一个连接中单独执行,也可以把LAST_INSERT_ID()放到insert语句后面用;
    分割(使用ExecuteScalar执行即可)

1.使用 vs 自带的可视化工具,不推荐。

在 vs
的项目中添加
‘数据集’,然后通过可视化的工具添加数据库为数据源,默认可添加 SQL Server
和 Oracle 等,添加 Mysql 前需要额外安装组件。

优点,自带
sql 语句中特殊字符的转义,不会出现 sql
注入的问题,配合数据绑定可以在项目前期快速推进项目进度。

缺点:可视化工具的使用并不流行,不利于开发团队的招募和项目后期的修改维护。许多常用的功能很难使用,如:存储过程,事务,连接池控制等。

    案例2
    cmd.CommandText = “Select Password from T_Users where UserName
= ‘admin'”;
    string pwd = (string)cmd.ExecuteScalar();
    if(string.isNullOrEmpty(pwd))
    {
      Console.WriteLine(“找不到admin”);
    }
    else
    {
      Console.WriteLine(“admin的密码:”+pwd);
    }

4.存储过程

不论是 sql
语句还是事务,在传输指令的时候都需要耗费大量的时间,数据库编译这些指令也需要耗费大量时间,这不利于高访问量的数据库的运行。

存储过程是将 sql
指令写在数据库中,此时数据库直接完成编译,与数据库通讯是只需要传递参数即可。节省了传输时间和编译时间。

conn.Open();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "SpFillStudent";

MySqlParameter paraMoney;
paraMoney = cmd.Parameters.Add("@inMoney", MySqlDbType.Decimal);
paraMoney.Direction = ParameterDirection.Input;
paraMoney.Value = 50;

MySqlParameter paraTradeType;
paraTradeType = cmd.Parameters.Add("@inTradeType", MySqlDbType.String);
paraTradeType.Direction = ParameterDirection.Input;
paraTradeType.Value = "会员卡充值";

MySqlParameter paraPayDetailStr;
paraPayDetailStr = cmd.Parameters.Add("@inPayDetailStr", MySqlDbType.String);
paraPayDetailStr.Direction = ParameterDirection.Input;
paraPayDetailStr.Value = "";

MySqlParameter paraOutResult;
paraOutResult = cmd.Parameters.Add("@outResult", MySqlDbType.String);
paraOutResult.Direction = ParameterDirection.Output;

cmd.ExecuteNonQuery();
string result = (string)paraOutResult.Value;
conn.Close();

 

 

1.纯 sql 语句,执行后有三种返回方式:

cmd.ExecuteScalar();  //
查询结果仅一行一列,直接接收

string connStr = "server=127.0.0.1; port=3306; user id=user1; Password=pass1; database=data1; pooling = true";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();
MySqlCommand cmd = conn.CreateCommand();
string name = "Tom";
cmd.CommandText = string.Format("select  count(*) from student where Name = '{0}';", name);
object obj = cmd.ExecuteScalar(); //可能未null
int count = 0;
if (!obj.Equals(DBNull.Value))
{
  count = Convert.ToInt32(obj);
}  
conn.Close();  

 

 

MySqlDataReader
reader = cmd.ExecuteReader();  // 通过 reader 获得大量数据

string connStr = "server=127.0.0.1; port=3306; user id=user1; Password=pass1; database=data1; pooling = true";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = string.Format("select  Name from student;");
MySqlDataReader reader = cmd.ExecuteReader();
List<string> list = new List<string>();
while (reader.Read())
{
    list.Add(reader.GetString("Name"));
}
reader.Close();
conn.Close();

 

cmd.ExecuteNonQuery();  //
获得增删改语句执行后影响的行数

string connStr = "server=127.0.0.1; port=3306; user id=user1; Password=pass1; database=data1; pooling = true";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();
MySqlCommand cmd = conn.CreateCommand();
string name = "Tom";
cmd.CommandText = string.Format("update student SET Name='Tommy' where Name = '{0}';", name);
int count = cmd.ExecuteNonQuery();
conn.Close();

 

普通
sql 语句的缺点:如果上面例子中的 name 参数中带有单引号,整个 sql
语句将执行错误,因为未对特殊字符进行转义。主要特殊字符有单引号,反斜杠,#
等,而这些特殊字符在不同的使用情况下有时需要转义,有时不需要转义。Mysql
提供了类似 QUOTE(str) 这样的字符串处理函数,但不能完全满足要求。

这就是
sql 注入,sql
注入的概念网上资料较多,在此不再赘述,而常用的解决方案是采用参数化的 sql
语句。

  1,ADO.Net的连接字符串写到配置文件中。

2.使用连接创建 sql 命令并执行

    SQLServer的两种连接方式:
    Windows 身份验证(互相信任的局域网中);
    SQLServer身份验证(使用SQLServer用户名密码验证,密码要复杂一点)。
    Windows 身份验证还有一个用途:忘了sa密码,可以本机进去改。

两种开发方式

  1,不能用插入后获取最大值的方法,有并发问题

完整的通讯过程

  遍历DataTable

1.建立连接,连接分 “长连接” 和 “短连接” 

长连接在高频次的通讯时快速高效,但是占用资源,在并发访问下容易耗尽网络资源,对于带宽较低的局域网来说,如果大量使用长连接,会占用网速,影响使用体验。

短连接在使用时
open, 使用完成后
close,此时连接资源会进入连接池,等待下次连接时使用。虽然资源未被释放掉,但连接池的开销不大,是完全可以接受的。短连接一样需要考虑并发问题。

短连接在使用完成后可以直接
dispose,或者使用 using(){}
来限定连接的作用域,使用完成后自动释放掉,不进入连接池,这种方式资源占用最少,但在需要反复建立连接的情况下连接效率较低。

在实际开发的时候最好使用短连接,并在使用结束后关闭并放入连接池。

string connStr = "server=127.0.0.1; port=3306; user id=user1; Password=pass1; database=data1; pooling = true";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open(); 
conn.Close();

  1,是由于Sql语句的拼接造成的,
    在程序接收用户的输入时,需要考虑用户输入的内容对数据库操作产生的影响,

3.事务

多条 sql 语句的组合会出现某一条 sql 语句执行出错,而其他 sql
语句顺利执行的情况,这可能与预期不符合。此时需要使用事务。

事务在执行出错时可以回滚。

事务往往带有多条 sql 语句,在使用参数化的 sql
语句时要注意参数名称不能相同。

conn.Open();
MySqlCommand cmd = conn.CreateCommand();
MySqlTransaction myTrans = conn.BeginTransaction();
cmd.Connection = conn;
cmd.Transaction = myTrans;
cmd.CommandText = "";
cmd.ExecuteNonQuery();
myTrans.Commit();
conn.Close();

 

 

四、基本数据类型为空的问题

  2,事务(Transaction)有四大特征:
    原子性、一致性、隔离性、持久性
    原子性指的是:几个操作要么都成功,要么都失败

  5,执行SQL语句(数据库上点右键“新建查询”,不要点【调试】)

  1,连接字符串一般配置到App.config(网站是Web.config)中的<connectionStrings>段中
    然后使用ConfigurationManager类(需要添加对System.Configuration的引用)读取
    string connStr =
ConfigurationManager.ConnectionStrings[“connStr”].ConnectionString
    

    示例代码:
    MySqlTransaction tx = conn.BeginTransaction();
    try
    {
      MySqlHelper.ExecuteNonQuery(conn,”Update T_Accounts set
Amount = Amount-1000 where Number =’0001′”);
      string s = null;
      s.ToLower();//制造异常
      MySqlHelper.ExecuteNonQuery(conn, “Update t_accounts Set
Amount=Amount+1000 where Number=’0002′”);
      tx.Commit();
    }
    catch(Exception ex)
    {
      tx.Rollback();
    }

  3,新建数据库、新建表
    SQLServer的数据类型
    (varchar和nvarchar;nvarchar(n)和nvarchar(MAX);
    long是bigint;
    获取前10条数据: select top 10 * from t_persons )、
    SQLServer的自动增长(是标识)、不需要特殊指定编码

    注意:
      Reader的遍历、读取时需要Connection保持连接,如果关闭了Connection,使用会出错
      也可以根据列序号获取列的值,效率略高,不过程序不容易读;通过reeder.GetOrdinal(“Age”)获取列名对应的序列号

  4,参数化查询的查询参数个数不确定,可变长度参数会更方便

  2,Management Studio的使用

  2,新建项目,添加引用——“扩展”,添加Mysql.Data;如果是直接解压版,然后直接添加对MySql.Data.dll的文件的引用
    static void Main(string[] args)
    {
      string connStr =
ConfigurationManager.ConnectionStrings[“connStr”].ToString();
      using (SqlConnection conn = new SqlConnection(connStr))
      using (SqlCommand cmd = conn.CreateCommand())
      {
        conn.Open();
        cmd.CommandText = “insert into t_fuxi “;
        int count = cmd.ExecuteNonQuery();
        Console.WriteLine(count+”受到影响”);
        Console.ReadKey();
      }
    }

 

  DataAdapter还可以对结果进行傻瓜化更新、删除、修改。

十一、事务基础

一、ADO.Net简介
  1,程序要通过SQL语句自动化的操作数据库,必须要用一个类库,
    类库要提供execute(“insert into …”)/executeQuery(“select *
from …”)类似的方法

ADO.Net基础

    2,”Server=localhost;Database=study1;uid=root;pwd=root;Charset=utf8″叫连接字符串,
      Server是Mysql服务器的地址,Database是连接的数据库,uid、pwd是用户名和密码,采用utf8编码

九、实现MySqlHelper

    3,conn.Open();在执行MySqlCommand之前一定要先打开数据库连接,否则会报错

 

  4,public static object ExecuteScalar(MySqlConnection conn,string
sql,params MySqlParameter[] parameters)

  1,安装,版本:2008有兼容性的问题,有bug、因此推荐安装SQLServer 2008
R2

  我们一般查询结果集就一个DataTable,DataAdapter的傻瓜化更新不适合正式的项目,因此有更简单的用法

    注意:
    1,所有SQL中都可以使用参数化查询传递;表名、字段名等不能使用参数化进行替换
    2,不要用SqlParameter(string parameterName,object
value)这个构造函数,
    因为(“Age”,0)会被匹配成Sqlparameter(string
parameterName,SqlDbType dbType)这个构造函数

    Update T_Employees Set Salary = Salary – 10 where Name =
‘Tom’
    Update T_Employees Set Salary = Salary + 10 where Name =
‘Jerry’

  1,有一个需求,类似于转账,从Tom的工资上转走10元,转到Jerry的工资上增加10元

    怎么解决?
    使用ISDBNull获取指定序号的列的值是否为null

  5,如果基于接口编程,只要改动CreateConnection就可以了,查询参数以Directory<string,object>传递
    如果使用Provider,连代码都不用改,改配置文件即可

  for(int i =0;i<dt.Rows.Count;i++)
  {
    DataRow row = dt.Rows[i];
    string name =
row.IsNull(“name”)?null:(string)row[“Name”];//NULL处理
    Console.WriteLine(“name=”+name);
  }

    案例1
    cmd.CommandText = “Select count(*) from T_Users”;
    long count = (long)cmd.ExecuteScalar();

  3,public static int ExecuteNonQuery(string sql,params
MySqlParameter[] parameters)

  用法1:
  DataSet ds = new DataSet();
  MySqlDataAdapter adapter = new MySqlDataAdapter(cmd);
  adapter.Fill(ds);
  DataTable table = ds.Tables[0];

  5,public static object ExecuteScalar(string sql,params
MySqlParameter[] parameters)

十三、SQLServer的使用

 

 

  DATAReader是服务器结果集游标的体现,所有查询出来的数据都在MySql服务器上。
  好处是:当查询结果数据量打的时候避免占用本地内存。

  DataSet是一个离线结果集容器,它把结果数据放到本地内存中。
  因为查询结果可能会包含多个表,因此DataSet包含若干DataTable(ds.Tables),
  DataTable包含若干DataRow(dt.Rows)

  注意: 把DataTable声明到using外,using外再使用查询结果

    int? age = null;
    if(!reader.IsDBNull(reader.GetOrdinal(“Age”)))
    {
      age = reader.GetInt32(“Age”);
    }

八、MySqlHelper方法规划

  1,安装MySql的.Net驱动mysql-connector-net-***.msi添加到项目的库中。
    如果安装遇到问题,则直接下载mysqlnetconnection(V4.5).zip。

  2,ADO.Net是.Net中提供的标准访问数据库的接口,访问不同的DBMS的底层方法是不一样的,ADO.Net把访问数据库的方法进行了统一,
    访问MYSql、Oracle、SqlServer等不同数据库的方法几乎是一样的

    注意:
    一定要保证代码中的名字和配置文件中的名字是一致的

  2,要在同一个连接中:select LAST_INSERT_ID()

  1,public static MySqlConnection CreateConnection()

  4,执行查询 MySqlDataReader

三、SQL注入漏洞

  2,每次操作数据库都要写一坨代码,太累,因此封装一个简化ADO.Net操作的库出来:
    配置文件的设置连接字符串;简化连接的创建;简化SQL的执行

  2,其他方法的实现

    为什么这样可以避免“SQL注入漏洞”,
    因此使用参数化查询,就可以对用户输入的内容进行判断和处理了,本质上是参数赋值

五、离线结果集DataSet

六、DataTable
  DataSet可以盛放多个查询结果集到DataTable

十、获得自动增长字段的值

七、封装一个库:分析

  2,对应的解决方法,参数化查询

  6,public static DataTable ExecuteQuery(MySqlConnection conn, string
sql, params MySqlParameter[] parameters)

十二、ADO.Net事务

    事务还有隔离级别、嵌套事务等问题

  4,获得自动增长列的值:
    Insert into t1(…) output insert.Id values(….)

  5,为了方便大部分情况下的小结果集,执行查询返回DataTable

    cmd.CommandText = “select * from T_Users”;
    using(MySqlDataReader reader = cmd.ExecuteReader())
    {
      while(reader.Read())
      {
        long id = reader.GetInt64(“Id”);
        string userName = reader.GetString(“UserName”);
        string passWord = reader.GetString(“Password”);
        Console.WriteLine(“id=”+id+”;UserName=”+userName+”;Password=”+passWord);
      }
    }

  2,连接字符串:
    server=ip;user id =sa;passWord = 密码;database = db1

  4,保存表设计修改的时候,如果报错“不允许保存更改”:
    工具→选项→Designers→把“阻止保存要求重新创建表的更高”勾选掉。
    遇到报错信息:要先仔细阅读。

  2,public static int ExecuteNonQuery(MySqlConnection conn,string
sql,params MySqlParameter[] parameters)

  3,测试几个方法,调用不需要自己控制连接的,再调用公用一个连接的

    如果执行从Jerry账上加10元的时候执行出错(使用SQL语法写错误模拟),那么就会出现总体丢失10元的问题,如果是转账呢?

    4,ExecuteNoQuery是执行Update、Insert、Delete等非查询语句,返回值为受影响的行数

  1,把T_Persons表的Name、Age列修改为“允许为空”,插入一条Name、Age为空的行
    执行后发现,对于空数据reader.Get**方法会抛异常SqlNullValueException,
    相当于问数据库“当前行的Name”是什么,数据库告诉你“不知道”。

  3,SQLHelper:
    把MySql替换成Sql就可以了

十四、ADO.Net连接SQLServer
  1,ADO.Net如何连接SQLServer:
    SQLServer驱动内置

  不过大部分项目中都会避免大查询结果,因此缺点就明显了
  读取的时候必须保持Connection,不仅用起来麻烦,而且会较长时间占用MySql服务器的连接资源

  1,要在一个连接中(否则要涉及到分布式事务)
    MySqlTransaction tx = conn.BeginTransaction();
    操作结束后执行tx.Commit()提交事务;
    如果执行出错,则tx.Rollback()回滚(当前事务的操作全部取消)

二、ADO.Net连接MYSQL

    代码解释:
    1,MySQLConnection、MySQLCommend实现了IDisposable接口,因此使用using进行资源回收

  3,如果一个操作要执行多条SQL语句,如果每条都打开一个连接——执行——关闭连接的话,效率会非常低,而且会有“事务”的问题。
    因此应该提供“打开、执行、关闭”这样的方法,也要提供“使用后现有连接执行的方法”。

  3,ADO.Net是规范,被不同的数据库厂商提供ADO.Net的实现,称之为ADO.Net驱动,每个厂商提供的驱动可以用来操作自己的数据库

  DataTable dt = new DataTable();
  dt.Load(reader);

    cmd.CommandText = “select count(*) from TUsers where
username=@username and password=@password”;
    cmd.Parameters.Add(new SqlParameter() { ParameterName =
“@UserName”, Value = username });
    cmd.Parameters.Add(new SqlParameter() { ParameterName =
“@Password”, Value = password });

  3,ExecuteScalar
    执行查询,并返回查询所返回的结果集中第一行的第一列,忽略其他行列。一般用来简单的获得自由一行一列的查询结果的值