diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f653215d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,54 @@ +# Change Log + +## v1.0 (unreleased) + +#### Breaking Changes + +* **`gorm.Open` return type `*gorm.DB` instead of `gorm.DB`** + +* **Updating will only update changed fields** + + Most applications won't be affected, only when you are changing updating values in callbacks like `BeforeSave`, `BeforeUpdate`, you should use `scope.SetColumn` then, for example: + + ```go + func (user *User) BeforeUpdate(scope *gorm.Scope) { + if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil { + scope.SetColumn("EncryptedPassword", pw) + // user.EncryptedPassword = pw // doesn't work, won't including EncryptedPassword field when updating + } + } + ``` + +* **Soft delete's default querying scope will only check `deleted_at IS NULL`** + + Before `db.Find(&user)` will generate querying SQL if user has `DeletedAt` field + + `SELECT * FROM users WHERE deleted_at IS NULL OR deleted_at <= '0001-01-02'` + + Now won't include blank time check `<= '0001-01-02` anymore, will generat SQL like: + + `SELECT * FROM users WHERE deleted_at IS NULL` + + So your application's `DeletedAt` field should not use `time.Time` as data type, need to use pointer `*time.Time` or something like `NullTime`. + If you are using `gorm.Model`, then you are good, nothing need to be change, just make sure all records using blank time for `deleted_at` has been set to NULL, sample migration script: + + ```go + import ( + "github.com/jinzhu/now" + ) + + func main() { + var models = []interface{}{&User{}, &Image{}} + for _, model := range models { + db.Unscoped().Model(model).Where("deleted_at < ?", now.MustParse("0001-01-02")).Update("deleted_at", gorm.Expr("NULL")) + } + } + ``` + +* **New ToDBName logic** + + Before when GORM convert Struct, Field's name to db name, only some common initialisms from [golint](https://github.com/golang/lint/blob/master/lint.go#L702) like `HTTP`, `URI` are special handled. + + So field `HTTP`'s db name will be `http` not `h_t_t_p`, but other initialisms like `SKU` not in golint, it's db name will be `s_k_u`, this release fixed this, any upper case initialisms should be converted correctly. + + If you applications using some upper case initialisms which doesn't exist in [golint](https://github.com/golang/lint/blob/master/lint.go#L702), you could set the column name with tag, like `sql:"column:s_k_u"`, or alert your database's column name in your database diff --git a/README.md b/README.md index 34d0c2d3..6d467f9a 100644 --- a/README.md +++ b/README.md @@ -653,7 +653,7 @@ db.Where("age = ?", 20).Delete(&User{}) // Soft deleted records will be ignored when query them db.Where("age = 20").Find(&user) -//// SELECT * FROM users WHERE age = 20 AND (deleted_at IS NULL OR deleted_at <= '0001-01-02'); +//// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL; // Find soft deleted records with Unscoped db.Unscoped().Where("age = 20").Find(&users) diff --git a/utils.go b/utils.go index f6bce479..7fc53fa5 100644 --- a/utils.go +++ b/utils.go @@ -76,7 +76,9 @@ func ToDBName(name string) string { if lastCase == upper && nextCase == upper { buf.WriteRune(v) } else { - buf.WriteRune('_') + if value[i-1] != '_' && value[i+1] != '_' { + buf.WriteRune('_') + } buf.WriteRune(v) } } else { @@ -92,8 +94,7 @@ func ToDBName(name string) string { buf.WriteByte(value[len(value)-1]) - s := strings.Replace(strings.ToLower(buf.String()), "__", "_", -1) - + s := strings.ToLower(buf.String()) smap.Set(name, s) return s } diff --git a/utils_test.go b/utils_test.go index 0e88a8b7..07f5b17f 100644 --- a/utils_test.go +++ b/utils_test.go @@ -13,6 +13,7 @@ func TestToDBNameGenerateFriendlyName(t *testing.T) { "PFAndESI": "pf_and_esi", "AbcAndJkl": "abc_and_jkl", "EmployeeID": "employee_id", + "SKU_ID": "sku_id", "HTTPAndSMTP": "http_and_smtp", "HTTPServerHandlerForURLID": "http_server_handler_for_url_id", "UUID": "uuid",