精华内容
下载资源
问答
  • SQL Server 2008 参数查询

    千次阅读 2011-02-09 22:30:00
    通过使用不同的参数,一个参数查询返回不同的结果.要获得一个参数查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准.有两种不同的方式来创建参数化查询.第一个方式是让查询优化器自动地参数...

    我将讨论如果一个查询可以被参数化,那么SQL Server优化器怎样尝试将其参数化,以及你可以怎样建立你自己的参数化查询.

    1.什么是参数化查询?

    一个简单理解参数化查询的方式是把它看做只是一个T-SQL查询,它接受控制这个查询返回什么的参数.通过使用不同的参数,一个参数化查询返回不同的结果.要获得一个参数化查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准.

    有两种不同的方式来创建参数化查询.第一个方式是让查询优化器自动地参数化你的查询.另一个方式是通过以一个特定方式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从而编程一个参数化查询.这篇文章的后面部分将介绍这个方法.

    参数化查询的关键是查询优化器将创建一个可以重用的缓存计划.通过自动地或编程使用参数化查询,SQL Server可以优化类似T-SQL语句的处理.这个优化消除了对使用高贵资源为这些类似T-SQL语句的每一次执行创建一个缓存计划的需求.而且通过创建一个可重用计划,SQL Server还减少了存放过程缓存中类似的执行计划所需的内存使用.

    2.现在让我们看看使得SQL Server创建参数化查询的不同方式.

    参数化查询是怎样自动创建的?

    微软编写查询优化器代码的人竭尽全力地优化SQL Server处理你的T-SQL命令的方式.我想这是查询优化器名称的由来.这些尽量减少资源和最大限度地提高查询优化器执行性能的方法之一是查看一个T-SQL语句并确定它们是否可以被参数化.要了解这是如何工作的,让我们看看下面的T-SQL语句:

    SELECT * FROM AdventureWorks.Sales.SalesOrderHeader

    WHERE SalesOrderID = 56000;

    GO

    在这里,你可以看到这个命令有两个特点.第一它简单,第二它在WHERE谓词中包含一个用于SalesOrderID值的指定值.查询优化器可以识别这个查询比较简单以及SalesOrderID有一个参数("56000").因此,查询优化器可以自动地参数化这个查询.

    如果你使用下面的SELECT语句来查看一个只包含用于上面语句的缓存计划的,干净的缓冲池,那么你会看到查询优化器将T-SQL查询重写为一个参数化T-SQL语句:

    SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], [sql].[text] AS [plan_text]

    FROM sys.dm_exec_cached_plans p

    OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

    JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

    GO

    当我在一个SQL Server 2008实例上运行这个命令时,我得到下面的输出,(注意,输出被重新格式化了,以便它更易读):

    如果你看看上面输出中的plan_text字段,你会看到它不像原来的T-SQL文本.如前所述,查询优化器将这个查询重新编写为一个参数化T-SQL语句.在这里,你可以看到它现在有一个数据类型为(int)的变量(@1),它在之前的SELECT语句中被定义的.另外在plan_text的末尾,"56000"被替换为变量@1.既然这个T-SQL语句被重写了,而且被存储为一个缓存计划,那么如果未来一个T-SQL命令和它大致相同,只有SalesOrderID字段被赋的值不同的话,它就可以被用于重用.让我们在动作中看看它.

    如果我在我的机器上运行下面的命令:

    DBCC FREEPROCCACHE

    GO

    SELECT * FROM AdventureWorks.Sales.SalesOrderHeader

    WHERE SalesOrderID = 56000;

    GO

    SELECT * FROM AdventureWorks.Sales.SalesOrderHeader

    WHERE SalesOrderID = 56001;

    GO

    SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], [sql].[text] AS [plan_text]

    FROM sys.dm_exec_cached_plans p

    OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

    JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

    GO

    我从最后的SELECT语句得到下面的输出,(注意,输出被重新格式化以便它更易读):

    在这里,我首先释放过程缓存,然后我执行两个不同、但却类似的非参数化查询来看看查询优化器是会创建两个不同的缓存计划还是创建用于这两个查询的一个缓存计划.在这里,你可以看到查询优化器事实上很聪明,它参数化第一个查询并缓存了计划.然后当第二个类似、但有一个不同的SalesOrderID值的查询发送到SQL Server,优化器可以识别已经缓存了一个计划,然后重用它来处理第二个查询.你可以这么说是因为"cnt"字段现在表明这个计划被用了两次.

    3.数据库配置选项PARAMETERIZATION可以影响T-SQL语句怎样被自动地参数化.对于这个选项有两种不同的设置,SIMPLEFORCED.PARAMETERIZATION设置被设置为SIMPLE,只有简单的T-SQL语句才会被参数化.要介绍这个,看下下面的命令:

    SELECT SUM(LineTotal) AS LineTotal

    FROM AdventureWorks.Sales.SalesOrderHeader H

    JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

    WHERE H.SalesOrderID = 56000

    这个查询类似于我前面的示例,除了在这里我添加了一个额外的JOIN标准.当数据库AdventureWorksPARAMETERIZATION选项被设置为SIMPLE,这个查询不会被自动地参数化.SIMPLE PARAMETERIZATION设置告诉查询优化器只参数化简单的查询.但是当选项PARAMETERIZATION被设置为FORCED,这个查询将被自动地参数化.

    当你设置数据库选项为使用FORCE PARAMETERIZATION,查询优化器试图参数化所有的查询,而不仅仅是简单的查询.你可能会认为这很好.但是在某些情况下,当数据库设置PARAMETERIZATIONFORCED,查询优化器将选择不是很理想的查询计划.当数据库设置PARAMETERFORCED,它改变查询中的字面常量.这可能导致当查询中涉及计算字段时索引和索引视图不被选中参与到执行计划中,从而导致一个无效的计划.FORCED PARAMETERIZATION选项可能是改进具有大量类似的、传递过来的参数稍有不同的查询的数据库性能的一个很好的解决方案.一个在线销售应用程序,它的客户对你的产品执行大量的类似搜索, 产品值不同,这可能是一个能够受益于FORCED PARAMETERIZATION的很好的应用程序类型.

    不是所有的查询从句都会被参数化.例如查询的TOPTABLESAMPLE HAVINGGROUP BYORDER BYOUTPUT...INTOFOR XML从句不会被参数化.

    4.使用sp_execute_sql来参数化你的T-SQL

    你不需要依赖于数据库的PARAMETERIZATION选项来使得查询优化器参数化一个查询.你可以参数化你自己的查询.你通过重新编写你的T-SQL语句并使用”sp_executesql”系统存储过程执行重写的语句来实现.正如已经看到的,上面包括一个"JOIN"从句的SELECT语句在数据库的PARAMETERIZATION设置为SIMPLE时没有被自动参数化.让我重新编写这个查询以便查询优化器将创建一个可重用的参数化查询执行计划.

    为了说明,让我们看两个类似的、不会被自动参数化的T-SQL语句,并创建两个不同的缓存执行计划.然后我将重新编写这两个查询使得它们都使用相同的缓存参数化执行计划.

    让我们看看这个代码:

    DBCC FREEPROCCACHE

    GO

    SELECT SUM(LineTotal) AS LineTotal

    FROM AdventureWorks.Sales.SalesOrderHeader H

    JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

    WHERE H.SalesOrderID = 56000

    GO

    SELECT SUM(LineTotal) AS LineTotal

    FROM AdventureWorks.Sales.SalesOrderHeader H

    JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

    WHERE H.SalesOrderID = 56001

    GO

    SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], [sql].[text] AS [plan_text]

    FROM sys.dm_exec_cached_plans p

    OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

    JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

    GO

    在这里,我释放了过程缓存,然后运行这两个包含一个JOIN的、不同的非简单的T-SQL语句.然后我将检查缓存计划.这是这个使用DMV SELECT语句的输出(注意,输出被重新格式化了,以便它更易读):

    正如你从这个输出看到的,这两个SELECT语句没有被查询优化器参数化.优化器创建了两个不同的缓存执行计划,每一个都只被执行了一次.我们可以通过使用sp_executesql系统存储过程来帮助优化器为这两个不同的SELECT语句创建一个参数化执行计划.

    下面是上面的代码被重新编写来使用sp_executesql 系统存储过程:

    DBCC FREEPROCCACHE

    GO

    EXEC sp_executesql N'SELECT SUM(LineTotal) AS LineTotal

    FROM AdventureWorks.Sales.SalesOrderHeader H

    JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

    WHERE H.SalesOrderID = @SalesOrderID', N'@SalesOrderID INT', @SalesOrderID = 56000

    GO

    EXEC sp_executesql N'SELECT SUM(LineTotal) AS LineTotal

    FROM AdventureWorks.Sales.SalesOrderHeader H

    JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

    WHERE H.SalesOrderID = @SalesOrderID', N'@SalesOrderID INT', @SalesOrderID = 56001

    GO

    SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], [sql].[text] AS [plan_text]

    FROM sys.dm_exec_cached_plans p

    OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

    JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

    GO

    如同你所看到的,我重新编写了这两个SELECT语句,使它们通过使用”EXEC sp_executesql”语句来执行.对这些EXEC语句中的每一个,我都传递三个不同的参数.第一个参数是基本的SELECT语句,但是我将SalesOrderID的值用一个变量(@SalesOrderID)替代.在第二个参数中,我确定了@SalesOrderID的数据类型,在这个例子中它是一个integer.然后在最后一个参数中,我传递了SalesOrderID的值.这个参数将控制我的SELECT根据SalesOrderID值所生成的结果.sp_executesql的每次执行中前两个参数都是一样的.但是第三个参数不同,因为每个都有不同的SalesOrderID.

    现在当我运行上面的代码时,我从DMV SELECT语句得到下面的输出(注意,输出被重新格式化了,以便它更易读):

    从这个输出,你可以看出,我有一个参数化缓存计划,它被执行了两次,为每个EXEC语句各执行了一次.

    使用参数化查询来节省资源和优化性能

    在语句可以被执行之前,每个T-SQL语句都需要被评估,而且需要建立一个执行计划.创建执行计划会占用宝贵的CPU资源.当执行计划被创建后,它使用内存空间将它存储在过程缓存中.降低CPU和内存使用的一个方法是利用参数化查询.尽管数据库可以被设置为对所有查询FORCE参数化,但是这不总是最好的选择.通过了解你的哪些T-SQL语句可以被参数化然后使用sp_executesql存储过程,你可以帮助SQL Server节省资源并优化你的查询的性能.

    展开全文
  • 通过我们已经学过的知识,你可以编写一个最简单的程序输出一个也许是程序世界中最有名的词语: echo "Hello World!"; ?> First PHP page // Single line C++ style comment /* printing the message */ ...
  • 我这边有一个需求要统计订单数据,需要统计订单的上传日期,统计的模块大概是 ...这样,将上月、上周、昨天、今天、本周、本月这些作为一个参数,我们来查询对应的日期范围。创建一个有输入参数和输...

    我这边有一个需求要统计订单数据,需要统计订单的上传日期,统计的模块大概是

    那么上月、上周、昨天、今天、本周、本月应该是怎样呢?

    1、数据分析

    因为今天是动态数据,我要查月份(上月、本月),应该是一个日期范围(开始日期到结束日期)

    要查周期(上周、本周),是星期一到星期天,要转化为日期范围

    这样,将上月、上周、昨天、今天、本周、本月这些作为一个参数,我们来查询对应的日期范围。创建一个有输入参数和输出参数的存储过程,通过输入参数(周期类型)获取输出参数(日期范围

    2、创建存储过程

    --存储过程语法--
    create proc sp_GetDateCycle
    (
        @DateRage varchar(20),   --输入参数
        @startDate varchar(30) ,  --输出参数
        @endDate varchar(30)   -- 输出参数
    )
    as
     begin
           if(@DateRage = ‘上月’)
            begin
                --返回日期的算法
            end
          if(@DateRage = ‘上周’)
            begin
                --返回日期的算法
            end
           --继续判断---
           -----
    end
    go   

    3、实例如下

    /***********************************************
    根据日期周期类型返回查询周期
    输入参数:@DateRage:日期周期类型
    输出参数:@startDate:查询周期的开始日期
              @endDate:查询周期的结束日期
    ***********************************************/
    /***
    ** 获取查询的周期
    ***/
    create proc [dbo].[sp_GetDateCycle](
        @DateRage varchar(20),
        @startDate varchar(30) output,
        @endDate varchar(30) output
    )
    as 
    begin 
        --声明变量--
        declare @nowDate datetime
        declare @startstr varchar(20)
        declare @endstr varchar(20) 
        declare @datestr1 varchar(30)
        declare @datestr2 varchar(30)
        --初始化变量--
        set @startstr = ' 00:00:00'
        set @endstr = ' 23:59:59'
        
        --进行日期范围判断--
        if(@DateRage='上月')
        begin
            ----计算当前日期与sql的初始日期(1900-01-01 00:00:00)的差值,返回月份
            --select DATEDIFF(MM,0,GETDATE())
            ----返回月份减去1,计算出上个月第一天日期--
            --select DATEADD(MM,DATEDIFF(MM,0,GETDATE())-1,0)
            ----获取当前月第一天日期--
            --select DATEADD(MM,DATEDIFF(MM,0,GETDATE()),0)
            ----减去3毫秒,计算出上个月的最后一天日期(sql精确到3毫秒)--
            --select DATEADD(MS,-3,DATEADD(MM,DATEDIFF(MM,0,GETDATE()),0))            
            
            --返回上月查询日期(开始到结束) 日期类型转字符类型--
            set @startDate = CONVERT(varchar(19),DATEADD(MM,DATEDIFF(MM,0,GETDATE())-1,0),120)    
            set @endDate = CONVERT(varchar(19),DATEADD(MS,-3,DATEADD(MM,DATEDIFF(MM,0,GETDATE()),0)),120)
        end
        if(@DateRage ='上周')
        begin
            -- --计算当前日期与sql的初始日期差值(返回周)--
            --select DATEDIFF(wk,0,GETDATE())
            ----返回的周期数减去1,,得到上周星期一的日期--
            --SELECT DATEADD(wk, DATEDIFF(wk,0,GETDATE())-1,0) 
            ----获取本周星期一的日期--
            --SELECT DATEADD(wk, DATEDIFF(wk,0,GETDATE()),0) 
            ----减去3毫秒,计算上周星期天的日期--
            --select DATEADD(MS,-3, DATEADD(wk, DATEDIFF(wk,0,GETDATE()),0))
            
            --返回上周查询日期(开始到结束) 日期类型转字符类型 --
            set @startDate = CONVERT(varchar(19),DATEADD(wk, DATEDIFF(wk,0,GETDATE())-1,0),120)
            set @endDate = CONVERT(varchar(19),DATEADD(MS,-3, DATEADD(wk, DATEDIFF(wk,0,GETDATE()),0)),120)
        end
        if(@DateRage='昨天')
        begin
            /***
            思考步骤如下:
            1)获取昨天日期(日期类型 yyyy-MM-dd hh:mm:ss)        
            2)转化昨天日期(字符类型 yyyy-MM-dd)
            3)返回查询日期(开始日期到结束日期)
            ***/    
            
            ----获取昨天日期--
            -- select DATEADD(DAY,-1,GETDATE())
            --将日期类型转换为字符类型(yyyy-MM-dd)
            select @datestr1 = CONVERT(varchar(10),DATEADD(DAY,-1,GETDATE()),120)    
            select @datestr2 = CONVERT(varchar(10),DATEADD(DAY,-1,GETDATE()),120)
            --返回昨天查询日期(开始到结束)--
            set @startDate = @datestr1 + @startstr
            set @endDate = @datestr2 + @endstr
        end
        if(@DateRage='今天')
        begin
            /***
            思考步骤如下:
            1)获取当前日期(日期类型 yyyy-MM-dd hh:mm:ss)
            2)转化当前日期(字符类型 yyyy-MM-dd)
            3)返回查询日期(开始日期到结束日期)
            ***/
            ---- 获取当前日期--
            --select DATEADD(DAY,0,GETDATE())
            --将日期类型转换为字符类型(yyyy-MM-dd)
            select @datestr1 = CONVERT(varchar(10),DATEADD(DAY,0,GETDATE()),120)
            select @datestr2 = CONVERT(varchar(10),DATEADD(DAY,0,GETDATE()),120)
            --返回今天查询日期(开始到结束)--
            set @startDate = @datestr1 + @startstr
            set @endDate = @datestr2  + @endstr
        end
        if(@DateRage='本周')
        begin
         --   --计算当前日期与sql的初始日期差值(返回周)--
            --select DATEDIFF(wk,0,GETDATE())
            ----获取本周的星期一的日期--
            --SELECT DATEADD(wk, DATEDIFF(wk,0,GETDATE()),0) 
            ----返回的周期数加上1,计算下周的星期一的日期--
            --SELECT DATEADD(wk, DATEDIFF(wk,0,GETDATE())+1,0) 
            ----减去3毫秒,计算本周星期天的日期--
            --select DATEADD(MS,-3,DATEADD(wk,DATEDIFF(wk,0,GETDATE())+1,0))
            
            --返回本周查询日期(开始到结束)--
            set @startDate = CONVERT(varchar(19), DATEADD(wk, DATEDIFF(wk,0,GETDATE()),0),120)
            set @endDate = CONVERT(varchar(19),DATEADD(MS,-3,DATEADD(wk,DATEDIFF(wk,0,GETDATE())+1,0)),120)
        end
        if(@DateRage='本月')
        begin
         --   --计算当前日期与sql的初始日期(1900-01-01 00:00:00)的差值,返回月份
            --select DATEDIFF(MM,0,GETDATE())
            ----得到当前月第一天日期--
            --select DATEADD(MM,DATEDIFF(MM,0,GETDATE()),0)
            ----返回月份加上1,计算出下个月第一天日期--
            --select DATEADD(MM,DATEDIFF(MM,0,GETDATE())+1,0)
            ----减去3毫秒,计算出当前月的最后一天日期--
            --select DATEADD(MS,-3,DATEADD(MM,DATEDIFF(MM,0,GETDATE())+1,0))
                
            --返回本月的查询日期(开始到结束----        
            --将当前月第一天日期转化(日期类型转字符类型)
            set @startDate = CONVERT(varchar(19),DATEADD(MM,DATEDIFF(MM,0,GETDATE()),0),120)
            --将当前月最后一天日期转化(日期类型转字符类型)
            set @endDate = CONVERT(varchar(19),DATEADD(MS,-3,DATEADD(MM,DATEDIFF(MM,0,GETDATE())+1,0)),120)
        end    
    end

    点击执行,生成存储过程: sp_GetDateCycle

    4、调用存储过程

    创建成功后,怎样调用辨析的存储过程呢?

    --声明输出参数--
    declare @startDate varchar(30)
    declare @endDate varchar(30)
    --获取上个月的查询日期--
    --exec sp_GetDateCycle '上月',@startDate output,@endDate output 
    --获取上周的查询日期--
    --exec sp_GetDateCycle '上周',@startDate output,@endDate output 
    --获取昨天的查询日期--
    --exec sp_GetDateCycle '昨天',@startDate output,@endDate output 
    --获取今天的查询日期--
    --exec sp_GetDateCycle '今天',@startDate output,@endDate output 
    --获取本周的查询日期--
    exec sp_GetDateCycle '本周',@startDate output,@endDate output 
    --获取本月的查询日期--
    --exec sp_GetDateCycle '本月',@startDate output,@endDate output 
    --输出查询周期---
    select @startDate
    select @endDate

    5、实际效果如下:

    1)当调用存储过程输入参数为今天,返回的日期范围

    2)当调用存储过程输入参数为上周,返回的日期范围

    3)当调用存储过程输入参数为上月,返回日期范围

    PS:

    参考网址: http://blog.csdn.net/zengcong2013/article/details/50134373

    展开全文
  • Dapper是一个用于.NET的简单的对象映射,并且在速度上有着轻ORM之...创建一个IDbConnection对象 写一个语句来执行CRUD操作 传递语句作为Execute方法的一个参数 因为这篇文章主要是为了学习其中一些方法的使用,...

    Dapper是一个用于.NET的简单的对象映射,并且在速度上有着轻ORM之王的称号。

    Dapper扩展IDbConnection,提供有用的扩展方法来查询数据库。

    那么Dapper是怎样工作的呢?

    总共三步:

    • 创建一个IDbConnection对象
    • 写一个语句来执行CRUD操作
    • 传递语句作为Execute方法的一个参数

    因为这篇文章主要是为了学习其中一些方法的使用,所以,这里不再叙述安装等的一些使用,有需要的同学可以参考:https://dapper-tutorial.net/dapper

    1.Execute

    Execute是可以被IDbConnection类型的任何对象调用的扩展方法。它可以执行一个命令一次或者很多次,并且返回受影响的行数。

    这个方法可以用于执行:

    • 存储过程(Stored Procedure)
    • 插入语句(INSERT statement)
    • 更新语句(UPDATE statement)
    • 删除语句(DELETE statement)

    下面的表格,展示了Execute方法的参数

     

    这里给出一个实现代码的示例,其余部分直接在官网上的示例上面记录学习。

    using Dapper;
    using System;
    using System.Data.SqlClient;
    using System.Runtime.Serialization;
    
    namespace Dapper_Demo
    {
    
        public class Customer
        {
            public int ID { get; set; }
    
            public string Name { get; set; }
    
            public string About { get; set; }
    
            public DateTime UpdateDate { get; set; }
            
        }
        class Program
        {
            private static readonly string connectionString = @"Data Source = 127.0.0.1;Initial Catalog = DapperDemo;User Id = sa;Password = 111111;";
            static void Main(string[] args)
            {
                Console.WriteLine("Hello World!");
    
                var customer = new Customer { ID = 2, Name = "jack", About = "jack hh", UpdateDate = DateTime.Now };
    
                var Insertsql = @"insert into Customer values(@ID,@Name,@About,@UpdateDate)";
    
                using(var connection=new System.Data.SqlClient.SqlConnection(connectionString))
                {
                    var affectedRows = connection.Execute(Insertsql,customer);
                    Console.WriteLine(affectedRows);
                }
    
                Console.ReadKey();
            }
        }
    }

    注意,在使用之前,可以nuget程序集引入Dapper和System.Data.SqlClient

    下面的部分是官方代码记录学习。

     1.1 执行存储过程

    单次(Single)

    执行一次存储过程

    string sql = "Invoice_Insert";
    
    using (var connection = My.ConnectionFactory())
    {
        var affectedRows = connection.Execute(sql,
            new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
            commandType: CommandType.StoredProcedure);
    
        My.Result.Show(affectedRows);
    }

    多次(Many)

     执行存储过程多次。数组列表中的每个对象执行一次

    string sql = "Invoice_Insert";
    
    using (var connection = My.ConnectionFactory())
    {
        var affectedRows = connection.Execute(sql,
            new[]
            {
                new {Kind = InvoiceKind.WebInvoice, Code = "Many_Insert_1"},
                new {Kind = InvoiceKind.WebInvoice, Code = "Many_Insert_2"},
                new {Kind = InvoiceKind.StoreInvoice, Code = "Many_Insert_3"}
            },
            commandType: CommandType.StoredProcedure
        );
    
        My.Result.Show(affectedRows);
    }

    1.2 执行插入

    单次(Single)

    执行一次插入语句

    string sql = "INSERT INTO Customers (CustomerName) Values (@CustomerName);";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {
        var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"});
    
        Console.WriteLine(affectedRows);
    
        var customer = connection.Query<Customer>("Select * FROM CUSTOMERS WHERE CustomerName = 'Mark'").ToList();
    
        FiddleHelper.WriteTable(customer);
    }

    多次(Many)

    执行多次插入语句。数组列表中的每个对象执行一次

    string sql = "INSERT INTO Customers (CustomerName) Values (@CustomerName);";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {
        connection.Open();
    
        var affectedRows = connection.Execute(sql,
        new[]
        {
        new {CustomerName = "John"},
        new {CustomerName = "Andy"},
        new {CustomerName = "Allan"}
        }
    );
    
    Console.WriteLine(affectedRows);

    1.3 执行更新

    单次(Single)

    执行一次更新语句

    string sql = "UPDATE Categories SET Description = @Description WHERE CategoryID = @CategoryID;";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {            
        var affectedRows = connection.Execute(sql,new {CategoryID = 1, Description = "Soft drinks, coffees, teas, beers, mixed drinks, and ales"});
    
        Console.WriteLine(affectedRows);
    }

    多次(Many)

    执行多次更新语句。数组列表中的每个对象执行一次

    string sql = "UPDATE Categories SET Description = @Description WHERE CategoryID = @CategoryID;";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {    
        var affectedRows = connection.Execute(sql,
        new[]
        {
        new {CategoryID = 1, Description = "Soft drinks, coffees, teas, beers, mixed drinks, and ales"},
        new {CategoryID = 4, Description = "Cheeses and butters etc."}
        }
    );
    
    Console.WriteLine(affectedRows);

    1.4 执行删除

    单次(Single)

    执行一次删除语句

    string sql = "DELETE FROM Customers WHERE CustomerID = @CustomerID";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {            
        var affectedRows = connection.Execute(sql, new {CustomerID = 1});
    
        Console.WriteLine(affectedRows);
    }

    多次(Many)

    执行多次删除语句。数组列表中的每个对象执行一次

    string sql = "DELETE FROM OrderDetails WHERE OrderDetailID = @OrderDetailID";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {            
        var affectedRows = connection.Execute(sql, 
            new[]
        {
        new {OrderDetailID = 1},
        new {OrderDetailID = 2},
        new {OrderDetailID = 3}
        }
    );
    
    Console.WriteLine(affectedRows);

    1.5 场景说明

    对于上面的execute方法在执行少量数据时,比较合适;但是如果执行数据量太大,速度就会很慢,就不适用了。下面会有对于大数据量的操作方法。

    下面给出使用excute在执行批量插入数据时的一些结果。

    代码如下:

     1 using Dapper;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Data.SqlClient;
     5 using System.Diagnostics;
     6 using System.Runtime.Serialization;
     7 
     8 namespace Dapper_Demo
     9 {
    10 
    11     public class Customer
    12     {
    13         public int ID { get; set; }
    14 
    15         public string Name { get; set; }
    16 
    17         public string About { get; set; }
    18 
    19         public DateTime UpdateDate { get; set; }
    20         
    21     }
    22     class Program
    23     {
    24         private static readonly string connectionString = @"Data Source = 127.0.0.1;Initial Catalog = DapperDemo;User Id = sa;Password = 111111;";
    25         static void Main(string[] args)
    26         {
    27             Console.WriteLine("Hello World!");
    28 
    29             var list = new List<Customer>();
    30             for(int i = 0; i < 100; i++)
    31             {
    32                 var customer = new Customer { ID = i, Name = "jack"+i, About = "jack hh"+i, UpdateDate = DateTime.Now };
    33                 list.Add(customer);
    34             }
    35             
    36 
    37             var Insertsql = @"insert into Customer values(@ID,@Name,@About,@UpdateDate)";
    38 
    39             var stopWatch = new Stopwatch();
    40             stopWatch.Start();
    41             using(var connection=new System.Data.SqlClient.SqlConnection(connectionString))
    42             {
    43                 var affectedRows = connection.Execute(Insertsql,list);
    44                 Console.WriteLine(affectedRows);
    45             }
    46             stopWatch.Stop();
    47             Console.WriteLine("花费的时间:" + stopWatch.ElapsedMilliseconds);
    48 
    49             Console.ReadKey();
    50         }
    51     }
    52 }
    View Code

    插入100条数据

     插入500条数据

     插入1000条数据

     可以看出当数据量逐渐增大时,execute方法就不太适用了。

    2. Query 

    这个方法使用来执行查询和映射结果的。

    它的结果可以映射到:

    • Anonymous
    • Strongly Typed
    • Multi-Mapping(One to One)
    • Multi-Mapping(One to Many)
    • Multi-Type

    可以执行的参数

    2.1 Query Anonymous

    Query方法可以执行原生 SQL 查询并且映射结果到动态集合

    string sql = "SELECT TOP 10 * FROM OrderDetails";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {    
        var orderDetail = connection.Query(sql).FirstOrDefault();
    
        FiddleHelper.WriteTable(orderDetail);
    }

    2.2 Query Strongly Typed

    Query方法可以执行原生 SQL 查询并且映射结果到强类型集合

    string sql = "SELECT TOP 10 * FROM OrderDetails";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {            
        var orderDetails = connection.Query<OrderDetail>(sql).ToList();
    
        Console.WriteLine(orderDetails.Count);
    
        FiddleHelper.WriteTable(orderDetails);
    }

    2.3 Query Multi-Mapping(One to One)

    Query方法可以执行原生 SQL 查询并且用一对一的关系映射结果到强类型集合

    string sql = "SELECT * FROM Invoice AS A INNER JOIN InvoiceDetail AS B ON A.InvoiceID = B.InvoiceID;";
    
    using (var connection = My.ConnectionFactory())
    {
        connection.Open();
    
        var invoices = connection.Query<Invoice, InvoiceDetail, Invoice>(
                sql,
                (invoice, invoiceDetail) =>
                {
                    invoice.InvoiceDetail = invoiceDetail;
                    return invoice;
                },
                splitOn: "InvoiceID")
            .Distinct()
            .ToList();
    }

    2.4 Query Multi-Mapping (One to Many)

    Query方法可以执行原生 SQL 查询并且用一对多的关系映射结果到强类型集合

    string sql = "SELECT TOP 10 * FROM Orders AS A INNER JOIN OrderDetails AS B ON A.OrderID = B.OrderID;";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {            
        var orderDictionary = new Dictionary<int, Order>();
    
    
        var list = connection.Query<Order, OrderDetail, Order>(
        sql,
        (order, orderDetail) =>
        {
            Order orderEntry;
    
            if (!orderDictionary.TryGetValue(order.OrderID, out orderEntry))
            {
            orderEntry = order;
            orderEntry.OrderDetails = new List<OrderDetail>();
            orderDictionary.Add(orderEntry.OrderID, orderEntry);
            }
    
            orderEntry.OrderDetails.Add(orderDetail);
            return orderEntry;
        },
        splitOn: "OrderID")
        .Distinct()
        .ToList();
    
        Console.WriteLine(list.Count);
    
        FiddleHelper.WriteTable(list);
        FiddleHelper.WriteTable(list.First().OrderDetails);
    }

    2.5 Query Multi-Type

    Query方法可以执行原生 SQL 查询并且映射结果到有多个类型的集合

    string sql = "SELECT * FROM Invoice;";
    
    using (var connection = My.ConnectionFactory())
    {
        connection.Open();
    
        var invoices = new List<Invoice>();
    
        using (var reader = connection.ExecuteReader(sql))
        {
            var storeInvoiceParser = reader.GetRowParser<StoreInvoice>();
            var webInvoiceParser = reader.GetRowParser<WebInvoice>();
    
            while (reader.Read())
            {
                Invoice invoice;
    
                switch ((InvoiceKind) reader.GetInt32(reader.GetOrdinal("Kind")))
                {
                    case InvoiceKind.StoreInvoice:
                        invoice = storeInvoiceParser(reader);
                        break;
                    case InvoiceKind.WebInvoice:
                        invoice = webInvoiceParser(reader);
                        break;
                    default:
                        throw new Exception(ExceptionMessage.GeneralException);
                }
    
                invoices.Add(invoice);
            }
        }
        
        My.Result.Show(invoices);
    }

     

    转载于:https://www.cnblogs.com/Vincent-yuan/p/11504004.html

    展开全文
  • 你必须知道的495C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    怎样创建无名的中间的常量结构值? 2.12 怎样从/向数据文件读/写结构? 结构填充 2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制...
  • CommonUtil.setResponseMsg("FFFFF", "查询人脸识别系统参数发生异常", moduleUtil); return "fail"; } String ip = sysParams.getString("ip"); String url =sysParams.getString("url"); //...
  • //创建一个SAXReader //使用Reader解析远程服务器RSS信息 /*Document doc = reader.read(...
  • 怎样创建无名的中间的常量结构值? 26 2.12 怎样从/向数据文件读/写结构? 27 结构填充 27 2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充...
  • 《你必须知道的495C语言问题》

    热门讨论 2010-03-20 16:41:18
    怎样创建无名的中间的常量结构值? 26 2.12 怎样从/向数据文件读/写结构? 27 结构填充 27 2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充...
  • 创建一个request对象并填充那些有可能被所引用的servlet使用的信息,如参数、头部、cookies、查询字符串、URI等等。一个request对象是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的一个...

    本系列文章来自《How Tomcat Works》,因本书中描述的tomcat版本是5以下的,所以后面章节在实操部分用了tomcat8的源码。

    一个Servlet容器怎样工作

    对于一个Servlet服务一个请求基本上包括三件事情:

    创建一个request对象并填充那些有可能被所引用的servlet使用的信息,如参数、头部、cookies、查询字符串、URI等等。一个request对象是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的一个实例。

    创建一个response对象,所引用的servlet使用它来给客户端发送响应。一个response对象javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的一个实例。

    调用servlet的service方法,并传入request和response对象。在这里servlet会从request对象取值,给response写值。

    Catalina架构图

    在这里插入图片描述

    不同模块处理不同的任务,如:manager 模块处理用户的sessions,loader模块加载servlet的类。

    16章的shutdown钩子,Tomcat使用它总能获得一个机会用于clean-up,而无论用户是怎样停止它的(即适当的发送一个shutdown命令或者不适当的简单关闭控制台)。

    Charpter 1: A simple Web Server

    在HTTP中,始终都是客户端通过建立连接和发送一个HTTP请求从而开启一个事务。web服务器不需要联系客户端或者对客户端做一个回调连接。无论是客户端或者服务器都可以提前终止连接。举例来说,当你正在使用一个web浏览器的时候,可以通过点击浏览器上的停止按钮来停止一个文件的下载进程,从而有效的关闭与web服务器的HTTP连接。

    HTTP Requests

    一个HTTP请求包括三个组成部分:

     方法—统一资源标识符(URI)—协议/版本

     请求的头部

     主体内容

    统一资源定位器(URL)其实是一种URI(查看http://www.ietf.org/rfc/rfc2396.txt)

    来的。该协议版本代表了正在使用的HTTP协议的版本。

    请求的头部包含了关于客户端环境和请求的主体内容的有用信息。例如它可能包括浏览器设置的语言,主体内容的长度等等。每个头部通过一个回车换行符(CRLF)来分隔的。

    对于HTTP请求格式来说,头部和主体内容之间有一个回车换行符(CRLF)是相当重要的。CRLF告诉HTTP服务器主体内容是在什么地方开始的。在一些互联网编程书籍中,CRLF还被认为是HTTP请求的第四部分。

    HTTP Response

    类似于HTTP请求,一个HTTP响应也包括三个组成部分:

     方法—统一资源标识符(URI)—协议/版本

     响应的头部

     主体内容

    Socket Class

    要创建一个客户端的套接字连接远程服务器,你可以使用Socket类众多构造方法中的一个。其中一个接收主机名称和端口号:

    public Socket (java.lang.String host, int port)

    在这里主机是指远程机器名称或者IP地址,端口是指远程应用的端口号。例如,要连接yahoo.com的80端口,你需要构造以下的Socket对象:

    new Socket (“yahoo.com”, 80);

    一旦你成功创建了一个Socket类的实例,你可以使用它来发送和接受字节流。要发送字节流,你首先必须调用Socket类的getOutputStream方法来获取一个java.io.OutputStream对象。要发送文本到一个远程应用,你经常要从返回的OutputStream对象中构造一个java.io.PrintWriter对象。要从连接的另一端接受字节流,你可以调用Socket类的getInputStream方法用来返回一个java.io.InputStream对象。

    ServerSocket Class

    为了让你的应用能随时待命,你需要使用java.net.ServerSocket类。这是服务器套接字的实现。

    要创建一个服务器套接字,你需要使用ServerSocket类提供的四个构造方法中的一个。你需要指定IP地址和服务器套接字将要进行监听的端口号。通常,IP地址将会是127.0.0.1,也就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的IP地址被称为是绑定地址。服务器套接字的另一个重要的属性是backlog,这是服务器套接字开始拒绝传入的请求之前,传入的连接请求的最大队列长度。 其中一个ServerSocket类的构造方法如下所示:

    public ServerSocket(int port, int backLog, InetAddress bindingAddress);

    Application

    Application由三个类组成:

     HttpServer

     Request

     Response

    这个应用程序的入口点(静态main方法)可以在HttpServer类里边找到。main方法创建了一个HttpServer的实例并调用了它的await方法。await方法,顾名思义就是在一个指定的端口上等待HTTP请求,处理它们并发送响应返回客户端。它一直等待直至接收到shutdown命令。 应用程序不能做什么,除了发送静态资源,例如放在一个特定目录的HTML文件和图像文件。它也在控制台上显示传入的HTTP请求的字节流。不过,它不给浏览器发送任何的头部例如日期或者cookies。

    Chapter2:A Simple Servlet Container

    servlet容器为servlet请求调用它的service方法。servlet容器传递一个javax.servlet.ServletRequest对象和javax.servlet.ServletResponse对象。ServletRequest对象包括客户端的HTTP请求信息,而ServletResponse对象封装servlet的响应。在servlet的生命周期中,service方法将会给调用多次.

    当从服务中移除一个servlet实例的时候,servlet容器调用destroy方法。这通常发生在servlet容器正在被关闭或者servlet容器需要一些空闲内存的时候。仅仅在所有servlet线程的service方法已经退出或者超时淘汰的时候,这个方法才被调用。在servlet容器已经调用完destroy方法之后,在同一个servlet里边将不会再调用service方法。destroy方法提供了一个机会来清理任何已经被占用的资源,例如内存,文件句柄和线程,并确保任何持久化状态和servlet的内存当前状态是同步的。

    Application 1

    当第一次调用servlet的时候,加载该servlet类并调用servlet的init方法(仅仅一次)。  对每次请求,构造一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。

     调用servlet的service方法,同时传递ServletRequest和ServletResponse对象。

     当servlet类被关闭的时候,调用servlet的destroy方法并卸载servlet类。

    Chapter3:Connector

    Tomcat为每个包都分配一个属性文件。例如,在包 org.apache.catalina.connector里边的属性文件包含了该包所有的类抛出的所有错误信息。每个属性文件都会被一个 org.apache.catalina.util.StringManager类的实例所处理。

    在servlet/JSP编程中,参数名jsessionid是用来携带一个会话标识符。会话标识符经常被作为cookie来嵌入,但是程序员可以选择把它嵌入到查询字

    符串去,例如,当浏览器的cookie被禁用的时候。

    Obtaining Parameters

    If the user requested the servlet using the GET method, all parameters are on the query string. If the POST method is used, you may find some in the request body too. All the name/value pairs are stored in a HashMap.

    Because parameters can exist in the query string and or the HTTP request body, the parseParameters method checks both the query string and the request body.

    the method tries to see if the HTTP request body contains parameters. This happens if the use sends the request using the POST method, the content length is greater than zero, and the content type is application/x-www-form-urlencoded.

    Chapter 4: Tomcat Default Connector

    Inside the invoke method, the container loads the servlet class, call its service method, manage sessions, log error messages,etc.

    HTTP1.1 New Features

    persistent Connection

    当一个页面被请求的时候,浏览器同样需要下载页面所引用到的资源。加入页面和它所引用到的全部资源使用不同连接来 下载的话,进程将会非常慢。那就是为什么HTTP1.1引入持久连接的原因了。使用持久连接的时候,当页面下载的时候,服务器并不直接关闭连接。相反,它 等待web客户端请求页面所引用的全部资源。这种情况下,页面和所引用的资源使用同一个连接来下载。

    Chunked Encoding

    在HTTP1.0中,服务器可以仅仅省略content-length 头部,并保持写入连接。当写入完成的时候,它将简单的关闭连接。在这种情况下,客户端将会保持读取状态,直到获取到-1,表示已经到达文件的尾部。 HTTP1.1使用一个特别的头部transfer-encoding来表示有多少以块形式的字节流将会被发送。For every chunk, the length(in hexadecimal) followed by CR/LF is sent prior to the data. A transaction is marked with a zero length chunk.

    Use of the 100(Continue) status

    在发送请求内容之前,HTTP 1.1客户端可以发送Expect: 100-continue头部到服务器,并等待服务器的确认。这个一般发生在当客户端需要发送一份长的请求内容而未能确保服务器愿意接受它的时候。如果你 发送一份长的请求内容仅仅发现服务器拒绝了它,那将是一种浪费来的。 当接受到Expect: 100-continue头部的时候,假如乐意或者可以处理请求的话,服务器响应100-continue头部,后边跟着两对CRLF字符。 HTTP/1.1 100 Continue 接着,服务器应该会继续读取输入流。

    Connector接口

    一个Tomcat连接器必须实现org.apache.catalina.Connector接口。这个接口中的比较重要的方法包括,getContainer,setContainer, createRequest, and createResponse。HttpConnector类就是现实了该接口。而连接器与容器之间是一对一的关系。

    HttpConnector类

    HttpConnector类实现了Connector,Runnable,Lifecycle三个接口。Lifecycle接口用于维持每个Catalina组件的生命周期并实现它。所以,当创建一个HttpConnector实例之后,应该先调用它的initialize和start方法。并且两个方法在生命周期内只能调用一次。

    创建一个服务端套接字

    HttpConnector的初始化方法initialize返回一个Socket。前面的例子在每个时刻只能处理一个HTTP请求。但是在默认的连接器,HttpConnector连机器有一池子的HttpProcessor对象,所以每个HttpProcessor实例都有它自己的线程,这样HttpConnector能同步服务多个HTTP请求。很显然,池化技术是为了解决Java中一直创建对象。每个HttpProcerssor负责解析HTTP请求行和HTTP头和添加请求对象。因此,每个实例与一个请求对象和一个返回对象联系。一个HttpProcessor类的构造函数包含调用HttpConnector类的createRequest和createResponse方法。

    服务HTTP请求

    对于每个HTTP请求,通过调用createProcessor私有方法获取一个HttpProcessor。但是,大部分时间createProcessor并不会创建一个HttpProcessor对象。而是从pool中获取一个。如果在栈上仍然有HttpProcessor,就弹出一个HttpProcessor用于处理请求。如果为空或者没有超过最大HttpProcessor实例就创建一个。如果到达了最大实例,则createProcessor返回null。如果发生这种情况,socket就简单关闭并且不处理HTTP请求。如果createProcessor没有返回null,客户端套接字传递给HttpProcessor类的assign方法。

    processor.assign(socket);
    

    现在HttpProcessor实例的任务就是读取socket中的输入流和解析HTTP请求。

    HttpProcessor类

    在这一节中,我们将学会HttpProcessor怎么工作和怎么使assign方法异步以至于HttpConnector实例能在同一时刻服务很多HTTP请求。需要注意的是, run中的while循环在await方法中结束。 await方法持有处理线程的控制流,直到从HttpConnector中获取到一个新的套接字。用另外一种说法就是,直到 HttpConnector调用 HttpProcessor 实例的 assign 方法。但是, await 方法和 assign 方 法运行在不同的线程上。 assign 方法从 HttpConnector 的 run 方法中调用。我们就说这个线程是 HttpConnector 实
    例的 run 方法运行的处理线程。 assign 方法是如何通知已经被调用的 await 方法的?就是通过
    一个布尔变量 available 并且使用 java.lang.Object 的 wait 和 notifyAll 方法。
    注意: wait 方法让当前线程等待直到另一个线程为这个对象调用 notify 或者 notifyAll 方法为止。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3cKXGm2r-1619231481739)(E:\study\tomcat\img\httpProcessor.png)]

    刚开始的时候,当处理器线程刚启动的时候, available 为 false,线程在 while 循环里边
    等待(见 Table 4.1 的第 1 列)。它将等待另一个线程调用 notify 或 notifyAll。这就是说,调
    用 wait 方法让处理器线程暂停,直到连接器线程调用 HttpProcessor 实例的 notifyAll 方法。现在,看看第 2 列,当一个新的套接字被分配的时候, 连接器线程调用 HttpProcessor 的assign 方法。 available 的值是 false,所以 while 循环给跳过,并且套接字给赋值给HttpProcessor 实例的 socket 变量:
    this.socket = socket;
    连接器线程把 available 设置为 true 并调用 notifyAll。这就唤醒了处理器线程,因为available 为 true,所以程序控制跳出 while 循环:把实例的 socket 赋值给一个本地变量,并把 available 设置为 false,调用 notifyAll,返回最后需要进行处理的 socket。为什么 await 需要使用一个本地变量(socket)而不是返回实例的 socket 变量呢?因为这样一来,在当前 socket 被完全处理之前,实例的 socket 变量可以赋给下一个前来的 socket。为什么 await 方法需要调用 notifyAll 呢? 这是为了防止在 available 为 true 的时候另一
    个 socket 到来。在这种情况下,连接器线程将会在 assign 方法的 while 循环中停止,直到接收到处理器线程的 notifyAll 调用。

    请求对象

    默认连接器的HTTP请求对象由org.apache.catalina.Request接口表示。这个接口直接由RequestBase类实现,而RequestBase是HttpRequest的父类。最终的实现是HttpRequestImpl类,它继承了HttpRequest类。

    返回对象

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4jGwqhzb-1619231481740)(E:\study\tomcat\img\responseObject.png)]

    处理请求

    在这节中我们关注 HttpProcessor 类的 process 方法,它是一个套接字赋给它之后,在 HttpProcessor 类的 run 方法中调用的。 process 方法会做下面这些工作。

    1. 解析连接。
    2. 解析请求。
    3. 解析头部。

    process 方法使用布尔变量 ok 来指代在处理过程中是否发现错误,并使用布尔变量
    finishResponse 来指代 Response 接口中的 finishResponse 方法是否应该被调用。
    boolean ok = true;
    boolean finishResponse = true;
    另外, process 方法也使用了布尔变量 keepAlive,stopped 和 http11。 keepAlive 表示连接
    是否是持久的, stopped 表示 HttpProcessor 实例是否已经被连接器终止来确认 process 是否也
    应该停止, http11 表示 从 web 客户端过来的 HTTP 请求是否支持 HTTP 1.1。

    有一个 SocketInputStream 实例用来包装套接字的输入流。 有个 while 循环用来保持从输入流中读取,直到 HttpProcessor 被停止,一个异常被抛出或者连接给关闭为止。

    keepAlive = true;
    while (!stopped && ok && keepAlive) {
         ...
    }
    

    在 while 循环的内部, process 方法首先把 finishResponse 设置为 true,并获得输出流,并对请求和响应对象做些初始化处理。

    finishResponse = true;
    try {
    	request.setStream(input);
    	request.setResponse(response);
    	output = socket.getOutputStream();
    	response.setStream(output);
    	response.setRequest(request);
    	((HttpServletResponse) response.getResponse()).setHeader
    		("Server", SERVER_INFO);
    } catch (Exception e) {
    	log("process.create", e);
    	ok = false;
    }
    

    随后,process方法通过调用parseConnection, parseRequest和parseHeaders方法解析HTTP请求。 这些方法将在这节的小节中讨论。

    if (ok) {
    	parseConnection(socket);
    	parseRequest(input, output);
    	if (!request.getRequest().getProtocol()
    		.startsWith("HTTP/0"))
    		parseHeaders(input);
    	if (http11) {
    		// Sending a request acknowledge back to the client if
    		// requested.
    		ackRequest(output);
    		// If the protocol is HTTP/1.1, chunking is allowed.
    		if (connector.isChunkingAllowed())
    			response.setAllowChunking(true);
    	}
    
    }
    

    parseConnection 方法获得协议的值,像 HTTP0.9, HTTP1.0 或 HTTP1.1。如果协议是 HTTP1.0,
    keepAlive 设置为 false,因为 HTTP1.0 不支持持久连接。如果在 HTTP 请求里边找到 Expect:
    100-continue 的头部信息,则 parseHeaders 方法将把 sendAck 设置为 true。
    如果协议是 HTTP1.1,并且 web 客户端发送头部 Expect: 100-continue 的话,通过调用ackRequest 方法它将响应这个头部。它将会测试组块是否是允许的 。

    ackRequest 方法测试 sendAck 的值,并在 sendAck 为 true 的时候发送下面的字符串:
    HTTP/1.1 100 Continue\r\n\r\n
    在解析 HTTP 请求的过程中,有可能会抛出异常。任何异常将会把 ok 或者 finishResponse
    设置为 false。在解析过后, process 方法把请求和响应对象传递给容器的 invoke 方法:

    // Ask our Container to process this request
    try {
    	((HttpServletResponse) response).setHeader
    		("Date", FastHttpDateFormat.getCurrentDate());
    	if (ok) {
    		connector.getContainer().invoke(request, response);
    	}
    } catch (ServletException e) {...}
    

    接着,如果finishResponse仍然是true,响应对象的finishResponse方法和请求对象的finishRequest方法将被调用,并且结束输出。

    // Finish up the handling of the request
    if (finishResponse) {
    	try {
    		response.finishResponse();
             ...
            request.finishRequest();
             ...
            output.flush();
    	} catch (IOException e) {...}
    

    while 循环的最后一部分检查响应的 Connection 头部是否已经在 servlet 内部设为 close,
    或者协议是 HTTP1.0.如果是这种情况的话, keepAlive 设置为 false。同样,请求和响应对象接
    着会被回收利用。

    if ( "close".equals(response.getHeader("Connection")) ) {
    	keepAlive = false;
    }
    // End of request processing
    status = Constants.PROCESSOR_IDLE;
    // Recycling the request and the response objects
    request.recycle();
    response.recycle();
    

    解析连接

    parseConnection方法从套接字中获取到网络地址并把它赋予HttpRequestImpl对象。它也检查是否使用代理并把套接字赋予请求对象。

    解析请求

    parseRequest 方法是第 3 章中类似方法的完整版本。

    解析头部

    parseHeaders方法包含一个while循环,可以持续读取HTTP请求直到再也没有更多的头部可以读取到。 while循环首先调用请求对象的allocateHeader方法来获取一个空的 HttpHead 实例。这个实例被传递给SocketInputStream 的 readHeader方法。

    while (true) {
        HttpHeader header = request.allocateHeader();
        // Read the next header
        input.readHeader(header);
    }
    

    假如所有的头部都被已经被读取的话, readHeader方法将不会赋值给HttpHeader实例,这个时候 parseHeaders 方法将会返回。

    if (header.nameEnd == 0) {
    	if (header.valueEnd == 0) {
    		return;
    	} else {
    		throw new ServletException
    			(sm.getString("httpProcessor.parseHeaders.colon"));
    	}
    }
    

    如果存在一个头部的名称的话,这里必须同样会有一个头部的值:
    String value = new String(header.value, 0, header.valueEnd);
    接下去,像第3章那样, parseHeaders方法将会把头部名称和DefaultHeaders里边的名称做对比。注意的是,这样的对比是基于两个字符数组之间,而不是两个字符串之间的。

    if (header.equals(DefaultHeaders.AUTHORIZATION_NAME)) {
         request.setAuthorization(value);
    }  
    ...
    

    Headers.colon"));
    }
    }

    
    如果存在一个头部的名称的话,这里必须同样会有一个头部的值:
    String value = new String(header.value, 0, header.valueEnd);
    接下去,像第3章那样, parseHeaders方法将会把头部名称和DefaultHeaders里边的名称做对比。注意的是,这样的对比是基于两个字符数组之间,而不是两个字符串之间的。
    
    ```java
    if (header.equals(DefaultHeaders.AUTHORIZATION_NAME)) {
         request.setAuthorization(value);
    }  
    ...
    
    展开全文
  • 怎样创建无名的中间的常量结构值?  2.12 怎样从/向数据文件读/写结构? 结构填充  2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者...
  • 怎样创建无名的中间的常量结构值? 2.12 怎样从/向数据文件读/写结构? 结构填充 2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制...
  • 怎样创建无名的中间的常量结构值? 55 2.12 怎样从/向数据文件读/写结构? 56 结构填充 56 2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,...
  • 怎样创建无名的中间的常量结构值? 55 2.12 怎样从/向数据文件读/写结构? 56 结构填充 56 2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,...
  • 你必须知道的495C语言问题(PDF)

    热门讨论 2009-09-15 10:25:47
    4.9 我怎样一个int 变量转换为char * 型?我试了类型转换, 但是不 行。. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 5 空(null) 指针21 5.1 臭名昭著的空指针到底是什么? . . . . . ...
  • sql经典语句部分

    2011-07-31 11:16:50
    3),例如:在一个外部表中导入数据,由于某些原因第一次只导入了一部分,但很难判断具体位置,这样只有在下一次全部导入,这样也就产生好多重复的字段,怎样删除重复字段 alter table tablename --添加一个自增列 add...
  • 3.1.4 创建一个新的crontab文件 24 3.1.5 列出crontab文件 24 3.1.6 编辑crontab文件 24 3.1.7 删除crontab文件 25 3.1.8 恢复丢失的crontab文件 25 3.2 at命令 25 3.2.1 使用at命令提交命令或脚本 26 3.2.2 列出所...
  • 3.1.4 创建一个新的crontab文件 24 3.1.5 列出crontab文件 24 3.1.6 编辑crontab文件 24 3.1.7 删除crontab文件 25 3.1.8 恢复丢失的crontab文件 25 3.2 at命令 25 3.2.1 使用at命令提交命令或脚本 26 3.2.2 列出所...
  • 第一种是通过rawQuery()这个方法,接收一个sql语句,获取查询的结果内容,第二种方式是通过API中提供的Query()方法,根据指定参数来获取查询结果,下面分别来看一下这两种方式具体是怎样去实现的。 首先来看通过sql...
  • Numpy基础知识小记

    2021-01-28 20:03:06
    Numpy基础知识小记Numpy的array以及创建方法和操作函数array对象的背景array本身的属性创建array的方法创建array的便捷函数array本身支持大量操作和函数Numpy怎样对数组使用索引查询基础索引维数组二维数组神奇...
  • 创建一个字回绕的CEditView 17 程序保持极小状态 18 移动窗口 18 重置窗口的大小 18 单击窗口标题栏以外区域使窗口移动 18 改变视窗的背景颜色 19 防止主框窗口在其说明中显示活动的文档名 20 获取有关窗口正在处理...
  •  用宏实现数据表,数据字段的动态查询的报表实例屡见不鲜,用宏也可以实现数据集的动态设定,我们经常...2. 定义一个参数,如下图所示: 3. 定义一个宏,如下图所示: 4. 在报表中,写入如下数据: 当我们
  • o 5.9 我怎样一个 int 变量转换为 char * 型?我试了类型转换, 但是不行。 * 6. 空 (null) 指针 o 6.1 臭名昭著的空指针到底是什么? o 6.2 怎样在程序里获得一个空指针? o 6.3 用缩写的指针比较 ``if(p)...
  • 创建一个类模板特定类型的实例怎样定义类模板的成员成员函数静态成员和嵌套类型 以及怎样用类模板来组织我们的程序最后以一个扩展的类模板的例子作为结束 面向对象的程序设计和C++的支持机制是第17 18 19 和20 章第...
  • MySQL中文参考手册

    2010-08-10 22:11:55
    8.4.2 创建一个数据库表 8.4.3 将数据装入数据库表 8.4.4 从一个数据库表检索信息 8.4.4.1 选择所有数据 8.4.4.2 选择特定行 8.4.4.3 选择特定列 8.4.4.4 排序行 8.4.4.5 日期计算 8.4.4.6 NULL值操作 8.4....

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 207
精华内容 82
关键字:

怎样创建一个参数查询