想想畢業已經快一年了,也就是大約兩年以前,懷着滿腔的熱血正式跨入程序員的世界,那時候的自己想象着所熱愛的技術生涯會是多么的豐富多彩,每天可以與大佬們坐在一起討論解決各種牛逼的技術問題,喝着咖啡,翹着二郎腿,大致就是下面這幅場景:
可是現實卻總是那么不盡如人意,現實的所謂技術生涯是永遠寫不完的增刪改查,還有那日漸稀薄的頭發,哎,難...
話說回來,如果連增刪改查都做不好,還談何技術生涯。
所以,我一直認為:如果你認為自己是一個優秀的程序員,那就應該從最基礎的增刪改查中就能體現出來。
有人可能會說,增刪改查?很難嗎?
其實,說難,也不難,無非就是寫SQL,Add、Delete、Update、Select,僅此而已;但是如果只是從SQL的角度認為這很容易,而對其不懈一顧,那你可能就和當初剛剛開始增刪改查大業的我一樣,太天真了。
首先,我和大家分享一下我在工作這將近兩年中在增刪改查這條路上的心路歷程。
一、初出茅廬——原生sql走天下,sql寫到手抽筋
某年某月某日,大三尾聲,翹課在宿舍睡大覺的我剛從床上艱難地爬起來,打開手機,發現剛工作的老學長阿威給我發來了一條信息:
"阿森,來活了,幫我搞個網站,會弄嗎?"
"網站?沒弄過誒。。"
"這個項目給的銀兩還挺多的。。"
"可以,可以,這個可以搞,沒問題,阿威哥"
就這樣,我就走上了Web開發的道路。初入坑,首先遇到的問題就是數據庫的操作,但是經過自己的旁門左道的學習也大致摸清了使用ADO.NET操作數據庫的方法:
第一步,使用DBConnection建立與數據庫之間的連接,打開連接
第二步,然后創建DBCommand對象,初始化你想要執行的SQL語句
第三步,調用DBCommand的方法,執行SQL語句
第四步,使用DataReader逐條讀取sql執行的結果,或者使用DataAdapter類將結果填充(Fill)到DataSet中,最后關閉Connection連接
到此為止,就實現了在.net中操作數據庫執行sql語句返回執行結果的操作。
當然,我覺得任何一個初級的程序員也都會給自己封裝一個叫做SQLHelper的幫助類,我也不例外,它可以幫助你節省頻繁的建立數據庫連接(DBConnection)、初始化DBCommand對象、讀取sql執行結果等重復的代碼量。所以,封裝一個自己的SQLHelper工具類是一個初級程序員必備的素質。
我把一個當初自己封裝的SQLHelper類貼出來獻獻丑:

1 public static class SqlServerHelper 2 { 3 //數據庫連接字符串,從配置文件的配置字段中讀取 4 private static readonly string connStr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; 5 6 /// <summary> 7 /// 創建新的數據庫連接 8 /// </summary> 9 public static SqlConnection CreateSqlConnection() 10 { 11 SqlConnection conn = new SqlConnection(connStr); 12 13 conn.Open(); //打開數據庫連接 14 15 return conn; 16 } 17 18 /// <summary> 19 /// 執行無結果返回的sql語句(共用同一個連接) 20 /// </summary> 21 /// <param name="sqlConn"></param> 22 /// <param name="sqlStr"></param> 23 /// <param name="sqlParamters"></param> 24 /// <returns>返回受影響的數據總條數</returns> 25 public static long ExecuteNoQuery(SqlConnection sqlConn, string sqlStr, params SqlParameter[] sqlParamters) 26 { 27 using (SqlCommand cmd = sqlConn.CreateCommand()) 28 { 29 cmd.CommandText = sqlStr; 30 cmd.Parameters.AddRange(sqlParamters); 31 32 long sum = cmd.ExecuteNonQuery(); 33 34 return sum; 35 } 36 } 37 38 /// <summary> 39 /// 執行無結果返回的sql語句(不共用一個連接) 40 /// </summary> 41 /// <param name="sqlStr"></param> 42 /// <param name="sqlParamters"></param> 43 /// <returns>返回受影響的數據總條數</returns> 44 public static long ExecuteNoQuery(string sqlStr, params SqlParameter[] sqlParamters) 45 { 46 using (SqlConnection sqlConn = CreateSqlConnection()) 47 using (SqlCommand cmd = sqlConn.CreateCommand()) 48 { 49 cmd.CommandText = sqlStr; 50 cmd.Parameters.AddRange(sqlParamters); 51 52 long sum = cmd.ExecuteNonQuery(); 53 54 return sum; 55 } 56 } 57 58 /// <summary> 59 /// 執行只有一行一列返回數據的sql語句(共用同一個連接) 60 /// </summary> 61 /// <param name="sqlConn"></param> 62 /// <param name="sqlStr"></param> 63 /// <param name="sqlParamters"></param> 64 /// <returns>返回結果的第一行第一列數據</returns> 65 public static object ExecuteScalar(SqlConnection sqlConn, string sqlStr, params SqlParameter[] sqlParamters) 66 { 67 using (SqlCommand cmd = sqlConn.CreateCommand()) 68 { 69 cmd.CommandText = sqlStr; 70 cmd.Parameters.AddRange(sqlParamters); 71 72 object res = cmd.ExecuteScalar(); 73 74 return res; 75 } 76 } 77 78 /// <summary> 79 /// 執行只有一行一列返回數據的sql語句(不共用同一個連接) 80 /// </summary> 81 /// <param name="sqlStr"></param> 82 /// <param name="sqlParamters"></param> 83 /// <returns>返回結果的第一行第一列數據</returns> 84 public static object ExecuteScalar(string sqlStr, params SqlParameter[] sqlParamters) 85 { 86 using (SqlConnection sqlConn = CreateSqlConnection()) 87 using (SqlCommand cmd = sqlConn.CreateCommand()) 88 { 89 cmd.CommandText = sqlStr; 90 cmd.Parameters.AddRange(sqlParamters); 91 92 object res = cmd.ExecuteScalar(); 93 94 return res; 95 } 96 } 97 98 /// <summary> 99 /// 執行有查詢結果的sql語句(共用同一個連接) 100 /// </summary> 101 /// <param name="sqlConn"></param> 102 /// <param name="sqlStr"></param> 103 /// <param name="sqlParamters"></param> 104 /// <returns></returns> 105 public static DataTable ExcuteQuery(SqlConnection sqlConn, string sqlStr, params SqlParameter[] sqlParamters) 106 { 107 using (SqlCommand cmd = sqlConn.CreateCommand()) 108 { 109 cmd.CommandText = sqlStr; 110 111 cmd.Parameters.AddRange(sqlParamters); 112 113 //把sql語句的執行結果填充到SqlDataAdapter中 114 SqlDataAdapter adapter = new SqlDataAdapter(cmd); 115 116 DataTable dt = new DataTable(); 117 adapter.Fill(dt); //將執行結果填充到dt對象中 118 119 return dt; 120 } 121 } 122 123 /// <summary> 124 /// 執行有查詢結果的sql語句(共用同一個連接) 125 /// </summary> 126 /// <param name="sqlStr"></param> 127 /// <param name="sqlParamters"></param> 128 /// <returns></returns> 129 public static DataTable ExcuteQuery(string sqlStr, params SqlParameter[] sqlParamters) 130 { 131 using (SqlConnection sqlConn = CreateSqlConnection()) 132 using (SqlCommand cmd = sqlConn.CreateCommand()) 133 { 134 cmd.CommandText = sqlStr; 135 136 cmd.Parameters.AddRange(sqlParamters); 137 138 //把sql語句的執行結果填充到SqlDataAdapter中 139 SqlDataAdapter adapter = new SqlDataAdapter(cmd); 140 141 DataTable dt = new DataTable(); 142 adapter.Fill(dt); //將執行結果填充到dt對象中 143 144 return dt; 145 } 146 } 147 148 /// <summary> 149 /// 批量插入數據到數據庫 150 /// </summary> 151 /// <param name="insertTable"></param> 152 /// <param name="dataTableName"></param> 153 public static void BatchInsert(DataTable insertTable, string dataTableName) 154 { 155 using (SqlBulkCopy sbc = new SqlBulkCopy(connStr)) 156 { 157 sbc.DestinationTableName = dataTableName; 158 159 for (int i = 0; i < insertTable.Columns.Count; i++ ) 160 { 161 sbc.ColumnMappings.Add(insertTable.Columns[i].ColumnName, insertTable.Columns[i].ColumnName); 162 } 163 164 sbc.WriteToServer(insertTable); 165 } 166 } 167 168 }
於是在那段開發的歲月里,是這個SQLHelper類陪我走完了全程,向它說聲辛苦了...
二、EF框架真香,Lambda表達式我最愛
大三暑假,由於抑制不住想要出去試一試的沖動,我來到了一家小型互聯網公司實習,公司也是用.net開發網站的技術棧。
當上崗的第一天,我准備掏出我自認為牛逼的SQLHelper的時候,卻被項目經理叫停了,項目經理阿勇對我說:
“小伙子,21世紀了,還在傻傻地用SQLHelper?來,試試EF吧,讓你欲罷不能...”
於是,一臉懵逼的我,一頭埋進了EF的世界中,不得不說,的確香。
EF全名EntityFramework,是微軟官方的一款ORM框架,支持Object(對象)與數據庫之間的數據映射,支持Linq的操作語法,受廣大.NET程序員青睞。
其實在接觸EntityFramework之前我就使用過Dapper,Dapper相對與EF來說是輕量的多的一款ORM框架,近乎於原生sql的寫法,讓它處於性能之最,也提供對象與數據庫表之間的映射。
但正是由於用過了EF,我才知道了原來還有 Lambda to SQL 這種東西,相信大多數程序員都和我一樣經歷過寫原生SQL的痛苦:
無論是多么簡單的一條sql,你都要在表設計文檔和sql編輯器中反復切換比對字段,生怕自己打錯一個字母;而且我們在基礎的業務中的大部分增刪改查其實都是相當簡單的單表操作sql,正是使用了Lambda to SQL 的代碼編寫方式,你可以在VsStudio豐富的代碼提示下飛快地完成一條簡單的SQL語句。
為什么說簡單的SQL編寫才使用Lambda表達式的形式去編寫呢,復雜的sql就不行了嗎?
理由是,你要明白不管是Lambda還是Linq的語句,最終都是在EF的SQL語句解析器中被翻譯成可供數據庫執行的sql語句,不得不說EF的Linq To SQL 的解析器的確強大,但是畢竟還是機器翻譯的sql,當你讓它給你轉化一個復雜的linq語句時,它是一定考慮不到sql性能的優化。
就像你去用金山詞霸翻譯一個單詞,它可以給你翻譯的很准確;但是你讓它去幫你翻譯一個長句,可能你讀着就覺得別扭了;更有甚者把一篇論文粘貼復制到金山詞霸里,那可能比你直接看英文原文還費勁,這是同樣一個道理,有些東西還是需要人工來做,就比如優化sql語句。不能奢求什么都扔給Lambda和Linq,它幫我們做的已經足夠多了。
總之一句話,Lambda很香,但絕對不能替代SQL。
三、全部都是存儲過程,調試太難
畢業后的我來到了一家大型制造業企業從事IT行業,說的這么體面,其實還是一個苦逼敲代碼的。
上崗的第一天我就問旁邊的程序媛,對,你沒看錯,是“媛”,不是“猿”,嘿嘿。想我代碼生涯一年以來,第一次見這么多程序媛,低調低調,不要聲張,不然讓隔壁的程序猿聽到了會嫉妒的。
“姐姐,我們這用EF嗎?”
“EF?是啥...”,姐姐一臉懵逼。。。
“那你們用什么操作數據庫啊?自己封裝SQLHelper嗎?”
“用存儲過程啊,都用了7、8年了”
“哦,這樣啊”,我心想,都用了7、8年了,這個姐姐是個狠人。
入鄉隨俗,咱也不能壞了規矩,那就照着用吧,存儲過程以前用過幾次,但是那是在sql的邏輯比較復雜的時候才會寫存儲過程調用的,要是所有的sql都放在存儲過程里面去調用的話,那就太繁瑣了吧。
經過一段時間的使用,證明我的顧慮是對的。
全部使用存儲過程后,發現,就是一條簡單的Delete、Insert語句都要寫在存儲過程中,簡直不要太麻煩。
首先的問題就是,不好編寫,對於一個剛上手存儲過程語法的程序員來說這簡直就是災難,目前的SQL編輯器沒有語法提示功能而造成非常不好的代碼編寫體驗,嚴重拖慢開發進程;
更要命的是,太難調試!當你在c#代碼中調用完存儲過程后,你發現程序安然無恙地執行完,但是並沒有得到你想要的結果,於是你找啊找,找啊找,最后,你只能懷疑是不是存儲過程有問題,但是運行過程中存儲過程一點動靜也沒有啊,這就是使用存儲過程最大的弊端,悄無聲息地出錯,完全不知道里面發生了什么,只能默默地把存儲過程的參數記下,然后輸入到sql的調試其中執行一遍才可以發現錯誤。
然后你的項目經理跑過來問你昨天的代碼敲完了嗎,心累...
於是,我對存儲過程留下了非常不好的印象。但是存儲過程的好處是顯而易見的:當你的業務邏輯用一條sql解決不了的時候你會考慮使用多條sql來配合完成查詢,可是如果執行每一條sql語句都要與數據庫建立連接然后再執行的話好像有點繁瑣不說,主要是重建數據庫連接的過程也是需要時間與資源的,所以這個時候存儲過程就派上用場了,把你的sql都扔到存儲過程里面去吧,多么令人心曠神怡的操作,這個時候你就不要去想存儲過程出錯難找的問題了,因為你想要獲得方便總要有點犧牲精神。
四、沒有哪一種是完全適合自己的,如果有,那就是自己造的
想要有Dapper的神速,又想要有EF中使用lambda表達式的便捷,但是二者好像並不能兼得。
Dapper雖然是效率之王,但是它沒有EF中可以使用的便捷的lambda表達式;
EF雖然可以使用便捷的lambda、linq語法,可是由於EF的過分強大(EF還具有對象追蹤的功能),讓它顯得稍微有點臃腫,屬於重量級的ORM框架。
於是我就在想,有沒有一款輕量級的ORM,既有不低的執行效率,又有簡單實用的Lambda TO SQL?
答案當然是,有的,現在開源的國產sqlSuger、freeSql,比比皆是。但是,我就是想自己試試造輪子的感覺,目的並不是說造出多么好的ORM框架去超越他們,而是在這個過程中學到的東西和得到的歷練是非常可觀的。
沒辦法,有觀眾說“褲子都脫了,就給我看這個?”,我決定還是先把藏的貨先擺出來,也好有個交代...
源碼地址:https://gitee.com/xiaosen123/CoffeeSqlORM
本文為作者原創,轉載請注明出處:https://www.cnblogs.com/MaMaNongNong/p/12884871.html