Sql Server来龙去脉系列 必须知道的权限控制核心篇
最近写了《Sql Server来龙去脉系列 必须知道的权限控制基础篇》,感觉反响比较大。这可能也说明了很多程序猿对数据库权限控制方面比较感兴趣,或者某些技术点了解的没有很透彻。 有些人看了上篇感觉意犹未尽,介绍的都是基础方面,不够深入。那么本篇内容就比较符合大家的胃口,本篇包括了数据库常用的权限控制,例如服务角色以及数据库角色管理。
提几个问题
在介绍权限控制之前先提下面几个问题,如果有回答不上来的问题,本篇内容你应该看。如果很清晰的回答出这些问题,那么本篇接下来的内容你可以直接忽略。
1.登陆账号和用户有什么区别;
2.服务角色和数据库角色有什么区别;
3.主体、用户、架构之间的区别(Principal、User、Schema);
温故而知新
在《Sql Server来龙去脉系列 必须知道的权限控制基础篇》中,我们介绍了登陆账号的增删改查、数据库的增删该查以及这些元数据对应的系统视图和操作语法。但是,一个登陆账号被创建后并不能创建或者操作数据库,必须经过授予某些权限后才能操作管理数据库。如何给登陆账号授予正确的权限,就是本篇的主要内容。
让人混淆的几个概念
1.主体
主体是可以请求SQL SERVER资源的实体。主体按照作用范围可分为三类主体:Windows级别主体、服务器级别主体、数据库级别主体。master数据库中有两张视图sys.server_principals、sys.server_principals用于存储主体数据。先来看看sys.server_principals视图,执行以下语句:
select principal_id, name, type, type_desc from sys.server_principals ,视图说明:https://msdn.microsoft.com/zh-cn/library/ms188786(v=sql.120).aspx
执行结果如下:
查询结果包含了Windows登录账号(WINDOWS_LOGINS)、SqlServer登录账号(SQL_LOGIN)、服务角色(SERVER_ROLE)。Windows级别主体就是我们的Windows登录账号,服务器级别主体就是Sql Server登录账号和服务角色。接下来我们再执行以下语句:
select principal_id, name, type, type_desc from sys.database_principals,视图说明:https://msdn.microsoft.com/zh-cn/library/ms187328(v=sql.120).aspx
执行结果如下:
结果中包含Sql Server用户(SQL_USER)、Windows用户(WINDOWS_USER)、数据库角色(DATABASE_ROLE),这三种数据都属于数据库级别主体。通过下图我们可以很容易的看出主体具体包括哪些数据。
需要注意的是,数据库级别主体中包含有Sql Server用户和Windows用户。那么什么是用户?用户就是我们接下来的第二个概念。
2.数据库用户
数据库用户是数据库级别的主体,被用于访问数据库层面的对象。一个独立的登录账号默认是不能够链接数据库的,必须和数据库用户建立对应关系才能根据用户的权限访问数据库。但是,一个数据库用户也是不能独立存在的,它也必须关联一个登录账号。前面说过,用户是访问数据库层面的对象。那么,数据库、登录账号、用户之间有什么关系?我们先看SQL Server Management Studio的一个操作:查看登录账号test2的账号信息,选择User Mapping页签。如下图所示:
分析上图,如果想给一个登录账号设置数据库权限,我们不能直接把登录账号和数据库做关联,数据库只能和数据库用户做关联,然后数据库用户和登录账号关联。登录账号和用户的存储是有区别的,登陆账号存储在系统数据库master中,而用户是存储在数据库中的,并且都是存储在系统视图sys.sysusers中。例如,我们查询上图的用户test。在系统数据库master和用户数据库TestDb中查询sys.sysusers(视图说明:https://msdn.microsoft.com/zh-cn/library/ms179871(v=sql.120).aspx)视图,对比如下:
通过上图我们明显看出,创建的用户test存储在testDb数据库中。并且对于同一个数据库,一个用户只能对应一个登陆账号。不同的数据库可以有相同名称的用户名。数据库、用户以及登录账号的关系可通过下面的关系图表示:
上图中,数据库DbAdmin中的存储有登录账号1对1关系的用户,而数据库TestDb中也单独存储有登录账号1对1关系的用户。但登录账号数据是唯一的并且保存在系统数据库master中。之前在操作SQL Server Management Studio中登录账号属性User Mapping页签数据时,我们能看到每个数据库其实还对应了一个"Default Schema",Schema也叫做架构。架构就是本篇需要说明的第三个概念。
3.数据库架构
架构(Schema)相当于存储数据库对象的一个容器,它是SQL Server 2005之后版本引入的。你可以理解架构为一个命名空间。在SQL Server 2000中其实也有架构的概念,不同的是SQL Server 2000的架构和用户是绑定的。例如 ,我新建一个用户heavi,Sql Server自动分配了一个叫做heavi的架构,并且用户heavi和架构heavi之间的关系不能更改。如果新建一张表Teacher,则为heavi.Teacher。但heavi离职时,Teacher这站表就很难维护。
但Sql Server 2005之后,用户和机构是分离的。我可以把Teacher表的架构heavi分配给其他用户,方便管理。访问一张表一般有三种方式:
select * from Teacher;
select * from heavi.Teacher;
select * from TestDb.heavi.Teacher;
第一种方式只能是当前登录账号对应的用户默认Schema正好是heavi的情况才能正常使用。例如用户heavi对应的登录账号为sa,那么只有sa账号才能直接使用select * from Teacher语句。其他账号必须加上架构名称。
数据库包含的对象包括系统表、用户表、视图、存储过程等。这些对象都被“包含”在架构下。例如下图,数据库表、视图、存储过程都挂在架构为dbo下。
服务器角色
查询角色
之前介绍了什么是主体,我们知道主体包含服务器级别主体。而服务器级别主体又包含有服务器角色。服务器角色的存在,方便操作员对服务器级别的管理。例如给登录账号授予sysadmin服务器角色后,该登录账号拥有Sql Server服务器的任何操作权限。Sql Server包括哪些服务器角色?从前面介绍的系统视图sys.server_principals可以查询所有的服务器角色。执行查询语句:
select sp.name as Name, sp.principal_id as Id, sp.sid as Sid, sp.type as Type, sp.type_desc as Type_Desc from sys.server_principals sp where sp.type = 'R'
查询结果如下:
查询的结果集就是服务器上包含的所有服务器角色。具体每个服务器角色包含哪些权限可查看说明:https://msdn.microsoft.com/zh-cn/library/ms188659(v=sql.120).aspx。
查询授权角色
在我的权限管理系统中,分别查询出了所有的登录账号和服务器角色,如下图所示:
当我选择登录列表中的某个登录账号时,系统会从数据库中查询当前选中账号授予了哪些服务器角色。由于服务器角色是固定不变的,微软直接把账号的服务器角色授权数据保存在系统视图sys.syslogins中。在权限管理服务器执行的查询语句如下:
select
sid as Sid, name as Name, dbname as DbName,password as Password, language as Laguage, createdate as CreateDate,
sysadmin as IsSysAdmin, securityadmin as IsSecurityAdmin, serveradmin as IsServerAdmin,
setupadmin as IsSetupAdmin, processadmin as IsProcessAdmin, diskadmin as IsDiskAdmin,
dbcreator as IsDbCreator, bulkadmin as IsBulkAdmin, as IsPublic
from sys.syslogins where name = 'sa'
查询结果如下:
结果中,1表示已授权、0表示未授权 。所以账号sa授予了sysadmin和public服务器角色,其中public是默认角色,新创建的登录账号默认拥有public角色。
修改授权角色
每个登录账号授予的服务器角色我们能够查询出来,有些时候我们也需要修改账号的服务角色。例如新创建的dbAdmin需要管理用户的数据库权限,则必须给 dbAdmin授予securityadmin服务角色。像修改登陆账号或者数据库时我们都是使用DDL语句操作,修改服务角色也不例外。我们先看下修改服务角色的参考语句:
ALTER SERVER ROLE server_role_name
{
[ ADD MEMBER server_principal ]
| [ DROP MEMBER server_principal ]
| [ WITH NAME = new_server_role_name ]
} [ ; ]--说明:https://msdn.microsoft.com/zh-cn/library/ee677634(v=sql.120).aspx
server_role_name表示角色名称,server_principal可以是登录账号或者用户定义的服务器角色。接下来再看看权限管理系统中修改授权角色的代码:
public void Update(string name, IList<string> roles) {
string sql = string.Empty;
foreach(var role in roles)
{
if (string.IsNullOrEmpty(role))
{
throw new Exception("更新服务角色失败,参数有误。");
}
string[] arr = role.Split(':');
bool value;
if(arr.Length != || string.IsNullOrEmpty(arr[]) || !bool.TryParse(arr[], out value))
{
throw new Exception("更新服务角色失败,参数不匹配");
}
if(arr[] == "public")
{
continue;
} string opt = value ? "ADD" : "DROP";
sql += string.Format(@"ALTER SERVER ROLE [{0}] {1} MEMBER [{2}] ;", arr[], opt, name);
}
DbHelper.Instance().ExecuteNonQuery(sql);
}
代码Update方法的参数name表示登录账号,roles表示服务器角色和操作项数据,例如其中一个role为:sysadmin:ADD。每个角色都会遍历执行一次。如删除sa的sysadmin角色,执行语句为:ALTER SERVER ROLE [sysadmin] DROP MEMBER [sa]。需要注意的是,public是默认授予的服务角色,不能修改。
数据库角色
查询角色
和服务器角色相似,主体也包含了数据库级别主体,而数据库级别主体又包含了数据库角色。我们可以从系统视图sys.database_principals查询。执行查询语句:
select sid as Sid, name as Name, principal_id as Id, type as Type, type_desc as Type_Desc from sys.database_principals where type = 'R'
查询结果如下:
结果集中包含了所有的数据库角色,具体每个数据库角色包含哪些权限,可查看说明:https://msdn.microsoft.com/zh-cn/library/ms189121(v=sql.120).aspx。
查询架构
架构的存储和数据用户存储相似,都是存储在数据库中。我们可以通过系统视图sys.schemas查询数据库中存在的架构数据。执行下面的语句:
USE [数据库];
select sc.name as SchemaName,sc.schema_id as Id, ps.type as Type,ps.type_desc as Type_Desc, ps.principal_id as PrincipalId, ps.name as PriincipalName
from sys.schemas as sc
inner join sys.database_principals ps on sc.principal_id = ps.principal_id
where ps.type = 'S'
在执行查询之前必须要做数据库切换,切换到当前需要查询的数据库。执行结果如下:
Schema是和用户关联的,因为每个用户都有一个默认的Schema。而用户的主体数据存储在database_principals中。系统视图sys.schemas的详细描述请查看:https://msdn.microsoft.com/zh-cn/library/ms176011(v=sql.120).aspx。
查询数据库用户和默认架构
首先看下权限管理系统的数据库权限管理界面,如下图所示:
整个界面包含了五个部分,分别是数据库列表、登录账号、数据库用户、默认架构、数据库角色。通过之前介绍的知识我们已经知道数据库和登录账号怎样查询了。查询数据库授权角色的步骤是先选择某个数据库,然后从登录账号下拉列表中选中某个登录账号。选择登录账号后,权限管理系统会根据选择的数据库和登录账号到数据库查询对应的数据库用户、默认架构以及授予了哪些数据库角色。
首先分析怎样查询数据库用户和默认架构,这些数据关联了sys.sql_logins、sys.sysusers、sys.database_principals、sys.schemas等系统视图。它们之间的关系如下图所示:
查询当前数据库中登录账号、数据库用户、架构数据的sql代码如下:
string sql = string.Format("USE {0};select lo.sid as Sid, lo.name as LoginName, su.uid as UserId, su.name as UserName, sc.schema_id as SchemaId, pr.default_schema_name as SchemaName from sys.sql_logins lo " +
"inner join sys.sysusers su on lo.sid = su.sid " +
"inner join sys.database_principals pr on lo.sid = pr.sid " +
"left join sys.schemas sc on pr.default_schema_name = sc.name " +
"where lo.name = '{1}';", dbName, loginName);
例如,我当前查询数据库testDb下登录账号为test2的用户和默认shcema数据,结果如下:
查询结果就包含了当前登录账号test2的数据库用户test以及用户test默认的schema为t。
查询授权角色
在同一个数据库中,不同的用户有不同的数据库角色。数据库用户和数据库角色同属于数据库级别主体,都存储在系统视图sys.database_principals中。数据库用户类型为S,数据库角色类型为R。如果用户授予了某些角色,用户和角色必定有关联关系,这些关联关系存储在系统视图sys.database_role_members。执行以下SQL语句:
select * from sys.database_role_members,视图说明:https://msdn.microsoft.com/zh-cn/library/ms189780(v=sql.120).aspx
查询结果如下图所示:
sys.database_role_members视图包含两个字段,role_principal_id表示角色主体ID,member_pricinpal_id表示用户主体ID。如果需要查询指定数据库下某个用户授予的数据库角色,可通过权限管理系统执行的SQL代码查询:
USE testDb;
select dbp1.principal_id as Id, dbp1.name as Name, dbp1.type as Type
from sys.database_role_members rm
inner join sys.database_principals dbp1 on rm.role_principal_id = dbp1.principal_id
inner join sys.database_principals dbp2 on rm.member_principal_id = dbp2.principal_id
where dbp2.name = 'test'
在查询前必须切换到需要查询的数据库,因为数据库角色、用户数据都存储在数据库中。例如,我们需要查询testDb(dbName = 'testDb')数据库中用户为test(userName = 'test')的数据库角色。查询结果如下:
查询结果表明test用户授予了db_owner和db_datareader两个数据库角色。而我们的权限管理系统查询结果界面为:
上面表示的整个流程为数据库testDb下,登录账号test2对应的用户为test,而用户test默认的架构为t。并且用户test拥有db_owner和db_datareader数据库角色。public是每个用户默认拥有的角色。那么查询的数据到底是否正确,我们可以通过SQL Server Management Studio工具中的login Properties校验。打开登录账号test2的login Properties窗口,结果如下图所示:
通过对比可以看出,testDb下登录账号test2的用户、默认架构以及数据库角色和我们权限管理系统查询结果完全一致。
修改权限角色
1.登录账号对应的用户没有改变,只改变了默认Schema或者角色
我们可以查询数据库下某个账号对应的用户和默认架构,那么我们也可以修改对应的用户和架构。我们应该记得,像操作系统级别元数据,我们一般都使用数据定义语言(DDL)。修改用户的 DDL语言定义如下:
-- SQL Server Syntax
ALTER USER userName
WITH <set_item> [ ,...n ]
[;]
<set_item> ::=
NAME = newUserName
| DEFAULT_SCHEMA = { schemaName | NULL }
| LOGIN = loginName
| PASSWORD = 'password' [ OLD_PASSWORD = 'oldpassword' ]
| DEFAULT_LANGUAGE = { NONE | <lcid> | <language name> | <language alias> }--说明:https://msdn.microsoft.com/zh-cn/library/ms176060(v=sql.120).aspx
用户不变情况下我们只用修改默认架构即可,SQL语句比较简单,如下所示:
ALTER USER {} WITH DEFAULT_SCHEMA = {} --{}表示用户,{}表示默认架构名称
默认架构修改完了 ,我们还得修改用户对应的数据库角色。修改用户的数据库角色和修改登录账号的服务器角色相似,都是遍历所有的数据库角色,按照每个角色的勾选情况删除或者增加每个角色的用户成员。数据库角色增加或删除成员的方式和服务器角色不同,是通过系统存储过程操作,增加成员的存储过程为sp_addrolemember(说明:https://msdn.microsoft.com/zh-cn/library/ms187750(v=sql.120).aspx),删除成员的存储过程为sp_droprolemember(说明:https://msdn.microsoft.com/zh-cn/library/ms188369(v=sql.120).aspx)。权限管理系统中操作数据库角色成员的代码如下:
private string CreateExecuteDbRoleSql(DatabaseUserMapping mapping) {
string setDbRoleSql = string.Empty;
if (mapping.RoleList != null && mapping.RoleList.Count > )
{
setDbRoleSql = string.Format("USE {0};", mapping.DbName);
foreach (var role in mapping.RoleList)
{
if(role.Name == "public")
{
continue;
}
var produreName = role.IsAuthorized ? "sp_addrolemember" : "sp_droprolemember";
setDbRoleSql += string.Format("EXEC {2} @rolename = '{0}',@membername = '{1}';", role.Name, mapping.UserName, produreName);
}
}
return setDbRoleSql;
}
第一步操作还是使用USE {0}切换到当前数据库,然后遍历角色集合,如果当前用户授予了角色(role.IsAuthorized为true),则执行sp_addrolemember。否则执行sp_droprolemember。
2.登录账号对应的用户已改变
之前的内容我们有讲到同一个数据库下,登录账号和用户是一一对应关系。也就是说同一个用户不能关联两个用户。在这样的一个背景下,现在我们修改了登录账号对应的用户,那么之前的用户就被丢弃了。例如,登录账号Tom,对应用户为tom。现在我把Tom对应的用户修改为joly,之前的用户tom就被丢弃了。所以,如果登录账号对应的用户改变后,我们需要做一系列的操作。具体步骤如下:
步骤1,删除之前用户对应的数据库角色
首先把之前用户授予的数据库角色查询出来(在"查询授权角色"已经讲过怎样查询授权的数据库角色)。然后逐个遍历角色,执行删除语句:
EXEC sp_droprolemember @rolename = '{0}' , @membername = '{1}' --{}表示角色名称,{}表示用户名称
步骤2,删除之前的用户
删除用户使用DDL的DROP操作,执行语句如下:
USE {};DROP USER {},--{}表示数据,{}表示用户名,DROP说明:https://msdn.microsoft.com/zh-cn/library/ms189438(v=sql.120).aspx
步骤3,创建新用户
创建用户使用DDL的CREATE USER操作,执行语句如下:
CREATE USER {} FOR LOGIN {} WITH DEFAULT_SCHEMA = {},CREATE说明:https://msdn.microsoft.com/zh-cn/library/ms173463(v=sql.120).aspx
操作语句其中的{0}表示用户名称,{1}表示登录账号,{2}表示默认架构名称。
步骤4,给新用户授予数据库角色
给新用户授予数据库角色方式和前面一样,直接遍历角色,然后调用存储过程sp_addrolemember为数据库角色增加用户成员。
重点
前面介绍的内容更偏重于怎样授权,接下来举个实际的例子来说明怎样使用这些授权。首先使用我们的权限管理系统创建数据库OperationDb,然后创建两个登录账号opt1和opt2。刚创建的登录账号opt1和opt2是没有任何权限的,所以需要给这两个账号授权。
登录账号opt1授权如下:
登录账号opt2授权如下:
两个账号都只授予了db_accessadmin数据库角色。那么现在我用opt1创建一张表OptTable1会是怎样的结果?执行结果如下:
执行结果说明opt1账号没有CREATE TABLE的权限,现在我们再为账号opt1和opt2都授予DDL操作权限db_ddladmin角色。然后我们再通过opt1创建OptTable1表,通过opt2创建OptTable2表,都能创建成功。我们可通过数据库管理工具查看到这两张表,如下图所示:
分析上图,OptTable1表前面标注了opt1,而OptTable2表前面标注了opt2。opt1表示的是登录账号opt1对应的用户opt1的默认架构opt1,opt2表示登录账号opt2对应的用户opt2的默认架构opt2。下面的图片表示的比较直观:
表创建好了后,分别使用登录账号opt1和opt2查询opt1.OptTable1、opt2.OptTable2表。opt1和opt2查询结果对比如下:
通过结果可看出在ddladmin角色权限下,账号opt1没有权限查看Schema为opt2下面的表数据,而opt2没有权限查看Schema为opt1下面的表数据。之前说过架构相当于一个容器,而架构opt1和架构opt2相当于同级的两个容器。整个层次关系如下图所示:
登录账号Opt1只能操作Schema为opt1下面的对象,而登录账号opt2只能操作Schema为opt2下面的对象。如果要想让opt1同时拥有操作Schema为opt1和opt2下面的表,我们需要把权限提高到数据库层面上。而数据库角色db_datareader,可以访问数据库下面的所有Schema。所以分别为账号opt1和opt2授予db_datareader后,现在可以访问OperationDb下面的所有表数据了。
总结
通过本篇的内容介绍,在回顾篇头提出的几个问题,现在应该能够回答个一二。本篇先介绍了几个常用到的概念,包括主体、数据库用户、数据库架构。然后分别介绍了服务器角色和数据库角色授权的查询、修改、删除功能。在”数据库角色“中也介绍了用户的增删改查以及登录账号、用户和Schema的关系。
如果本篇内容对大家有帮助,请点击页面右下角的关注。如果觉得不好,也欢迎拍砖。你们的评价就是博主的动力!下篇内容,敬请期待!
Sql Server来龙去脉系列 必须知道的权限控制核心篇的更多相关文章
- Sql Server来龙去脉系列 必须知道的权限控制基础篇
题外话:最近看到各种吐槽.NET怎么落寞..NET怎么不行了..NET工资低的帖子.我也吐槽一句:一个程序猿的自身价值不是由他选择了哪一门技术来决定,而是由他自身能创造出什么价值来决定. 在进入本篇内 ...
- Sql Server来龙去脉系列之四 数据库和文件
在讨论数据库之前我们先要明白一个问题:什么是数据库? 数据库是若干对象的集合,这些对象用来控制和维护数据.一个经典的数据库实例仅仅包含少量的数据库,但用户一般也不会在一个实例上创建太多 ...
- Sql Server来龙去脉系列之三 查询过程跟踪
我们在读写数据库文件时,当文件被读.写或者出现错误时,这些过程活动都会触发一些运行时事件.从一个用户角度来看,有些时候会关注这些事件,特别是我们调试.审核.服务维护.例如,当数据库错误出现.列数据被更 ...
- Sql Server来龙去脉系列之二 框架和配置
本节主要讲维持数据的元数据,以及数据库框架结构.内存管理.系统配置等.这些技术点在我们使用数据库时很少接触到,但如果要深入学习Sql Server这一章节也是不得不看.本人能力有限不能把所有核心的知识 ...
- Sql Server来龙去脉系列之一 目录篇
从工作一直到现在都没怎么花功夫深入学习下Sql Server数据库,在使用Sql Server时90%的时间基本上都是在接触T-SQL,所以数据库这块基本上属于菜鸟级别.至于数据库的底层框架以及运行机 ...
- SQL Server扫盲系列——安全性专题——SQL Server 2012 Security Cookbook
由于工作需要,最近研究这本书:<Microsoft SQL Server 2012 Security Cookbook>,为了总结及分享给有需要的人,所以把译文公布.预计每周最少3篇.如有 ...
- SQL Server中查询用户的对象权限和角色的方法
--SQL Server中查询用户的对象权限和角色的方法 -- 查询用户的object权限 exec sp_helprotect NULL, 'sa' -- 查询用户拥有的role exec sp_h ...
- 此操作只能由 SQL Server 中拥有配置数据库读取权限的用户在已加入到某个服务器场的计算机上执行
错误提示:此操作只能由 SQL Server 中拥有配置数据库读取权限的用户在已加入到某个服务器场的计算机上执行.若要将此服务器连接到服务器场,请使用 SharePoint 产品配置向导,该向导可从 ...
- Expert for SQL Server 诊断系列
Expert for SQL Server 诊断系列 Expert 诊断优化系列------------------锁是个大角色 前面几篇已经陆续从服务器的几个大块讲述了SQL SERVER数据库 ...
随机推荐
- 用户管理 之 用户(User)和用户组(Group)配置文件详解
用户(User)和用户组(Group)的配置文件,是系统管理员最应该了解和掌握的系统基础文件之一,从另一方面来说,了解这些文件也是系统安全管理的重要组成部份:做为一个合格的系统管理员应该对用户和用户组 ...
- Hadoop 2.7.1 使用minicluster进行单元测试
目的是要跑通这个单元测试:hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestClose.java 这个Java文件比较简单: /** * Lic ...
- Multiple Contexts have a path of "/xxxx"问题解决
转自:http://blog.csdn.net/ivanzhangqf/article/details/50326533 之前因为ecplise项目保存更新时一直显示等待后台操作完成,以至于一直卡住无 ...
- 使用Xcode6.1.1打包出现Your account already has a valid iOS Distribution certificate问题
1.问题描述: 使用客户证书在Xcode6.1.1上进行打包测试,出现如下问题,查看网上也很多类似错误且解决办法各异. 2.我的解决办法: 让客户将开发.发布证书重新revoke掉之后重新创新并给到p ...
- JavaScript中的setMonth()方法的小问题 解决:setMonth(month, 1)
今天测试人员发现一个问题,从英文日期转化中文日期,月份总会有“6月”变为“7月”.于是我在本地反复测试,发现如下规律:只要setMonth()的参数为小于31天的月份时就会变为下一个月. 原因是:因为 ...
- hibernate下载包中配置文件路径
路径:hibernate-release-5.0.2.Final\project\hibernate-ehcache\src\test\resources\hibernate-config 文件:hi ...
- WebDriver兼容SeleniumRC(基于C#)
WebDriver兼容SeleniumRC(基于C#)http://www.automationqa.com/forum.php?mod=viewthread&tid=3535&fro ...
- C#的回调被C++调用
__stdcall 要加这个 extern "C" __declspec(dllexport) 要用这样的东东 定义 extern "C" __declspec ...
- Xenia and Weights(深度优先搜索)
Xenia and Weights time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...
- java笔试练习题
选择题(共50题,每题1.5分,共75分.多选题选不全或选错都不得分.)1. 以下属于面向对象的特征的是(C,D).(两项)A) 重载B) 重写C) 封装D) 继承 2. 以下代码运行输出是(C)pu ...