Git.Framework 是近几年工作的一些工作经验总结,虽不能和某些知名的框架相提并论,但是还是比较实用的。此框架经过三年多的升级和维护,已经具有较强的实用性,在此记录该框架的使用操作方式,贡献给公司第一线开发的技术人员们,感谢你们所付出的努力。
一. 框架由来
前几年我都是在互联网公司工作,做什么大型B2B,B2C网站。至于有多大就不提了,但是在其间做开发人员使用的技术的确比较辛苦,那个时候公司使用的技术还比较落后,最起码我是这么认为的,开发效率很低,一层不变的开发模式,Copy到想吐的代码。我是从事.NET技术开发的,当时公司还是使用的.NET2.0开发的,要知道2.0 和 3.0 差别还是挺大的,最起码在语法上。
据说公司的框架是参考某个大型B2C网站的,据说当时公司某技术君就是从那里面出来的,使用微软企业库(Enterprise Library) , 于是一个B2C网站+ SNS社区 几乎所有的数据访问操作都是写的SQL语句,而且都是配置在某个文件夹的配置文件中。做过B2C或者SNS方面开发的可以想象得到有多少个SQL语句,当时我估计也有好几千个吧。于是我就想在原有的技术框架上改进这个操作,经过世事变迁最终改的体无完肤也就是现在所谓的的框架(Git.Framework)了。
二. 写SQL语句复杂在哪里
先给大家看看当时我们的SQL语句的写法,以及如何配置:
<dataCommand name="User.UpdateAllBase" database="Git" commandType="Text">
<commandText>
<![CDATA[
UPDATE [Gas_BasicCenter].[dbo].[User_Base]
SET [UserName] = @UserName
,[Email] = @Email
,[Password] = @Password
,[RegisterDate] = @RegisterDate
,[RegisterIp] = @RegisterIp
,[Status] = @Status
,[RegisterApplicationID] = @RegisterApplicationID
,[ActiveDate] = @ActiveDate
,[LastLoginDate] = @LastLoginDate
,[LastLoginApplicationID] = @LastLoginApplicationID
,[RegisterSource] = @RegisterSource
,[AuditStatus] = @AuditStatus
,[IsLogin] = @IsLogin
,[LoginCount] = @LoginCount
,[LastLoginIp] = @LastLoginIp
,[AuditUser] = @AuditUser
,[AuditDate] = @AuditDate
,[IsDeleted] = @IsDeleted
,[OLDApplicationID] = @OLDApplicationID
,[OLDID] = @OLDID
,[RowGuid] = @RowGuid
,[IMNum] = @IMNum
,[Phone] = @Phone
,[IsEmailValidate] = @IsEmailValidate
,[IsPhoneValidate] = @IsPhoneValidate
,[StepNum] = @StepNum
,[SaleCode]=@SaleCode
,[PasswordIM]=PasswordIM
,[ActiveIP]=@ActiveIP
,[CompanyType]=@CompanyType
Where UserId=@UserId
]]>
</commandText>
<parameters>
<param name="@UserId" dbType="Int32" direction="Input"/>
<param name="@UserName" dbType="String" direction="Input"/>
<param name="@Email" dbType="String" direction="Input"/>
<param name="@Password" dbType="String" direction="Input"/>
<param name="@RegisterDate" dbType="DateTime" direction="Input"/>
<param name="@RegisterIp" dbType="String" direction="Input"/>
<param name="@Status" dbType="Int32" direction="Input"/>
<param name="@RegisterApplicationID" dbType="Int32" direction="Input"/>
<param name="@ActiveDate" dbType="DateTime" direction="Input"/>
<param name="@LastLoginDate" dbType="DateTime" direction="Input"/>
<param name="@LastLoginApplicationID" dbType="Int32" direction="Input"/>
<param name="@RegisterSource" dbType="Int32" direction="Input"/>
<param name="@AuditStatus" dbType="Int32" direction="Input"/>
<param name="@IsLogin" dbType="Int16" direction="Input"/>
<param name="@LoginCount" dbType="Int32" direction="Input"/>
<param name="@LastLoginIp" dbType="String" direction="Input"/>
<param name="@AuditUser" dbType="String" direction="Input"/>
<param name="@AuditDate" dbType="DateTime" direction="Input"/>
<param name="@IsDeleted" dbType="Int16" direction="Input"/>
<param name="@OLDApplicationID" dbType="Int32" direction="Input"/>
<param name="@OLDID" dbType="Int32" direction="Input"/>
<param name="@RowGuid" dbType="String" direction="Input"/>
<param name="@IMNum" dbType="String" direction="Input"/>
<param name="@Phone" dbType="String" direction="Input"/>
<param name="@IsEmailValidate" dbType="Int32" direction="Input"/>
<param name="@IsPhoneValidate" dbType="Int32" direction="Input"/>
<param name="@StepNum" dbType="Int32" direction="Input"/>
<param name="@SaleCode" dbType="String" direction="Input"/>
<param name="@PasswordIM" dbType="String" direction="Input"/>
<param name="@ActiveIP" dbType="String" direction="Input"/>
<param name="@CompanyType" dbType="Int32" direction="Input"/>
</parameters>
</dataCommand>
SQL语句的配置
情况1:看到上面的配置情况,很多人会联系到java中的某些框架,的确如此!个人对java涉略较少,后来在专门去查找了相关的资料。先不讨论SQL语句配置的格式,一个B2C网站+SNS社区需要多少这样的配置节点,项目在不断的增大,配置的难度到了让人无法接受的程度,而且我们无法知道是否有人已经写过一个同样的SQL语句。有多少人的离开或多或少与这个有关系,那个时候系统出现bug我就是每天盯着屏幕ctrl+h 全局查找这样的配置。
情况2:SQL语句要每个自己去写,转化还必须要占位符的方式,然后要填充参数(<param name="@UserId" dbType="Int32" direction="Input"/> 也就是这个配置)。当然这些都是手工体力劳动,不难当时的确会写死人,比类文件中直接写SQL还要累。
情况3: <dataCommand name="User.UpdateAllBase" database="Git" commandType="Text"> 配置项name在全局是唯一的,而系统中有很多个这样的配置文件,在这个文件中你查找不到,并不代表其他的文件没有,要每一个文件都去查找一遍然后才能定义这个name值。 database 这个也存在问题,这个是关联数据库的,因为系统中存在多个数据库,必须关联这个sql在哪个库里面执行。
情况4:<parameters><param name="@UserId" dbType="Int32" direction="Input"/></parameters> 参数项的配置太多,特别是表的字段多的时候,修改好烦躁。有时候修改一个字段,有时候修改其中几个字段,要分别写不同的SQL来执行。
情况N .... 就不多写了,各种问题足以让人抓狂,拿人钱财替人写代码天经地义的事情所以没办法,硬着头皮写。
三. 以上问题的改进
那个时候自己还是小兵啊,人微言轻的,也不敢提框架不好使,不然大牛会生气,后果很严重。想想也是完成好自己的工作就好了。
配置问题的解决:
上面贴出来的这个配置文件,其实一个定义好的标准格式,包括里面的参考类型以及名称都是定义好的,后来我使用CodeSmith自定义模板生成,很快速的结果手工写模板的问题,但是有一个问题就是模板没有那么灵活,我需要指定哪些字段要使用哪些不能使用,不是那么自能,只能全部给我生成出来,不过也算是方便了不少。
实际问题考虑:
(1) 登录系统,两个步骤: 1. 查询用户名和密码 2. 查询用户详细信息
都是使用User表,我们可以查询User表的所有数据,但是往往登录的时候我们只会查询用户名和密码,查询用户详细的时候又不查询密码,甚至还有权限的问题,网络传输的问题。只能查询当前业务操作所需的字段。
SELECT [UserNo],[UserPwd] FROM [dbo].[SysUser] SELECT [UserNo],[UserCode],[UserName],[UserPwd],[IsDelete] FROM [dbo].[SysUser]
以上两个SQL语句有区别么,肯定有区别,查询的虽然是同一张表,但是返回的数据量大小不一样,为什么要这么做? 后来做信息化管理系统之后,也就不管这么多了都直接使用 Select * From Table 直接代替了。
(2) 修改个人资料信息
以前工作的项目图片不好找了,直接以博客园的为例。 假设用户头像,昵称,姓名,联系方式,毕业院校等信息是保持在User表中(当然现在很多信息也有是分表处理的,暂且不谈,这是数据结构设计问题)的。看上面的截图,用户信息的维护每次用户数据的提交并不是提交了所有的个人信息数据,而只是部分提交的。
Update [dbo].[SysUser] Set Picture=@Picture where UserNo=@UserNo Update [dbo].[SysUser] Set LoginCount=@LoginCount,UserName=@UserName where UserNo=@UserNo
诸如以上的这种太多的有共性但是又有区别的SQL语句太多了。
后来使用的方式就是,先将某条数据全部查询出来,封装为一个Entity,然后修改Entity中的某个值,然后提交所有的Entity值,也就是修改表中的每一个字段,虽然处理的方式不好,但是比写N个这样的Sql语句配置省事多了。
四. 如何检查配置项的重复问题
颇为头疼的就是配置项name值的重复问题,N多个配置文件在某一个文件夹下,而文件夹中又有文件夹,查找不方便。
软件的作用是什么,这个问题太过于广泛,但是有一点软件能够省人工,这个我是赞同。如何去查找重复项,那么就写一个小程序去区分好了。程序非常简单就是读取所有的配置文件,然后盘点是否有相同的项,这里不必多说,但是这个时候让我学到一个技能,就是如何写VS插件。
之前写的工具已经不复存在,刚才在网上随便找了一篇文章关于这个的介绍,有兴趣可以参考一下:
http://blog.csdn.net/clever101/article/details/8733799
五. 实质性的问题如何解决
以上种种手段都是暂时的解决问题,并没有达到治本的目的。于是自己开始筹划改进公司的技术框架。
当时公司的技术体系就是这样,基于以前的工作经验,你想要去推翻公司的技术体系然后再重新来过是不太可能的,而且这个想法也是过于天真的,先不说你是否有这个能力,就算你有公司也不会冒这样的风险。
当然当时我也提过技术框架的改进,当时领导评估风险太大,只能慢慢的去维护,也就是公司不可能专门设计组织或项目来搞这个东西,所以我只能自己业余时间来了。
1. 数据操作使用ORM是不可避免的方向
当时推崇过一段时间的Linq to SQL,包括Entity Framework ,也给公司提过,公司坚决的否定了,而后来我也对此嗤之以鼻因为其臃肿度,也不知道我是否理解错了。研究了好长一段时间的Linq to SQL ,Entity Framework 后来也不不再理这个。
对比了一些较为常见的ORM,无论怎么吹嘘怎么怎么强大但都有其天生的缺陷性,没有一个是万能的。后来我也承认这些东西不可能是万能的,所谓真正好的框架是在实际的业务中提炼出来的,而不是你想怎样就能帮你解决问题的。
加上要兼容公司原有的技术体系,所以这个只能改进不能重新来过。
2. 去配置化
那个时候还年轻啊,听老前辈说配置化如何的牛逼,特别是java程序员,java中的配置化多么牛逼,系统如何的灵活! 现在我始终认为那是一个骗局,要么是我没有理解其精髓,要么他们也就是照书上照本宣科的胡乱说一通。这不是重点,重点是如果我改进一定要去配置化,因为配置已经让我们身处火热之中。
3. 蛋疼的跨数据库查询
如果在同一个数据库实例中,两个或者多个数据库跨库查询貌似还比较好解决,如果涉及到跨网络肿么办,貌似没有ORM专门去解决这个问题,或许是我孤陋寡闻了。在公司的改进中我提议是跨库查询还是在内存中去处理,就是多次去查询数据库然后内存整合数据,后来也证明了我还是有点明智的,多数据库分离到不同的服务器上了。
六. 本文小结
在平时的工作中还有很多类似的问题,不知道是因为自己遇到的东西少了还是其他的原因,都是一些非常低级而又苦恼的问题,真正有什么科研技术的问题我真没有遇到。决定写这个文章只是为了总结一下自己这些年的工作,觉得有很多地方是值得借鉴的。
1. 在一个技术体系已经成型的团队里面,不要妄想去推翻现有的技术体系,虽然这样的体系问题非常多,多到让你无法接受,你要做的是改进,去提升,而不是抱怨问题太多。"要么忍要么滚" 这是我的格言。
2. 问题多是一个团队的正常表现,如果没有问题团队也就不需要你了。
3. 只要是问题肯定就会有解决的办法,如果你自己没有去尝试过,不要轻易去评判别人的对与错,否则就闭嘴。
4. 刚出道或者刚进公司不要认为领导都是傻逼,如果你有这种想法只能说明你比他们更傻逼,几年前我也站在骂领导是傻逼的行列。
5. 任何一个公司的技术体系都有值得你去借鉴学习的,只要它摆在那里就不会是垃圾。
如今自己做实施了,虽然程序还写,相比之前心情放松了很多,不是为了丢弃代码,只是有时候该换一种方式去思考或许程序会写的更好一点。
成功的定义永远只有一个,那就是对结果负责!