My quest to improve gorm, the Object Relational Mapper for Go

blov · · 412 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I don&#39;t have a blog, I will just write here.</p> <p>For the impatient. <a href="https://github.com/gernest/ngorm"> here is the source code for the improved gorm</a></p> <p><strong>TL:DR</strong></p> <p>One redditor in critique of my web framework mentioned that using gorm as the main ORM was bad, the reason he said gorm was bloated. Since I don&#39;t believe in heresy, I decided to find out for myself.</p> <p>Before I go on and highlight the things that I found that I din&#39;t like and thought they could get some improvement, I would like to outline things that I like about gorm( In its current state).</p> <ul> <li><p>Clean API : The gorm API is elegant indeed, you get almost all the basic things you might need to interact with a relational database. Take for instance <code>db.First(&amp;user)</code> to find the first user. <code>db.Automigrate(&amp;User{})</code> to automaticallly migrate user model. I don&#39;t regret picking this ORM I will still pick it when given a choice 1000 times.</p></li> <li><p>Intuitive mapping to Go types: Mapping of results just works. You supply a pointer to your models and it is magically filled with results.</p></li> <li><p>It just works: This is one of the libraries that I don&#39;t have to worry about, It works like a charm.</p></li> </ul> <p>Okay, those are some of the coll stuffs I like about gorm.</p> <h1>The parts I found that could use some improvement</h1> <h3>Heavy reliance on globals that can lead to unpredictable behavior</h3> <p>Take for instance the function for updating time <a href="https://github.com/jinzhu/gorm/blob/0fbff1e8f0821fc67ef793a9f403beda5ca372b8/utils.go#L21">https://github.com/jinzhu/gorm/blob/0fbff1e8f0821fc67ef793a9f403beda5ca372b8/utils.go#L21</a> This function is used everywhere in gorm and it is exported , so any part of your application can change this and you will have inconstant time value. This can be seen as a feature, but I don&#39;t think it is a good idea, and here is why.</p> <pre><code>func CrazyTime() time.Time { src := &#34;Mon Jan 2 15:04:05 -0700 MST 2006&#34; t, _ := time.Parse(time.ANSIC, src) return t } gorm.NowFunc=CrazyTime </code></pre> <p>Adding this function anywhere in your application will result into confusing time values depending where you called it, how and when you did that.</p> <p>For instance <a href="https://github.com/jinzhu/gorm/blob/9edd66250e8ae11d572213054643b7bb1ce4d102/callback_create.go#L33-L37">https://github.com/jinzhu/gorm/blob/9edd66250e8ae11d572213054643b7bb1ce4d102/callback_create.go#L33-L37</a></p> <pre><code>func updateTimeStampForCreateCallback(scope *Scope) { if !scope.HasError() { now := NowFunc() scope.SetColumn(&#34;CreatedAt&#34;, now) scope.SetColumn(&#34;UpdatedAt&#34;, now) } } </code></pre> <p>That is where CreatedAT and UpdateAt time stamps are added. Guess what, with our <code>CrazyTime</code> function both fields will be not be accurate, well they will all contain the value returned by <code>CrazyTime</code>. This happens all over the code base multiple places relies on this.</p> <p>The reliance on global scope doesn&#39;t end here. I will need another post, maybe even longer to highlight this.</p> <h2>Project structure</h2> <p>This is true for many Go projects which have been around for a long time. Having everything under one roof with this kind of codebase makes it hard to navigate, and result in obscure naming( you can possibly out of names! lol!). <code>scope</code>,<code>search</code> and <code>DB</code> are all mixed together it is hard to follow when trying to make changes. And difficult to know what happens where and when.</p> <h2>Execution model</h2> <p>Simply there is no execution model. Anything can happen anywhere and god knows how! I wont go into details abouut how callbacks come into the picture.</p> <p>Okay, not to bore you I did try to improve this. Address all the issues. The changes were breaking the API so I didn&#39;t bother the author first, I just forked and started to work on it.</p> <p>These are highlights on how some of the issues I highlighted here were addressed. </p> <p><strong>NOTE</strong> : I implemented a dialect for ql database, since I needed something fast and easy to experiment with my ideas ql was the best thing I can find, the goal was not to support everything lets stick with that first.</p> <h2>Eliminating global scope</h2> <p>Yep, globals are gone, the problem of unpredictability is gone too. Instead anything that can be shared is passed explicitly through the <code>engine struct</code></p> <pre><code>//Engine is the driving force for ngorm. It contains, Scope, Search and other //utility properties for easily building complex SQL queries. type Engine struct { RowsAffected int64 //When this field is set to true. The table names will not be pluarized. //The default behaviour is to plurarize table names e.g Order struct will //give orders table name. SingularTable bool Ctx context.Context Dialect dialects.Dialect Search *model.Search Scope *model.Scope StructMap *model.SafeStructsMap SQLDB model.SQLCommon Log *logger.Zapper Now func() time.Time } </code></pre> <p>See the now func? Yeah, if you go on and change it, only the functions that will be applied with this engine will be affected. There is more into this story, and It is still a work in progress, so a lot of room for improvement</p> <h2>Fix project structure</h2> <pre><code>. ├── builder │ ├── sql.go │ └── sql_test.go ├── dialects │ ├── ql │ │ ├── ql.go │ │ └── ql_test.go │ └── dialect.go ├── engine │ ├── db.go │ └── db_test.go ├── errmsg │ └── errors.go ├── fixture │ └── fixtures.go ├── hooks │ ├── default.go │ └── hooks.go ├── logger │ └── logger.go ├── model │ ├── field.go │ ├── field_test.go │ └── struct.go ├── regexes │ └── regexes.go ├── scope │ ├── join_table.go │ ├── scope.go │ └── scope_test.go ├── search │ ├── search.go │ └── search_test.go ├── util │ ├── utils.go │ └── utils_test.go ├── GUIDE.md ├── License ├── README.md ├── examples_test.go ├── ngorm.go └── ngorm_test.go 13 directories, 29 files </code></pre> <h2>Fix execution model</h2> <p>There are two phases, SQL generation and SQL execution. The areas which builds SQL are in <code>builder</code> subpackcge. Execution is handled in expicit manner, in way that you can easy know who is executing what and when.</p> <p><strong>SUMMARY</strong>: gorm needs improvement not bashing without any evidence!</p> <p>Unfortunate, I can&#39;t keep hacking on this. I have to go back to learning algorithms, it saddens me that startups have a fetish for algorithms it is okay though. It was a fun endeavor!</p> <p><a href="https://github.com/gernest/ngorm">source code to the gorm with improvement</a> Enjoy!</p> <p><strong>EDIT</strong>: Fix code blocks <strong>EDIT</strong>2:Remove unnecessary text</p> <hr/>**评论:**<br/><br/>jimmjii: <pre><blockquote> <p>SUMMARY: gorm needs improvement not bashing without any evidence!</p> </blockquote> <p><a href="/r/golang" rel="nofollow">r/golang</a> is a small subset of the &#34;go community&#34;, I wouldn&#39;t pay attention too much on what is said here. You don&#39;t have to feel compelled to listen whatever is said here, me included. This sub community is heavily biased against anything that isn&#39;t in the standard library, ORM and &#34;frameworks&#34; (like a basic http router is a Framework ...). Plenty of people like gorm and find it useful. Is there room for improvement ? always, but don&#39;t waste your time listening to people here. They represent nothing but themselves. If people really wanted to contribute they would open an issue on github instead of trying to shame your project publicly. </p></pre>Spirit_of_Stallman: <pre><blockquote> <p>Database support ql</p> </blockquote> <p>Killer feature for me :) </p></pre>thesilentwitness: <pre><p>Or you could just use a better ORM and call it a day.</p></pre>bboozzoo: <pre><blockquote> <p>better ORM</p> </blockquote> <p>Care to name one?</p></pre>thesilentwitness: <pre><p>Take your pick: <a href="https://github.com/avelino/awesome-go">https://github.com/avelino/awesome-go</a></p> <p>Here&#39;s some benches of the most popular ones including the one I use (SQLBoiler): <a href="https://github.com/vattle/sqlboiler#benchmarks">https://github.com/vattle/sqlboiler#benchmarks</a></p></pre>bboozzoo: <pre><p>Thanks</p></pre>UnknownTed: <pre><p>i like <a href="https://github.com/go-pg/pg" rel="nofollow">https://github.com/go-pg/pg</a></p></pre>pkieltyka: <pre><p>I suggest to also look at: <a href="https://github.com/upper/db" rel="nofollow">https://github.com/upper/db</a></p></pre>AllThingsWiseWndrful: <pre><p>i like gorm too. big thank you to the developers. It saves people a lot of time on the small and medium projects we are doing. </p></pre>comrade-jim: <pre><p>Companies pay other companies to pay shills to come into tech communities and disrupt things. </p></pre>zackkitzmiller: <pre><p>Why do you think startups have a fetid how for algorithms? I certainly done think that is the case. </p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

412 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传