Comments (21)
If the field type is struct or pointer to struct, it is has_one; If the field type is slice, map or pointer to slice or map, it is has _many. So I think the two tags could be merged to one tag.
from xorm.
there is still difference for has_one and belongs_to, check Active Record pattern http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference, as it's the basis for my suggestion
from xorm.
omitting 'has_many' seem a valid suggestion though
from xorm.
I'd like to see some support for many-to-many relationships too.
from xorm.
I want to use xorm cascade loading like below:
type Country struct {
Id int64
Name string
}
type Group struct {
Id int64
Name string
}
type UserGroup struct {
UserId int64 `xorm:"index"`
GroupId int64 `xorm:"index"`
}
type UserFamily struct {
UserId int64 `xorm:"index"`
FamilyId int64 `xorm:"index"`
}
type Device struct {
Id int64
UserId int64 `xorm:"index"`
Name string
}
type User struct {
Id int64
Name string
Country Country `xorm:"cascade(country_id)"` // one to one
Father User `xorm:"cascade(father_id)"` //one to one
Devices []Device `xorm:"cascade(user_id)"` // one to many
Families []User `xorm:"cascade(user_family, user_id, family_id)"` // many to many
Groups []Group `xorm:"cascade(user_group, user_id, group_id)"` // many to many
}
from xorm.
This sounds good.
type Device struct {
Id int64
User User xorm:"index"
Name string
}
Wouldn't it make sense to reference back the User object instead of the UserId?
from xorm.
Of course, both yours and the below are ok.
type Device struct {
Id int64
User User `xorm:"index cascade(user_id)"`
Name string
}
from xorm.
cascade tag wording is like customer mapper for the FK field, but it's missing the owning issue, the meaning of "belongs_to" is the owning rights so it also meaning if owner got deleted the
type Order struct {
Customer Customer `xorm:"belongs_to"` // customer_id FK association, and customer_id as default mapper
}
type Customer struct {
Order []Order // has many required default lazy loaded, need an API for lazy loading
}
meaning that a customer got deleted all its orders will be deleted too.
from xorm.
has_one is also required as there are situation where FK association is placed on the owner in most one-to-one situation
type Nose struct {
Face Face // default face_id mapper
}
type Face struct {
Nose Nose `xorm:"has_one"`// FK lookup from inverse table (Nose)
}
and still, delete face will also delete nose
from xorm.
has_one is very useful indeed.
Shouldn't we be able to specify if the CASCADE should delete? I'm coming from python to quite used to sqlalchemy and think they do it quite well. http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html#cascades.
Realise you may not be heading the same way, but worth just checking.
from xorm.
@ahall I suppose Active Record by its infamous RoR framework is providing better example/pattern?
from xorm.
@ahall just read sqlalchemy the cascade served the save/update/delete behaviour, which is exactly the same usage from using grails, and grails is following active record pattern plus cascade mapping behaviour to override default behaviour, from my experience I've only find that "delete-orphan" mostly used.
from xorm.
Yep I'm only suggesting by default it wont cascade the deletes and you can use e.g. a cascade tag to specify if you want it to delete on cascade. sqlalchemy my default wont cascade the deletes unless you tell it to as you dont always want it to delete on cascade.
from xorm.
This would be extra nice to have. Has anyone started working on it?
from xorm.
I think currently no one is working on it. I think it should be discussed more.
from xorm.
Basic ORM preconditions:
- All association domain models is using ‘Id int64’ as its PK
One-to-One:
type Nose struct {
Id int64
TheFace *Face `xorm:"belongs_to(the_face_id)”` // optional, use belongs_to if you wanna mapping different column name
}
type Face struct {
Id int64
TheNose *Nose `xorm:”has_one(the_face_id)”`
}
// 'belongs_to' clause usage:
// belongs_to([mapping column name] [, <mapping table name>])
// 'has_one' clause usage:
// has_one([mapping column name] [, <mapping table name>])
create table face (id bigint generated by default as identity (start with 1),
primary key (id));
create table nose (id bigint generated by default as identity (start with 1),
the_face_id bigint not null,
primary key (id));
alter table nose add constraint fk_nose_the_face_id foreign key (the_face_id) references face (id);
implementation notes:
- XORM will ALWAYS using JOIN fetch for One-to-One with 1 depth level
- cascade save:
face := &Face{ TheNose:Nose{} }
engine.Insert(&face) // insert both nose and face record with association made
- cascade delete, using DB feature, i.e., ‘ON DELETE CASCADE’ clause in mysql?
- cascade save and delete only works on owning side, i.e., has_one
One-to-Many:
type Order struct {
Id int64
TheCustomer *Customer `xorm:"belongs_to(the_customer_id)”` // optional, use belongs_to if you wanna mapping different column name
}
type Customer struct {
Id int64
TheOrders []*Order `xorm:”has_many(the_customer_id)"`
}
// 'has_many' clause usage:
// has_many([mapping column name] [, <mapping table name>])
create table customer (id bigint generated by default as identity (start with 1),
primary key (id));
create table order (id bigint generated by default as identity (start with 1),
the_customer_id bigint not null,
primary key (id));
alter table order add constraint fk_order_the_customer_id foreign key (the_customer_id) reference customer (id);
Many-to-Many:
(yet supported)
Using has_many fetching strategy:
lazy:
type Customer struct {
Id int64
TheOrders []*Order `xorm:”has_many(the_customer_id)”` // default will be lazy
}
// 'lazy' clause usage:
// lazy // default behavior works with declaring it, and with no limit max fetch size and no table ordering
// lazy(<max fetch size:int>[, <order by fields:string>])
eager_select:
type Customer struct {
Id int64
TheOrders []*Order `xorm:”has_many(the_customer_id) eager_select”`
}
// 'eager_select' clause usage:
// eager_select // default with no limit max fetch size and no table ordering
// eager_select(<max fetch size:int>[,<order by fields:string>])
eager_join:
type Customer struct {
Id int64
TheOrders []*Order `xorm:”has_many(the_customer_id) eager_join”`
}
// 'eager_join' clause usage:
// eager_join // default with no table ordering
// eager_join(<order by fields:string>)
Adding and Removing associations:
Only work on the inverse side of domain object for removing and adding associations:
Removing association:
// retrieved var order1 Order
order1.TheCustomer = nil
engine.Update(&order1)
Adding association:
// retrieved var order1 Order
order1.TheCustomer = differentCustomer
engine.Update(&order1)
// insert new record
order := Order{ TheCustomer:customer1 }
engine.Insert(&order)
from xorm.
Perfect work!!!
And some rules could be considered:
- Cascade tag field MUST be pointer to struct, struct is not allowed.
- Many to Many need a default joint-table name, and user could specify one.
type Customer struct {
Id int64
TheOrders []*Order `xorm:”has_many(joint-table, the_customer_id, the_order_id)”` // default will be lazy
}
type Order struct {
Id int64
TheCustomers []*Order `xorm:”has_many(joint-table, the_order_id, the_customer_id)”` // default will be lazy
}
// 'lazy' clause usage:
// lazy // default behavior works with declaring it, and with no limit max fetch size and no table ordering
// lazy(<max fetch size:int>[, <order by fields:string>])
- For Many to Many, lazy load need a method to manually load the data.
engine.LazyLoad(&orders) // this will a object or a slice of object
from xorm.
I've updated has_many clause usage above:
// 'has_many' clause usage:
// has_many([mapping column name] [, <mapping table name>])
Which is a conflicted design to above comments.
extended to:
has_many([mapping column name] [, <mapping table name> [, <many-to-many join table inverse mapping column name> ] ])
so you above example can be:
type CustomerOrder struct {
CustomerId int64
OrderId int64
}
type Customer struct {
Id int64
TheOrders []*Order `xorm:”has_many(customer_id, customer_order, order_id)”`
}
type Order struct {
Id int64
TheCustomers []*Order `xorm:”has_many(order_id, customer_order, customer_id)”`
}
And with above design, it also means that for many-to-many declaration, that all 3 params are needed.
@lunny any thought on adding/removing many-to-many associations without adding new APIs?
from xorm.
For Many to Many, lazy load need a method to manually load the data, we have missed multiple has_many issue:
engine.LazyLoad(&orders) // this will a object or a slice of object
consider following a domain has multiple has_many:
type Flight struct {
Id int64
DepartureAirport Airport
DestinationAirport Airport
}
type Airport struct {
Id int64
InboundFlights []*Flight `xorm:”has_many(destination_airport_id, flight)”`
OutboundFlights []*Flight `xorm:”has_many(departure_airport_id, flight)”`
}
engine.LazyLoad(&airPort, "InboundFlights")
from xorm.
Sounds good to me. Shame there is no way of auto lazyload due to language restrictions.
from xorm.
How to add or remove associations simply. It's currently diffculty.
from xorm.
Related Issues (20)
- sync2不支持同步mysql中MEMORY引擎的数据表V0.7.0
- Invalid object name 'SYS.INDEXES' on SQL Server case sensitive collate HOT 1
- CTE (Common Table Expression) with aliases support
- 事务无法执行afterClosure HOT 1
- go mod download get stuck at xorm.io/core HOT 2
- lost table name with session when insert multiple Slice
- xorm mock CURD HOT 2
- [xorm.NewEngine] I gave wrong 'dataSourceName' but can't panic the error HOT 2
- 文档的问题 HOT 1
- SQLite to mssql dump
- undefined: builder.StringBuilder HOT 3
- Oracle Dialect Quote Brackets HOT 1
- Mysql split-table HOT 2
- driver.Valuer not checked on Insert() HOT 1
- Add support for vertica
- Avoid varargs for batch operations HOT 1
- go fmt go file warning HOT 1
- how to use force index when construct session HOT 2
- mysql5.7版本中json类型的数据插入空数组,数据为null
- how to open session autoclose flag with xorm.engine HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from xorm.