一、ADO.NET常用的对象
一、Connection对象
Connection对象也称为数据库连接对象,Connection对象的功能是负责对数据源的连接。所有Connection对象的基类都是DbConnection类。
Connection对象有两个重要属性:
(1)ConnectionString:表示用于打开 SQL Server 数据库的字符串;
(2)State:表示 Connection 的状态,有Closed和Open两种状态。
Connection对象有两个重要方法:
(1)Open()方法:指示打开数据库;
(2)Close()方法:指示关闭数据库。
1 SqlConnection connection = new SqlConnection( "server=localhost;database=pubs;uid=sa;pwd=''";); 2 //注意,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")是将当前时间格式化为类似于2008-10-09 00:00:03的形式的字符串
3 Response.Write("时间"+ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")+"当前数据库连接状态是:"+connection.State +"<br/>"); 4 connection.Open(); 5 Response.Write("时间" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "当前数据库连接状态是:" + connection.State + "<br/>"); 6 connection.Close(); 7 Response.Write("时间" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "当前数据库连接状态是:" + connection.State + "<br/>");
二、Command对象
Command对象也称为数据库命令对象,Command对象主要执行包括添加、删除、修改及查询数据的操作的命令。也可以用来执行存储过程。用于执行存储过程时需要将Command对象的CommandType 属性设置为CommandType.StoredProcedure,默认情况下CommandType 属性为CommandType.Text,表示执行的是普通SQL语句。
Command主要有三个方法:
ExecuteNonQuery():执行一个SQL语句,返回受影响的行数,这个方法主要用于执行对数据库执行增加、更新、删除操作,注意查询的时候不是调用这个方法。
1 OleDbConnection conn = new OleDbConnection(); 2 conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" +
3 "Data Source=" + Server.MapPath("person.mdb"); 4 conn.Open(); 5 string strSQL = "insert into grade values(12,'女','小张',78,86,98)"; 6 OleDbCommand Comm = new OleDbCommand(strSQL, conn); 7 Comm.ExecuteNonQuery(); 8 conn.Close();
ExecuteReader ():执行一个查询的SQL语句,返回一个DataReader对象。
1 SqlConnection Conn = new SqlConnection(); 2 Conn.ConnectionString = "server=localhost;database=pubs;uid=sa;pwd=''"; 3 Conn.Open(); 4 SqlCommand Comm = new SqlCommand("select * from Authors", Conn); 5 SqlDataReader dr = Comm.ExecuteReader(); 6 dg.DataSource = dr; 7 dg.DataBind(); 8 Conn.Close();
ExecuteScalar ():从数据库检索单个值。这个方法主要用于统计操作。ExecuteScalar ()这个方法是针对SQL语句执行的结果是一行一列的结果集,这个方法只返回查询结果集的第一行第一列。
1 OleDbConnection conn = new OleDbConnection(); 2 conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" +"Data Source=" + Server.MapPath("person.mdb"); 3 conn.Open(); 4 string strSQL = "select avg(数学) from grade"; 5 OleDbCommand Comm = new OleDbCommand(strSQL, conn); 6 double d = (double)Comm.ExecuteScalar(); 7
8 Message.Text = "所有人数学的平均成绩为:"+d.ToString()+"分"; 9 conn.Close();
【注意】在操作数据库的时候,为了提高性能,都遵循一个原则:数据库连接对象应该尽可能晚打开,尽可能早关闭
三、DataReader对象
DataReader对象是一个读取行的只读流的方式,绑定数据时比使用数据集方式性能要高,因为它是只读的,所以如果要对数据库中的数据进行修改就需要借助其它方法将所作的更改保存到数据库。
DataReader对象不能通过直接实例化,必须借助与相关的Command对象来创建实例
例如:用SqlCommand的实例的ExecuteReader()方法可以创建SqlDataReader实例。因为DataReader对象读取数据时需要与数据库保持连接,所以在使用完DataReader对象读取完数据之后应该立即调用它的Close()方法关闭,并且还应该关闭与之相关Connection对象。在.net类库中提供了一种方法,在关闭DataReader对象的同时自动关闭掉与之相关的Connection对象,使用这种方法是可以为ExecuteReader()方法指定一个参数,如: SqlDataReader reader =command.ExecuteReader(CommandBehavior.CloseConnection); CommandBehavior是一个枚举,上面使用了CommandBehavior枚举的CloseConnection值,它能在关闭SqlDataReader时关闭相应的SqlConnection对象。
并且DataReader对象读取数据有三种方式:
第一种是按查询的时候列的索引用指定的方式来读取列值,无需做相应转换,如GetByte(int i)就是读取第i列的值并且转换成byte类型的值。
这种方法的优点是指定列后直接将该列的直接读取出来了,无需再转换,缺点是一旦指定的列不能按照指定的方式转换时就会抛出异常,比如数据库里字段的类型是string类型或者该字段的值为空时按照GetByte(i)这种方式读取会抛出异常。
第二种方式就是按照列索引的方式读取,在读取的时候并不进行值转换,如:reader[5]就是读取第5列的值(这里reader是一个Reader对象的实例),这样得到的值是一个object类型的值,这也很好理解,因为在数据库可能存储各种类型的值,而object是所有类的基类,所以这个方法不会抛出异常。如果要得到它的正确类型,还需要根据数据库里的字段进行进行相应转换。
最后一种是按照列名的方式去读,并且在读的时候也不进行相应转换,得到的是object类型的值。
综合前面三种方式各有特点:
第一种方式最直接,但是有可能抛出异常
第二种方式比第一种稍微灵活一些,我们可以根据读取到值为空(在.net里用DBNull类来表示,可以表示数据库中任意数据类型的空值),我们就不进行相应的类型转换,避免出现异常。
第三种方式按照列的名字来读取数据,也需要按照第二种方式进行一定的转换。
就性能来说第一种最高,第二种稍低,第三种最低(这很好理解,假设要在一个旅馆里找人直通过房间号找肯定比通过名字找快)
就灵活性来说第三种最灵活,第二种次之,第一种最不灵活(假如在后来编写SQL语句中更改了列的索引,第一种和第二种都可能出现问题)。
实际开发中根据实际情况选择合适的方式。
使用 DataReader 检索数据的步骤:
1、创建 Command 对象
2、调用 ExecuteReader() 创建 DataReader 对象
3、使用 DataReader 的 Read() 方法逐行读取数据
4、读取某列的数据,(type)dataReader[ ]
5、关闭 DataReader 对象
注意:DataReader 使用后必须关闭
1 string sql = "SELECT StudentName FROM Student
2 WHERE StudentName LIKE '李%'";
3 SqlCommand command = new SqlCommand(sql, connection); 4 connection.Open(); 5 SqlDataReader dataReader = command.ExecuteReader(); 6 Console.WriteLine("查询结果:"); 7 while (dataReader.Read()) 8 { 9 Console.WriteLine((string)dataReader["StudentName"]); 10 } 11 dataReader.Close();
四、DataAdapter对象
DataAdapter对象也称之为数据适配器对象,DataAdapter对象利用数据库连接对象(Connection)连接的数据源,使用数据库命令对象(Command)规定的操作从数据源中检索出数据送往数据集对象(DataSet),或者将数据集中经过编辑后的数据送回数据源。 数据适配器将数据填入数据集时调用方法Fill(),语句如下:
1 //直接填充表
2 dataAdapter1.Fill (dataTable); 3 或者 4 //填充dataSet11数据集中的"Products"表
5 dataAdapter1.Fill (dataSet11, "Products");
当dataAdapter1调用Fill() 方法时将使用与之相关联的命令组件所指定的 SELECT 语句从数据源中检索行。然后将行中的数据添加到 DataSet 中的DataTable 对象中或者直接填充到DataTable的实例中,如果 DataTable 对象不存在,则自动创建该对象。
当执行上述SELECT语句时,与数据库的连接必须有效,但不需要用语句将连接对象打开。如果调用Fill()方法之前与数据库的连接已经关闭,则将自动打开它以检索数据,执行完毕后再自动将其关闭。如果调用Fill()方法之前连接对象已经打开,则检索后继续保持打开状态。
注意:一个数据集中可以放置多张数据表。但是每个数据适配器只能够对应于一张数据表。
五、DataSet对象
DataSet对象也称为数据集对象,DataSet对象用于表示那些储存在内存中的数据,它相当于一个内存中的数据库。它可以包括多个DataTable对象及DataView对象。DataSet主要用于管理存储在内存中的数据以及对数据的断开操作。 由于DataSet对象提供了一个离线的数据源,这样减轻了数据库以及网络的负担,在设计程序的时候可以将DataSet对象作为程序的数据源。
六、DataTable对象
DataTable 是 ADO.NET 库中的核心对象,就像普通的数据库中的表一样,它也有行和列。它主要包括DataRow和DataColumn,分别代表行和列。
(1) 数据行(DataRow)
数据行是给定数据表中的一行数据,或者说是数据表中的一条记录。它可能代表一个学生、一位用户、一张订单或者一件货物的相关数据。DataRow对象的方法提供了对表中数据的插入、删除、更新和查看等功能。提取数据表中的行的语句如下:
DataRow dr = dt.Rows[n];
其中:DataRow代表数据行类;dr是数据行对象;dt代表数据表对象; n代表行的序号(序号从0开始)。
(2) 数据列(DataColumn)
数据表中的数据列(又称字段)定义了表的数据结构,例如,可以用它确定列中的数据类型和大小,还可以对其他属性进行设置。例如,确定列中的数据是否是只读的、是否是主键、是否允许空值等;还可以让列在一个初始值的基础上自动增殖,增值的步长还可以自行定义。 某列的值需要在数据行的基础上进行。语句如下:
1 string dc = dr.Columns["字段名"].ToString(); 2 或者 3 string dc = dr.Column[i].ToString();//i表示对应的列索引
如若想取出数据表(dt)中第3条记录中的“姓名”字段,并将该字段的值放入一输入框(textBox1)中时,语句可以写成:
1 DataRow dRow = dt.Rows[2 ]; // 从数据表提取行
2 string textBox1.Text=dRow["CompanyName"].ToString(); // 从行中取出字段的值
一个基本的例子
1 //实例化Connection对象
2 SqlConnection connection = new SqlConnection("Data Source=(local);
3 Initial Catalog=数据库名;Persist Security Info=True;User ID=sa;Password=sa");
4 connection.open(); 5 //要执行查询,则先需要实例化Command对象,
6 SqlCommand command = new SqlCommand("select * from UserInfo where sex=0", connection); 7 SqlDataAdapter adapter = new SqlDataAdapter(command); 8 /*
9 下面的被注释掉的代码与上面的代码是等效的 10 SqlDataAdapter adapter = new SqlDataAdapter("select * from UserInfo where sex=0",connection); 11 */
12 DataTable data = new DataTable(); 13 adapter.Fill(data); 14 /* 下面的被注释掉语句与上面填充DataTable的效果是一样的,我更倾向于没有注释掉的部分 15 DataSet ds = new DataSet();//实例化DataSet 16 adapter.Fill(ds, "UserInfo");//填充ds中的"UserInfo"表 17 DataTable data = ds.Tables["UserInfo"]; 18 */
19 connection.close();
参考资料:https://www.cnblogs.com/aito/archive/2010/08/25/1808471.html
二、SQL Server 中varchar与nvarchar的区别
varchar(n)
长度为 n 个字节的可变长度且非 Unicode 的字符数据。n 必须是一个介于 1 和 8,000 之间的数值。存储大小为输入数据的字节的实际长度,而不是 n 个字节。
nvarchar(n)
包含 n 个字符的可变长度 Unicode 字符数据。n 的值必须介于 1 与 4,000 之间。字节的存储大小是所输入字符个数的两倍。
具体示例
1、数据
varchar和nvarchar长度都为10
varchar只能存十个字节,一个汉字占两个字节,所以存了四个汉字,两个字符,多输入字符则会报错
nvarchar能存储十个字符,英文字母也算一个字符,所以存了六个汉字,四个英文字母,多输入字符则会报错
一句话就是varchar是按字节算得(10个字节),nvarchar是按字符个数算的(十个字符,汉字英文都是十个字符)
字符计算结果:
varchar占了六个字符,nvarchar占了10个字符
三、以下程序的输出
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 IGrandFather grandFather = new Children(); 6 grandFather.F(); 7 Console.ReadKey(); 8 } 9 } 10 interface IGrandFather 11 { 12 void F(); 13 } 14 abstract class Father : IGrandFather 15 { 16 public void F() 17 { 18 Console.WriteLine("\nFather"); 19 } 20 } 21 class Children : Father 22 { 23 public void F() 24 { 25 Console.WriteLine("\nChildren:{0}"); 26 } 27 }
程序执行结果是:Father
Children.F()不是虚方法的继承,不能实现多态。
要覆盖父类的方法要用new关键字
四、Session共享
1.通过数据库mysql共享session
a.采用一台专门的mysql服务器来存储所有的session信息。
用户访问随机的web服务器时,会去这个专门的数据库服务器check一下session的情况,以达到session同步的目的。
缺点就是:依懒性太强,mysql服务器无法工作,影响整个系统;
b.将存放session的数据表与业务的数据表放在同一个库。如果mysql做了主从,需要每一个库都需要存在这个表,并且需要数据实时同步。
缺点:用数据库来同步session,会加大数据库的负担,数据库本来就是容易产生瓶颈的地方,如果把session还放到数据库里面,无疑是雪上加霜。上面的二种方法,第一点方法较好,把放session的表独立开来,减轻了真正数据库的负担 。但是session一般的查询频率较高,放在数据库中查询性能也不是很好,不推荐使用这种方式。
2.通过cookie共享session
把用户访问页面产生的session放到cookie里面,就是以cookie为中转站。
当访问服务器A时,登录成功之后将产生的session信息存放在cookie中;当访问请求分配到服务器B时,服务器B先判断服务器有没有这个session,如果没有,在去看看客户端的cookie里面有没有这个session,如果cookie里面有,就把cookie里面的sessoin同步到web服务器B,这样就可以实现session的同步了。
缺点:cookie的安全性不高,容易伪造、客户端禁止使用cookie等都可能造成无法共享session。
3.通过服务器之间的数据同步session
使用一台作为用户的登录服务器,当用户登录成功之后,会将session写到当前服务器上,我们通过脚本或者守护进程将session同步到其他服务器上,这时当用户跳转到其他服务器,session一致,也就不用再次登录。
缺陷:速度慢,同步session有延迟性,可能导致跳转服务器之后,session未同步。而且单向同步时,登录服务器宕机,整个系统都不能正常运行。当然也可以考虑双向同步的问题。这个方案都可以解决,目前zookeeper可以实现。
4.通过NFS共享Session
选择一台公共的NFS服务器(Network File Server)做共享服务器,所有的Web服务器登陆的时候把session数据写到这台服务器上,那么所有的session数据其实都是保存在这台NFS服务器上的,不论用户访问那太Web服务器,都要来这台服务器获取session数据,那么就能够实现共享session数据了。
缺点:依赖性太强,如果NFS服务器down掉了,那么大家都无法工作了,当然,可以考虑多台NFS服务器同步的形式。这个方案都可以解决,目前zookeeper可以实现,当然memcached也可以实现session共享。
5.通过memcache同步session
memcache可以做分布式,如果没有这功能,他也不能用来做session同步。他可以把web服务器中的内存组合起来,成为一个"内存池",不管是哪个服务器产生的sessoin都可以放到这个"内存池"中,其他的都可以使用。
优点:以这种方式来同步session,不会加大数据库的负担,并且安全性比用cookie大大的提高,把session放到内存里面,比从文件中读取要快很多。
缺点:memcache把内存分成很多种规格的存储块,有块就有大小,这种方式也就决定了,memcache不能完全利用内存,会产生内存碎片,如果存储块不足,还会产生内存溢出。
6.通过redis共享session
redis与memcache一样,都是将数据放在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
根据实际开发应用,一般选择使用memcache或redis方式来共享session.
五、cookie与session的区别与联系
cookie是什么?
cookie是存在于客户端(浏览器)。
cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。
cookie的内容主要包括:名字,值,过期时间,路径和域。其中路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。
session是什么?
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。
session 存放在什么地方?
存在于服务器端的。
cookie与session如何联系与通信的
用户首次与Web服务器建立连接的时候,服务器会给用户分发一个 SessionID作为标识。SessionID是一个由24个字符组成的随机字符串。用户每次提交页面,浏览器都会把这个SessionID包含在 HTTP头中提交给Web服务器,这样Web服务器就能区分当前请求页面的是哪一个客户端。这个SessionID就是保存在客户端的,属于客户端Session。其实客户端Session默认是以cookie的形式来存储的。
当然我们客户端可以禁用cookie,这时候服务器端就拿不到sessionID。
另外一个办法:使用url方式存储sessionID;但是一般都不推荐使用,因为可以伪造url。
六、SQL Server分页
1、NTILE() 的分页方法
NTILE (integer_expression) OVER ( [ <partition_by_clause> ] < order_by_clause > )
1 SET STATISTICS PROFILE ON 2 SET STATISTICS IO ON 3 SET STATISTICS TIME ON; 4
5 with #pager as
6 ( 7 select id,NTILE(100) OVER(Order By id) as pageid from lr_base_log 8 ) 9 select id from #pager where pageid=50
10
11 SET STATISTICS PROFILE OFF 12 SET STATISTICS IO OFF 13 SET STATISTICS TIME OFF
NTILE(RowCount / Pagesize)中的100是计算出来的 ,比如有20000条数据,每页要有200条数据,结果就是20000/200=100,就分了100页,pageid=50 就是找 第50页的数据
执行结果:
2、 ROW_NUMBER() 的分页方法
在 Sql Server 2000 之后的版本中,ROW_NUMBER() 这种分页方式一直都是很不错的,比起之前的游标分页,性能好了很多,因为 ROW_NUMBER() 并不会引起全表扫表,但是,语法比较复杂,并且,随着页码的增加,性能也越来越差。
语法 :
ROW_NUMBER ( ) OVER ( [ PARTITION BY value_expression , ... [ n ] ] order_by_clause
sql语句:
1 SET STATISTICS PROFILE ON 2 SET STATISTICS IO ON 3 SET STATISTICS TIME ON; 4
5 declare @pageSize int
6 declare @pageIndex int
7
8 set @pageIndex=50
9 set @pageSize=200
10
11 SELECT TOP (@pageIndex*@pageSize) *
12 FROM 13 ( 14 SELECT ROW_NUMBER() OVER (ORDER BY id) AS RowNumber,* FROM lr_base_log 15 ) as A 16 WHERE A.RowNumber > @pageSize*(@pageIndex-1) 17 SET STATISTICS PROFILE OFF 18 SET STATISTICS IO OFF 19 SET STATISTICS TIME OFF
找第一页的10条数据
3、Offset and Fetch 的分页方法
OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS } FETCH { FIRST | NEXT } { integer_constant | fetch_row_count_expression } { ROW | ROWS } ONLY
1 SET STATISTICS PROFILE ON 2 SET STATISTICS IO ON 3 SET STATISTICS TIME ON; 4
5 declare @pageSize int
6 declare @pageIndex int
7
8 set @pageIndex=50
9 set @pageSize=200
10
11 select * from lr_base_log order by id OFFSET (@pageSize * (@pageIndex-1)) ROW FETCH NEXT @pageSize rows only 12 SET STATISTICS PROFILE OFF 13 SET STATISTICS IO OFF 14 SET STATISTICS TIME OFF
综合比较:
七、SQL分组取每组前一(或几)条记录(排名)
查找每个类型F_OPERATETYPEID的前三条数据
1 select * from ( 2 select *, ROW_NUMBER() over(partition by F_OPERATETYPEID order by id desc) rowNum 3 from lr_base_log 4 ) as s where s.rowNum<=3
执行结果:
八、switch能否作用在byte、long、String
switch(表达式),表达式的内容为整数表达式或枚举常量
整数表达式即为int或integer型,因为byte、short、char都可以转换成整型
String、long不可以,所以switch能作用在byte上而不能作用在long和String上
char byte 等 可以向上转型至int 不会丢失精度
如果把long转换成int 本身程序就是错的 除非强制类型转换
九、多线程有几种实现方法?同步有几种实现方法
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
十、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法
不能,一个对象的一个synchronized方法只能由一个线程访问。
对象的synchronized方法不能进入了,但它的其他非synchronized方法还是可以访问的。
十一、启动一个线程是用run()还是start()
启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码