Original author, public number [programmer reading], welcome to pay attention to the public number, reprint the article please indicate the source oh.

When developing an application, we usually design the data table and then use the development language to create the corresponding data model. However, today we will talk about the reverse process, that is, how to define the data model of the GORM framework, and then implement the application written by the GROM framework. Use the defined data model to create corresponding data tables in the database.

So we need to talk about how to define the GORM data model.

The model definition

In general, we say GROM model definition, refers to the definition represents a data table structure (struct), then we can use GROM frame structures can be mapped to the corresponding relational database tables, the data in the table or query data to populate the structure, as shown below, we define a structure called the Post.

type Post struct {
    PostId    int
    Uid       int
    Title     string
    Content   string
    Type      int
    CreatedAt time.Time
    UpdatedAt time.Time
}
Copy the code

Creating a structure is just the first step, but before you know how to create a table, we should first understand the mapping rules between the structure and the table, mainly have the following points:

Struct tags

As we know, the constructs of the Go language support tags to extend additional information for each field of the structure. For example, tags can be used to extend additional information when encoding JSON using standard library Encoding/JSON package.

The GROM framework has its own tags convention, which looks like this:

Column Specifies the Column name. Type Specifies the Column data Type. Size Specifies the Column Size. DEFAULT value 255 PRIMARY_KEY Specifies the column as the primary key UNIQUE specifies the column as the UNIQUE DEFAULT Specifies the column DEFAULT value PRECISION Specifies the column PRECISION NOT NULL Specifies the column as non-null AUTO_INCREMENT Specifies whether the column is an autoincrement type INDEX Creates an INDEX with or without a name. If multiple indexes have the same name, a composite INDEX UNIQUE_INDEX is created. EMBEDDED sets the structure to EMBEDDED_PREFIX sets the prefix of the EMBEDDED structure - ignore this fieldCopy the code

GROM also supports tags conventions for associated tables, which I’ll talk about when I get a chance to talk about GROM table associations.

In the following code, we can customize the tags extension to the Post structure:

type Post struct {
    PostId    int    `gorm:"primary_key; auto_increment"`
    Uid       int    `gorm:"type:int; not null"`
    Title     string `gorm:"type:varchar(255); not null"`
    Content   string `gorm:"type:text; not null"`
    Type      uint8  `gorm:"type:tinyint; default 1; not null"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt time.Time
}
Copy the code

From the above example, we can see that GORM defines the tags format for the fields of the data model. Each field can have multiple types of tags information, separated by semicolons.

practice

In addition to tags, which define mapping rules between fields, Go has its own set of conventions, or conventions, for mapping structures to relational tables. The main points are as follows:

A primary key

In GROM convention, the ID field in the data model is mapped to the primary key of the data table, such as TestModel defined below,ID is the primary key, TestModel ID of the data type is string, if the ID of the data type is int, GROM will also set AUTO_INCREMENT, Use ID to become the auto-increment primary key.

type TestModel struct{
    ID   int
    Name string
}
Copy the code

Of course, we can also customize the name of the primary key field. In the Post structure above, we set the PostId field as the primary key. If we define another field as the primary key, then the GROM framework will not treat the ID field as the primary key, even if the ID field is still in the structure.

type Post struct {
    ID        int
    PostId    int    `gorm:"primary_key; auto_increment"`
    Uid       int    `gorm:"type:int; not null"`
    Title     string `gorm:"type:varchar(255); not null"`
    Content   string `gorm:"type:text; not null"`
    Type      uint8  `gorm:"type:tinyint; default 1; not null"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt time.Time
}
Copy the code

So we added an ID field to the Post structure, and PostId is still the primary key. Here’s what we get when we use the DESC posts statement in the data:

Data table mapping rules

When we create a table using a structure, the default name of the table is lowercase plural. For example, the name of the table for Post is posts. We can also specify the table name for the structure instead of using the default.

Add the TableName() method to the structure, which returns a custom TableName like this:

My_posts func (p Post) TableName() string{return "my_posts"
}
Copy the code
Table prefix

In addition to the specified data table name, we can also rewrite gorm. DefaultTableNameHandler this variable, so that you can specify unity for all of the data table data table prefix, as follows:

gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string  {
    return "tb_" + defaultTableName;
}
Copy the code

In this case, the data table created by the structure Post is named TB_posts.

Field mapping rule

Structure-to-table name mapping rules are plural of struct names, and the default mapping rules for struct field-to-table fields are to delimit each uppercase word with an underscore, as follows:

type Prize struct {
	ID int
	PrizeName string
}
Copy the code

The table corresponding to the PrizeName field in the structure Prize above is prize_name, but when we change PrizeName to PrizeName, the corresponding table field name is PrizeName, which is because only words at the beginning of the uppercase field are separated.

Of course, we can also define tags extension information for a structure field, so that the mapping rules between structure fields and data table fields are defined in tags.

Time point tracking

Famous in front, as we have said, if the structure is called the ID field, GORM framework will take the field as the primary key of the data table, in addition, if the structure of CreatedAt, UpdatedAt, DeletedAt this a few fields, GROM framework also will make some special processing, the rules are as follows:

CreatedAt: This field is automatically written when a new table record is added. UpdatedAt: This field is automatically updated when a data table record is updated. DeletedAt: When soft delete is performed, this field is automatically updated, indicating the deletion timeCopy the code
gorm.Model

Because if you have any ID structure, CreatedAt, UpdatedAt, DeletedAt this a few more general fields, GORM framework will automatically handle these fields, so if we need this a few field structure, we can directly in the custom structure embedded in the GORM. The Model structure, The structure of gorm.Model is as follows:

type Model struct {
    ID        uint `gorm:"primary_key"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt *time.Time `sql:"index"`}Copy the code

So, if we embed gorm.model in the structure Prize, as follows:

type Prize struct{
    gorm.Model
    Name string
}
Copy the code

In this case, the structure Prize contains five fields.

Database Migration

When we talk about database migration, we use GROM to provide a series of methods, according to the rules defined by the data model, to create and delete data tables, namely DDL operations of the database.

GORM provides methods for DDL operations on databases, including the following categories:

Data table operation

Func (s *DB) AutoMigrate(values... Func (s *DB) CreateTable(models... Func (s *DB) DropTable(values... Interface {}) *DB // equivalent to drop tableifFunc (s *DB) DropTableIfExists(values... Interface {}) *DB func (s *DB) HasTable(value interface{}) boolCopy the code

The column operation

Func (s *DB) DropColumn(column String) *DB func (s *DB) ModifyColumn(column string, typ string) *DBCopy the code

The index operation

Func (s *DB) AddForeignKey(Field String, dest String, onDelete String, Func (s *DB) AddIndex(indexName String, columns... Func (s *DB) AddUniqueIndex(indexName String, columns... string) *DBCopy the code

Data migration simple code example

Note that the db variable in the sample program below represents the gorm.db object, which was initialized in my previous article.

typeUsername string Password String Email String Phone String} funcmain(){post.automigrate (&post {}, &user {}); // Create post.automigrate (&post {}, &user {});"gorm:table_options"."ENGINE=InnoDB"DropTable(&post {}).droptable (&post {},"users"DropTableIfExists(&post {},"users"Mysql > select * from posts where users exists; mysql > delete from posts where users existsif db.HasTable("users") {
        db.DropTable("users"Db.model (&post {}).dropcolumn ("id"Db.model (&post {}).modifyColumn ("id"."varchar(255)"Db.model (&post {}).addForeignKey ("uid"."users(id)"."RESTRICT"."RESTRICT"Db.model (&post {}).addIndex ();"index_title"."title"Db.model (&user {}).addUniqueIndex (db.model (&user {}).addUniqueIndex ("index_phone"."phone")}Copy the code

summary

You may ask, directly in the database to create tables, delete operations, etc. Why do this in an application? The database migration capabilities provided by the GROM framework come in handy in situations where we may not be able to log into the database system or where we need to develop an application that can manage the database.


Your attention is the biggest encouragement on my writing road!