v0.1.8 union index and union unique supported

This commit is contained in:
Lunny Xiao 2013-07-27 12:24:38 +08:00
parent 2113d5fd4c
commit 5adfc8e923
8 changed files with 167 additions and 30 deletions

View File

@ -17,6 +17,7 @@ Drivers for Go's sql package which currently support database/sql includes:
## Changelog ## Changelog
* **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping).
* **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit. * **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit.
* **v0.1.6** : Added conversion interface support; added struct derive support; added single mapping support * **v0.1.6** : Added conversion interface support; added struct derive support; added single mapping support
* **v0.1.5** : Added multi threads support; added Sql() function for struct query; Get function changed return inteface; MakeSession and Create are instead with NewSession and NewEngine. * **v0.1.5** : Added multi threads support; added Sql() function for struct query; Get function changed return inteface; MakeSession and Create are instead with NewSession and NewEngine.
@ -304,7 +305,7 @@ Another is use field tag, field tag support the below keywords which split with
<table> <table>
<tr> <tr>
<td>name</td><td>column name</td> <td>name</td><td>column name, if no this name, the name is auto generated according field name and mapper rule.</td>
</tr> </tr>
<tr> <tr>
<td>pk</td><td>the field is a primary key</td> <td>pk</td><td>the field is a primary key</td>
@ -319,7 +320,10 @@ Another is use field tag, field tag support the below keywords which split with
<td>[not ]null</td><td>if column can be null value</td> <td>[not ]null</td><td>if column can be null value</td>
</tr> </tr>
<tr> <tr>
<td>unique</td><td>unique</td> <td>unique or unique(uniquename)</td><td>unique or union unique as uniquename</td>
</tr>
<tr>
<td>index or index(indexname)</td><td>index or union index as indexname</td>
</tr> </tr>
<tr> <tr>
<td>extends</td><td>used in anonymous struct means mapping this struct's fields to table</td> <td>extends</td><td>used in anonymous struct means mapping this struct's fields to table</td>
@ -334,7 +338,7 @@ For Example
```Go ```Go
type Userinfo struct { type Userinfo struct {
Uid int `xorm:"id pk not null autoincr"` Uid int `xorm:"id pk not null autoincr"`
Username string Username string `xorm:"unique"`
Departname string Departname string
Alias string `xorm:"-"` Alias string `xorm:"-"`
Created time.Time Created time.Time

View File

@ -16,6 +16,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 更新日志 ## 更新日志
* **v0.1.8** : 新增联合index联合unique支持请查看[映射规则](#mapping)。
* **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现可以选择不使用连接池使用系统连接池和使用自带连接池三种实现默认为SysConnectPool即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法在系统退出时应调用此方法。 * **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现可以选择不使用连接池使用系统连接池和使用自带连接池三种实现默认为SysConnectPool即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法在系统退出时应调用此方法。
* **v0.1.6** : 新增Conversion支持自定义类型到数据库类型的转换新增查询结构体自动检测匿名成员支持新增单向映射支持 * **v0.1.6** : 新增Conversion支持自定义类型到数据库类型的转换新增查询结构体自动检测匿名成员支持新增单向映射支持
* **v0.1.5** : 新增对多线程的支持新增Sql()函数支持任意sql语句的struct查询Get函数返回值变动MakeSession和Create函数被NewSession和NewEngine函数替代 * **v0.1.5** : 新增对多线程的支持新增Sql()函数支持任意sql语句的struct查询Get函数返回值变动MakeSession和Create函数被NewSession和NewEngine函数替代
@ -70,6 +71,11 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close() defer engine.Close()
``` ```
具体连接请参考:
Mysqlhttps://github.com/go-sql-driver/mysql#dsn-data-source-name
Sqlite
1.1.默认将不会显示自动生成的SQL语句如果要显示则需要设置 1.1.默认将不会显示自动生成的SQL语句如果要显示则需要设置
```Go ```Go
@ -305,10 +311,10 @@ UserInfo中的成员UserName将会自动对应名为user_name的字段。
<table> <table>
<tr> <tr>
<td>name</td><td>当前field对应的字段的名称可选</td> <td>name</td><td>当前field对应的字段的名称可选如不写则自动根据field名字和转换规则命名</td>
</tr> </tr>
<tr> <tr>
<td>pk</td><td>是否是Primary Key</td> <td>pk</td><td>是否是Primary Key当前仅支持int64类型</td>
</tr> </tr>
<tr> <tr>
<td>int(11)/varchar(50)/text/date/datetime/blob/decimal(26,2)</td><td>字段类型</td> <td>int(11)/varchar(50)/text/date/datetime/blob/decimal(26,2)</td><td>字段类型</td>
@ -320,7 +326,10 @@ UserInfo中的成员UserName将会自动对应名为user_name的字段。
<td>[not ]null</td><td>是否可以为空</td> <td>[not ]null</td><td>是否可以为空</td>
</tr> </tr>
<tr> <tr>
<td>unique</td><td>是否是唯一</td> <td>unique或unique(uniquename)</td><td>是否是唯一,如不加括号则该字段不允许重复,如加上括号,则括号中为联合唯一的名字,此时可以有另外一个或多个字段有相同的写法</td>
</tr>
<tr>
<td>index或index(indexname)</td><td>是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时可以有另外一个或多个字段有相同的写法</td>
</tr> </tr>
<tr> <tr>
<td>extends</td><td>应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中</td> <td>extends</td><td>应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中</td>
@ -334,7 +343,7 @@ UserInfo中的成员UserName将会自动对应名为user_name的字段。
```Go ```Go
type Userinfo struct { type Userinfo struct {
Uid int `xorm:"id pk not null autoincr"` Uid int `xorm:"id pk not null autoincr"`
Username string Username string `xorm:"unique"`
Departname string Departname string
Alias string `xorm:"-"` Alias string `xorm:"-"`
Created time.Time Created time.Time

View File

@ -116,6 +116,16 @@ func (engine *Engine) Id(id int64) *Session {
return session.Id(id) return session.Id(id)
} }
func (engine *Engine) Charset(charset string) *Session {
session := engine.NewSession()
return session.Charset(charset)
}
func (engine *Engine) StoreEngine(storeEngine string) *Session {
session := engine.NewSession()
return session.StoreEngine(storeEngine)
}
func (engine *Engine) In(column string, args ...interface{}) *Session { func (engine *Engine) In(column string, args ...interface{}) *Session {
session := engine.NewSession() session := engine.NewSession()
return session.In(column, args...) return session.In(column, args...)
@ -170,7 +180,8 @@ func (engine *Engine) AutoMap(bean interface{}) *Table {
} }
func (engine *Engine) MapType(t reflect.Type) *Table { func (engine *Engine) MapType(t reflect.Type) *Table {
table := &Table{Name: engine.Mapper.Obj2Table(t.Name()), Type: t} table := &Table{Name: engine.Mapper.Obj2Table(t.Name()), Type: t,
Indexes: map[string][]string{}, Uniques: map[string][]string{}}
table.Columns = make(map[string]Column) table.Columns = make(map[string]Column)
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
@ -227,23 +238,43 @@ func (engine *Engine) MapType(t reflect.Type) *Table {
col.Length, _ = strconv.Atoi(lens) col.Length, _ = strconv.Atoi(lens)
} }
case strings.HasPrefix(k, "varchar"): case strings.HasPrefix(k, "varchar"):
col.SQLType = Varchar if k == "varchar" {
lens := k[len("varchar")+1 : len(k)-1] col.SQLType = Varchar
col.Length, _ = strconv.Atoi(lens) col.Length = Varchar.DefaultLength
col.Length2 = Varchar.DefaultLength2
} else {
col.SQLType = Varchar
lens := k[len("varchar")+1 : len(k)-1]
col.Length, _ = strconv.Atoi(lens)
}
case strings.HasPrefix(k, "decimal"): case strings.HasPrefix(k, "decimal"):
col.SQLType = Decimal col.SQLType = Decimal
lens := k[len("decimal")+1 : len(k)-1] lens := k[len("decimal")+1 : len(k)-1]
twolen := strings.Split(lens, ",") twolen := strings.Split(lens, ",")
col.Length, _ = strconv.Atoi(twolen[0]) col.Length, _ = strconv.Atoi(twolen[0])
col.Length2, _ = strconv.Atoi(twolen[1]) col.Length2, _ = strconv.Atoi(twolen[1])
case strings.HasPrefix(k, "index"):
if k == "index" {
col.IndexName = ""
col.IndexType = SINGLEINDEX
} else {
col.IndexName = k[len("index")+1 : len(k)-1]
col.IndexType = UNIONINDEX
}
case strings.HasPrefix(k, "unique"):
if k == "unique" {
col.UniqueName = ""
col.UniqueType = SINGLEUNIQUE
} else {
col.UniqueName = k[len("unique")+1 : len(k)-1]
col.UniqueType = UNIONUNIQUE
}
case k == "date": case k == "date":
col.SQLType = Date col.SQLType = Date
case k == "datetime": case k == "datetime":
col.SQLType = DateTime col.SQLType = DateTime
case k == "timestamp": case k == "timestamp":
col.SQLType = TimeStamp col.SQLType = TimeStamp
case k == "unique":
col.IsUnique = true
case k == "not": case k == "not":
default: default:
if k != col.Default { if k != col.Default {
@ -265,6 +296,28 @@ func (engine *Engine) MapType(t reflect.Type) *Table {
if col.Name == "" { if col.Name == "" {
col.Name = engine.Mapper.Obj2Table(t.Field(i).Name) col.Name = engine.Mapper.Obj2Table(t.Field(i).Name)
} }
if col.IndexType == SINGLEINDEX {
col.IndexName = col.Name
table.Indexes[col.IndexName] = []string{col.Name}
} else if col.IndexType == UNIONINDEX {
if unionIdxes, ok := table.Indexes[col.IndexName]; ok {
table.Indexes[col.IndexName] = append(unionIdxes, col.Name)
} else {
table.Indexes[col.IndexName] = []string{col.Name}
}
}
if col.UniqueType == SINGLEUNIQUE {
col.UniqueName = col.Name
table.Uniques[col.UniqueName] = []string{col.Name}
} else if col.UniqueType == UNIONUNIQUE {
if unionUniques, ok := table.Uniques[col.UniqueName]; ok {
table.Uniques[col.UniqueName] = append(unionUniques, col.Name)
} else {
table.Uniques[col.UniqueName] = []string{col.Name}
}
}
if col.IsPrimaryKey { if col.IsPrimaryKey {
table.PrimaryKey = col.Name table.PrimaryKey = col.Name
} }
@ -272,7 +325,7 @@ func (engine *Engine) MapType(t reflect.Type) *Table {
} else { } else {
sqlType := Type2SQLType(fieldType) sqlType := Type2SQLType(fieldType)
col = Column{engine.Mapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, col = Column{engine.Mapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType,
sqlType.DefaultLength, sqlType.DefaultLength2, true, "", false, false, false, TWOSIDES} sqlType.DefaultLength, sqlType.DefaultLength2, true, "", NONEUNIQUE, "", NONEINDEX, "", false, false, TWOSIDES}
if col.Name == "id" { if col.Name == "id" {
col.IsPrimaryKey = true col.IsPrimaryKey = true

View File

@ -79,6 +79,16 @@ func (session *Session) OrderBy(order string) *Session {
return session return session
} }
func (session *Session) StoreEngine(storeEngine string) *Session {
session.Statement.StoreEngine = storeEngine
return session
}
func (session *Session) Charset(charset string) *Session {
session.Statement.Charset = charset
return session
}
func (session *Session) Cascade(trueOrFalse ...bool) *Session { func (session *Session) Cascade(trueOrFalse ...bool) *Session {
if len(trueOrFalse) >= 1 { if len(trueOrFalse) >= 1 {
session.Statement.UseCascade = trueOrFalse[0] session.Statement.UseCascade = trueOrFalse[0]
@ -319,12 +329,33 @@ func (session *Session) Exec(sql string, args ...interface{}) (sql.Result, error
return session.Tx.Exec(sql, args...) return session.Tx.Exec(sql, args...)
} }
// this function create a table according a bean
func (session *Session) CreateTable(bean interface{}) error { func (session *Session) CreateTable(bean interface{}) error {
statement := session.Statement statement := session.Statement
defer statement.Init() defer statement.Init()
statement.RefTable = session.Engine.AutoMap(bean) statement.RefTable = session.Engine.AutoMap(bean)
sql := statement.genCreateSQL() sql := statement.genCreateSQL()
_, err := session.Exec(sql) res, err := session.Exec(sql)
if err != nil {
return err
}
affected, err := res.RowsAffected()
if err != nil {
return err
}
if affected > 0 {
sql = statement.genIndexSQL()
if len(sql) > 0 {
_, err = session.Exec(sql)
}
}
if err == nil && affected > 0 {
sql = statement.genUniqueSQL()
if len(sql) > 0 {
_, err = session.Exec(sql)
}
}
return err return err
} }

View File

@ -31,6 +31,9 @@ type Statement struct {
RawSQL string RawSQL string
RawParams []interface{} RawParams []interface{}
UseCascade bool UseCascade bool
UseAutoJoin bool
StoreEngine string
Charset string
BeanArgs []interface{} BeanArgs []interface{}
} }
@ -142,7 +145,7 @@ func (statement *Statement) Id(id int64) {
} }
func (statement *Statement) In(column string, args ...interface{}) { func (statement *Statement) In(column string, args ...interface{}) {
inStr := fmt.Sprintf("%v in (%v)", column, strings.Join(MakeArray("?", len(args)), ",")) inStr := fmt.Sprintf("%v IN (%v)", column, strings.Join(MakeArray("?", len(args)), ","))
if statement.WhereStr == "" { if statement.WhereStr == "" {
statement.WhereStr = inStr statement.WhereStr = inStr
statement.Params = args statement.Params = args
@ -199,9 +202,9 @@ func (statement *Statement) genColumnStr(col *Column) string {
sql += "NOT NULL " sql += "NOT NULL "
} }
if col.IsUnique { /*if col.UniqueType == SINGLEUNIQUE {
sql += "Unique " sql += "UNIQUE "
} }*/
if col.Default != "" { if col.Default != "" {
sql += "DEFAULT " + col.Default + " " sql += "DEFAULT " + col.Default + " "
@ -227,7 +230,32 @@ func (statement *Statement) genCreateSQL() string {
sql = strings.TrimSpace(sql) sql = strings.TrimSpace(sql)
sql += ", " sql += ", "
} }
sql = sql[:len(sql)-2] + ");" sql = sql[:len(sql)-2] + ")"
if statement.StoreEngine != "" {
sql += " ENGINE=" + statement.StoreEngine
}
if statement.Charset != "" {
sql += " DEFAULT CHARSET " + statement.Charset
}
sql += ";"
return sql
}
func (statement *Statement) genIndexSQL() string {
var sql string = ""
for indexName, cols := range statement.RefTable.Indexes {
sql += fmt.Sprintf("CREATE INDEX IF NOT EXISTS IDX_%v_%v ON %v (%v);", statement.TableName(), indexName,
statement.TableName(), strings.Join(cols, ","))
}
return sql
}
func (statement *Statement) genUniqueSQL() string {
var sql string = ""
for indexName, cols := range statement.RefTable.Uniques {
sql += fmt.Sprintf("CREATE UNIQUE INDEX IF NOT EXISTS UQE_%v_%v ON %v (%v);", statement.TableName(), indexName,
statement.TableName(), strings.Join(cols, ","))
}
return sql return sql
} }

View File

@ -77,6 +77,18 @@ const (
ONLYFROMDB ONLYFROMDB
) )
const (
NONEINDEX = iota
SINGLEINDEX
UNIONINDEX
)
const (
NONEUNIQUE = iota
SINGLEUNIQUE
UNIONUNIQUE
)
type Column struct { type Column struct {
Name string Name string
FieldName string FieldName string
@ -85,7 +97,10 @@ type Column struct {
Length2 int Length2 int
Nullable bool Nullable bool
Default string Default string
IsUnique bool UniqueType int
UniqueName string
IndexType int
IndexName string
IsPrimaryKey bool IsPrimaryKey bool
IsAutoIncrement bool IsAutoIncrement bool
MapType int MapType int
@ -95,6 +110,8 @@ type Table struct {
Name string Name string
Type reflect.Type Type reflect.Type
Columns map[string]Column Columns map[string]Column
Indexes map[string][]string
Uniques map[string][]string
PrimaryKey string PrimaryKey string
} }

View File

@ -23,8 +23,8 @@ CREATE TABLE `userdeatail` (
*/ */
type Userinfo struct { type Userinfo struct {
Uid int64 `xorm:"id pk not null autoincr"` Uid int64 `xorm:"id pk not null autoincr"`
Username string Username string `xorm:"unique"`
Departname string Departname string
Alias string `xorm:"-"` Alias string `xorm:"-"`
Created time.Time Created time.Time

View File

@ -17,7 +17,7 @@ import (
) )
const ( const (
version string = "0.1.6" version string = "0.1.8"
) )
func NewEngine(driverName string, dataSourceName string) (*Engine, error) { func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
@ -26,16 +26,11 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
engine.Tables = make(map[reflect.Type]*Table) engine.Tables = make(map[reflect.Type]*Table)
engine.mutex = &sync.Mutex{} engine.mutex = &sync.Mutex{}
//engine.InsertMany = true
engine.TagIdentifier = "xorm" engine.TagIdentifier = "xorm"
//engine.QuoteIdentifier = "`"
if driverName == SQLITE { if driverName == SQLITE {
engine.Dialect = &sqlite3{} engine.Dialect = &sqlite3{}
//engine.AutoIncrement = "AUTOINCREMENT"
//engine.Pool = NoneConnectPool{}
} else if driverName == MYSQL { } else if driverName == MYSQL {
engine.Dialect = &mysql{} engine.Dialect = &mysql{}
//engine.AutoIncrement = "AUTO_INCREMENT"
} else { } else {
return nil, errors.New(fmt.Sprintf("Unsupported driver name: %v", driverName)) return nil, errors.New(fmt.Sprintf("Unsupported driver name: %v", driverName))
} }