【ado.net访问 数据库技术】 Net 6 开发系列(已完结)_哔哩哔哩_bilibili
目前认为应该是和JDBCTemplate类似的东西。
1.Connection连接对象
Connection是数据库连接对象,用于与数据库建立连接。SqlServer连接类是SqlConnection,Mysql的连接类是MySqlConnection,Oracle数据库连接类是OracleConnection,这些类都实现了IDbConnection接口
1.1 SqlConnection
常用方法
| 方法名 | 描述 |
|---|---|
| Open() | 打开数据库连接 |
| Close() | 关闭数据库连接 其实包含了Disopose()的功能 |
| Dispose() | 执行与释放或重置非托管资源关联的应用程序定义的任务 |
| BeginTransaction() | 开始数据库事务 |
| BeginTransaction(IsolationLevel) | 以指定的隔离级别启动数据库事物 |
| BeginTransaction(IsolationLevel,string) | 以指定的隔离级别和事务名称启动数据库事物 |
| CreateCommand | 创建并返回与SqlConnection关联的SqlCommand对象 |
// 1. nuget安装程序集 引入
using System.Data.SqlClient;
// 2. 创建连接对象
// 告诉连接对象服务器地址 用户名 密码 数据库
SqlConnection connection = new("server=10.10.11.248;uid=sa;pwd=password;database=LearnADO");
// 3. 打开数据库连接
connection.Open();
Console.WriteLine(connection.State);
Console.WriteLine(connection.Database);
Console.WriteLine(connection.ServerVersion);
Console.WriteLine(connection.ConnectionTimeout);
// 4. 关闭数据库连接
connection.Close();
2.自动释放
using 关键字可以自动释放资源
using System.Data.SqlClient;
SqlConnection connection;
// 1. using 自动释放资源
using (connection = new("server=10.10.11.248;uid=sa;pwd=Ff17301576208;database=LearnADO"))
{
// 2. 打开数据库连接
connection.Open();
Console.WriteLine(connection.State);
// 3. using {} 结束的时候会自动释放资源
}
Console.WriteLine(connection.State);
//C# 7.0以后的简写方式
// 1. connection 作用域返回最后一行进行关闭
// 2. 此方法运行结束时关闭
using SqlConnection connection = new SqlConnection("server=10.10.11.248;uid=sa;pwd=Ff17301576208;database=LearnADO");
connection.Open();
Console.WriteLine(connection.State);
Console.WriteLine(connection.State);
using
- 引用命名空间
- 释放资源
- 为什么以前定义的变量不需要释放资源呢? 答:GC(垃圾回收器)封装了一些算法,这些算法规定了什么时候回收资源。我们自己定义的变量不需要关系它有没有释放掉,因为GC会帮助我们自动回收资源。
- 既然已经有了GC为什么还需要手动释放资源呢?
答:GC只能回收托管资源(由 .Net CLR 管理的资源),而
SQLConnection属于非托管资源。 - 怎么知道一个资源是非托管资源呢?
答:如果它实现了
IDisposable接口,那么就可以使用using来手动释放资源。
3.Command 命令对象
构造方法
| 方法名 | 描述 |
|---|---|
| SqlCommand() | 初始化SqlCommand类的新实例 |
| SqlCommand(string) | 使用查询的文本初始化SqlCommand实例 |
| SqlComamnd(string,SqlConnection) | 使用查询文本和SqlConnection初始化SqlCommand实例 |
| SqlCommand(string,SqlConnection,SqlTransaction) | 使用查询文本、SQL Connection以及SqlTransaction初始化SqlCommand实例 |
常用属性
| 名称 | 描述 |
|---|---|
| CommandText | 获取或设置要在数据源中执行的Transact-SQL语句、表名或存储过程 |
| CommandTimeout | 获取或设置在终止尝试执行命令并生成错误之前的等待时间 s为单位 |
| CommandType | 获取或设置一个值,该值指示解释 CommandText 属性的方式 |
| Connection | 获取或设置SqlCommand对象使用的SqlConnection |
| Parameters | 获取SqlParameterCollection |
| Transaction | 获取或设置要在其中执行SqlTransaction的SqlCommand |
常用方法
| 方法名 | 描述 |
|---|---|
| Cancle() | 尝试取消SqlCommand的执行 |
| CreateParameter() | 创建SqlParameter对象实例 |
| Dispose() | 执行与释放或重置非托管资源关联的应用程序定义的任务 (继承自DbCommand) |
| Dispose(Boolean) | 释放由DbCommand占用的非托管资源,还可以另外再释放托管资源。 |
| ExecuteNonQuery() | 对连接执行Transact-SQL语句并返回受影响的行数 |
| ExecuteScalar() | 执行查询,并返回查询所返回的结果集中第一行的第一列。忽略其他列或行。 |
| ExecuteReader() | 将CommandText发送到Connection,并生成SqlDataReader |
// 一次简单的插入操作
using SqlConnection connection = new SqlConnection("server=10.10.11.248;uid=sa;pwd=password;database=LearnADO");
connection.Open();
// 编写Sql语句
string sql = "insert into users values('Oho','123312')";
//创建Command对象
SqlCommand cmd = new();
//设置连接对象
cmd.Connection = connection;
cmd.CommandText = sql;
cmd.ExecuteNonQuery();//执行增删改
//简写以上代码
using SqlConnection connection = new ("server=10.10.11.248;uid=sa;pwd=Ff17301576208;database=LearnADO");
connection.Open();
using SqlCommand cmd = new("select * from users", connection);
var res=Convert.ToInt32(cmd.ExecuteScalar());//获取首行首列 一般用于获取总条数
Console.WriteLine(res);
4.SqlDataReader
提供一种从SQL Server数据库中读取只进的行流的方式。在读取数据的过程中需要一直与数据库保持连接,适合数据量小的情况,执行效率还是可以的。我们将SqlDataReader读取数据的方式称为连接模式
属性
| 属性名 | 描述 |
|---|---|
| Connection | 获取与SqlConnection关联的SqlDataReader |
| Depth | 获取一个值, 用于指示当前行的嵌套深度 |
| FieldCount | 获取当前行中列数 |
| HasRows | 获取一个值,该值表示SqlDataReader是否包含一行还是多行 |
| IsClosed | 检索一个布尔值,表示是否已经关闭指定的SqlDataReader实例 |
| Item[Int32] | 在给定列序号的情况下,获取指定列的以本机格式表示的值 |
| Item[String] | 在给定列名称情况下,获取指定列以本机格式表示的值 |
| RecordsAffected | 获取执行Transact-SQL语句所更改、插入或删除的行数 |
| VisibleFieldCount | 获取SqlDataReader中未隐藏的字段数目 |
方法
| 方法名 | 描述 |
|---|---|
| Close() | 关闭连接 |
| Dispose() | 释放资源 |
| GetName(Int32) | 获取指定列名称 |
| GetOriginal(String) | 获取指定列名的序号 |
| Read() | 让SqlDataReader前进到下一条记录,返回bool |
| ... | ... |
//一次读取数据的尝试
using SqlConnection connection = new ("server=10.10.11.248;uid=sa;pwd=password;database=LearnADO");
connection.Open();
using SqlCommand cmd = new("select * from users", connection);
using var sqlDataReader = cmd.ExecuteReader();
//行流
while (sqlDataReader.Read()) {
/*var uidIndex = sqlDataReader.GetOrdinal("uid");
var uidValue = sqlDataReader.GetValue(uidIndex); //在此情况下 uidValue的类型为Object 被自动装箱 所以需要强制转换
var nameValue = sqlDataReader.GetValue(1);
var passwordValue = sqlDataReader["password"];*/ //也可以使用这种方式来获取值 但是也需要强制抓换
var user = new Users(
sqlDataReader.GetInt32(0),
sqlDataReader.GetString(1),
sqlDataReader.GetString(2)
);
Console.WriteLine($"uid:{user.uid} username:{user.username} password:{user.password}");
}
//先封装一次
record Users(int uid, string username, string password);
断开模式与连接模式
连接模式 :SqlDataReader 就是连接模式, 需要一直与数据库保持连接。
断开模式:SqlDataAdapter 就是断开模式,一次连接取得数据之后,即可断开
5. SqlDataAdapter 适配器 (常用)
表示用于填充DataSet(表示数据的内存中的数据库,它可以由多个table组成)和更新SQL Server数据库的一组数据命令和一个数据库连接。适配器模式也被称为断开模式。
一次连接取得数据之后,即可断开,在用户非常多的情况下,不会占用太多的连接池资源。 还有一点,就是一次性的从数据库中取得了数据之后,这些数据是存在内存中的,而不会再去操作数据库,所以你对这些数据做任何的操作,都只是修改内存,不会改变数据库中的内容。
缺点:
- 效率相对于 SqlDataReader低
- 占内存多
构造方法
| 方法名 | 描述 |
|---|---|
| SqlDataAdapter() | |
| SqlDataAdapter(SqlCommand) | 初始化SqlDataAdapter实例,用地指定的SqlCommand作为SelectCommand的属性 |
| SqlDataAdapter(String,SqlConnection) | 使用SqlDataAdapter和SelectCommand对象初始化SQL Connection类的一个新实例==(用的最多)== |
| SqlDataAdapter(String,String) | 使用SqlDataAdapter和一个连接字符串初始化SelectCommand类的一个新实例 |
//一次简单的尝试
using SqlConnection connection = new ("server=10.10.11.248;uid=sa;pwd=Ff17301576208;database=LearnADO");
// 1. 此处并未 手动打开连接
// 2. 适配器
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
// 3. 创建命令对象
sqlDataAdapter.SelectCommand=new SqlCommand("select * from users",connection);
// 4. 读取数据
DataSet dataSet = new DataSet();
// 5. 填充数据到DataSet
sqlDataAdapter.Fill(dataSet);
Console.WriteLine(dataSet.Tables[0].Rows.Count);
DataTable
将DataTable封装成List集合
using SqlConnection connection = new ("server=10.10.11.248;uid=sa;pwd=password;database=LearnADO");
// 1. 此处并未 手动打开连接
// 2. 适配器 数据一旦拿到了就会把连接断开 故名断开模式
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
// 3. 创建命令对象
sqlDataAdapter.SelectCommand=new SqlCommand("select * from users",connection);
// 4. 读取数据
//DataSet dataSet = new DataSet(); //由多个DataTable组成
DataTable dataTable = new DataTable();// 只有一张表 所以使用DataTable
// 5. 填充数据到Datatable
sqlDataAdapter.Fill(dataTable);
//在面向对象中 DataTable使用率并不高,因为它不符合面向对象思想
//更多使用的是List<T> 泛型集合
List<User> users = new List<User>();
//DBNull与C#中的Null不一样 DBNull 数据库中为空值会报错
foreach (DataRow row in dataTable.Rows) {
//使用List封装
User user = new();
user.Uid = Convert.ToInt32(row["uid"]);
user.Username = row["username"].ToString();
//数字类型一定要记得 非空判断
if (row["password"] is not DBNull) {
//非空判断
user.Password=row["password"].ToString();
}
}
//输出
//1. List更符合面向对象思想
//2. row 在操作列名的时候 有可能会拼写错误
//3. List 集合操作速度比DataTable快,并且属于类型安全
foreach (User user in users) {
Console.WriteLine($"id={user.uid}\t username={user.username}\t password={user.password}");
}
//封装
record User(int uid, string username, string password);
6.配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--控制台的配置文件名称一定要叫 App.Config-->
<!--连接字符串-->
<connectionStrings>
<!--10.10.11.248 服务器的连接字符串-->
<add name="248ConnString" connectionString="server=10.10.11.248;uid=sa;pwd=password;database=LearnADO"/>
</connectionStrings>
<appSettings>
<add key="databaseIP" value="10.10.11.248"/>
</appSettings>
</configuration>
string connectionString = ConfigurationManager.ConnectionStrings["248ConnString"].ConnectionString;
var s = ConfigurationManager.AppSettings["databaseIP"].ToString();
7.数据连接池
知道现在编程什么是最宝贵的?数据库连接,它其实是很耗费资源的。这里我们就连接池这个问题我们来一起讨论讨论吧。数据库连接池是为了更有效地利用数据库连接的最重要措施。它对于一个大型的应用系统的性能至关重要,特别是Web应用。ADO.NETData Provider(以下简称Data Provider))会帮我们管理连接池,所以有人说使用连接池就像游儿童池一样轻松。但并不是说有了Data Provider程序员就万事无忧的,不正确地使用连接池可能导致你的应用在池里淹死。
什么是ADO.NET连接池?
ADO.Net连接池是Data Provider提供的一个机制,使得应用程序使用的连接保存在连接池里而避免每次都要完成建立/关闭连接的完整过程。要理解连接池,先要理解程序里SqlConnection.Open()、SqlConnection.Close()和打开/关闭一个"物理连接”的关系。
Data Provider在收到连接请求时建立连接的完整过程是:先连接池里建立新的连接(即"逻辑连接") ,然后建立该"逻辑连接"对应的"物理连接"。建立"逻辑连接"一定伴随着建立"物理连接"。 Data Provider关闭一个连接的完整过程是先关闭"逻辑连接"对应的"物理连接"然后销毁"逻辑连接"。销毁"逻辑连接"一定伴随着关闭"物理连接"。SqlConnection.Open)是向Data Provider请求一个连接, Data Provider不一定需要完成建立连接的完整过程,可能只需要从连接池里取出一个可用的连接就可以;SqlConnection.Close()是请求关闭一个连接, Data Provider不一定需要完成关闭连接的完整过程,可能只需要把连接释放回连接池就可以。
8.参数化查询
解决sql注入问题
- 存储过程
- 参数化
参数化:将要传递的sql参数不通过拼接的方式进行组装sql语句,而是通过sql变量的形式进行传递
select * from users where Account=@account and Pwd=@pwd;
SqlParameter对象
表示SqlCommand参数,参数名称不区分大小写。
| 构造函数 | 描述 |
|---|---|
| SqlParameter() | 使用参数名称和SqlParameter初始化 |
| SqlParameter(String,Object) | 使用提供的参数名称和数据类型初始化SqlParameter类的新实例 |
| SqlParameter(String,SqlDbType,Int32) | 使用参数名称、SqlDbType和大小初始化SqlParamater类的新实例 |
| SqlParameter(String,SqlDbType,Int32,ParameterDirection,Boolean,Byte,Byte,String,DataRowVersion) | 使用提供的参数名称、参数类型、参数大小、ParameterDirection、参数精度、参数小数位数、源列、要使用的DataRowVersion以及参数值初始化SqlParameter类的新实例 |
| ... | ... |
一次登陆操作
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
//读取配置文件
string connectionString = ConfigurationManager.ConnectionStrings["248ConnString"].ConnectionString;
using SqlConnection conn=new(connectionString);
conn.Open();
SqlCommand cmd = new SqlCommand("select count(*) from users where username=@username and password=@password",conn);
SqlParameter parameter1=new SqlParameter();
parameter1.ParameterName="username";// 参数名
parameter1.Value="Aealen"; //参数值
parameter1.Size=20; //参数大小
parameter1.SqlDbType=SqlDbType.VarChar; //参数类型
SqlParameter parameter2=new ();
parameter2.ParameterName="password";
parameter2.Value="1221222";
parameter2.Size=20;
parameter2.SqlDbType=SqlDbType.VarChar;
cmd.Parameters.Add(parameter1);//添加参数到命令对象中
cmd.Parameters.Add(parameter2);
int cmdRes = Convert.ToInt32(cmd.ExecuteScalar());
if (cmdRes > 0) {
Console.WriteLine("登陆成功");
}
else {
Console.WriteLine("登陆失败");
}
插入操作
Q:指定参数名与参数值 ,那么参数类型与大小呢?
A:如果类型合适,会自动推断类型和大小
//读取配置文件
string connectionString = ConfigurationManager.ConnectionStrings["248ConnString"].ConnectionString;
using SqlConnection conn=new(connectionString);
conn.Open();
using SqlCommand cmd = new SqlCommand("Insert into users values(@username,@password)",conn);
SqlParameter[] parameters = {
new SqlParameter("username","AowuBaka"), //指定参数名与参数值 ,那么参数类型与大小呢? A:如果类型合适,会自动推断类型和大小
new SqlParameter("password","2212331"), //如果要使用此重载传入 0 需要使用 Object(0)
};
//创建参数对象
cmd.Parameters.AddRange(parameters);
修改操作
using SqlCommand cmd = new SqlCommand("update users set password=@newPwd where uid=@uid",conn);
SqlParameter[] parameters= {
new ("newPwd",SqlDbType.VarChar,20),
new SqlParameter("uid",SqlDbType.Int,4)
};
parameters[0].Value = "newpassword";
parameters[1].Value = 3;
cmd.Parameters.AddRange(parameters);
cmd.ExecuteNonQuery();
查询操作!
string connectionString = ConfigurationManager.ConnectionStrings["248ConnString"].ConnectionString;
using SqlDataAdapter sqlDataAdapter = new("select * from users where username like @username", new SqlConnection(connectionString));
SqlParameter sqlParameter = new("username", SqlDbType.VarChar);
sqlParameter.Value = "%" + "e" + "%";
sqlDataAdapter.SelectCommand.Parameters.Add(sqlParameter);
DataTable dataTable = new DataTable();
sqlDataAdapter.Fill(dataTable);
批量删除
循环单个删除
事务相关
如批量删除, 要么全部成功,要么全部失败。
//读取配置文件
string connectionString = ConfigurationManager.ConnectionStrings["248ConnString"].ConnectionString;
SqlConnection connection=new (connectionString);
connection.Open();
//打开事务 并设置隔离级别
SqlTransaction sqlTransaction = connection.BeginTransaction();
try {
var sqlCommand = new SqlCommand("delete from users where uid=@uid", connection, sqlTransaction); //以事务方式执行
int[] arr = { 3, 7, 8, 9, 10, 11, 12 };
foreach (int item in arr) {
sqlCommand.Parameters.Add(new("uid", item));
sqlCommand.ExecuteNonQuery();
sqlCommand.Parameters.Clear(); //清除已经执行过的参数。
}
sqlTransaction.Commit(); //提交事务
}
catch (Exception e) {
Console.WriteLine(e);
sqlTransaction.Rollback();
}
存储过程相关
存储过程
create proc p_users_insert(
@username varchar(20),
@password varchar(20),
@code int output,
@msg varchar(20) output
)as
begin
insert into users values(@username,@password);
set @msg='插入成功';
set @code=200;
endxxxxxxxxxx18 1alter proc p_users_insert(2 @username varchar(20),3 @password varchar(20),4 @code int output,5 @msg varchar(20) output6)as 7begin 8 insert into users values(@username,@password);9 set @msg='插入成功';10 set @code=200;11endcreate proc p_users_insert12(13 @username varchar(20),14 @password varchar(20)15) as 16begin 17 insert into users values(@username,@password) 18end
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
//读取配置文件
string connectionString = ConfigurationManager.ConnectionStrings["248ConnString"].ConnectionString;
SqlConnection connection=new (connectionString);
connection.Open();
//填写存储过程名称
SqlCommand cmd = new SqlCommand("p_users_insert", connection);
cmd.CommandType = CommandType.StoredProcedure;//一定要设置 cmd类型 默认为Text
SqlParameter[] parameters = {
new("username", SqlDbType.VarChar, 20),
new("password", SqlDbType.VarChar, 20),
new("code",SqlDbType.Int,4), //输出参数 int
new("msg",SqlDbType.VarChar,20) //输出参数 string
};
parameters[0].Value = "ProcOut2";
parameters[1].Value = "ssss";
parameters[2].Direction = ParameterDirection.Output;//设置为输出参数
parameters[3].Direction = ParameterDirection.Output;
cmd.Parameters.AddRange(parameters);
cmd.ExecuteNonQuery();
//一定要在执行完存储过程之后才能获取输出参数的值
Console.WriteLine(parameters[2].Value);
Console.WriteLine(parameters[3].Value);
- 输出参数一定要在存储过程执行之后 才能获取。!!
9.DbHelper封装
在上述案例中,存在太多的重复代码了,开发过程是绝不能容忍如此多的重复代码。上述代码中,除了sql 与 字段参数有可能不一样以外,其他创建连接等代码都是重复的,我们可以将上述操作分为3类。
- 读取
- 写入
- Count计数
如此,我们可以将上述3类封装为三个方法,将sq语句与字段参数当作方法参数传入,达到重用作用。
总结:emmm,就是封装一个类似JDBCTemplate的工具类!
