mingru
v0.63.0
Published
SQL Generator for mingru-models
Downloads
22
Readme
mingru (WIP)
Convert mingru-models to Go code.
All APIs are subject to change before 1.0.0
Goals:
- No performance penalty at runtime, mingru is an SQL builder, not an ORM
- Strongly typed models, models are defined in TypeScript not Go
- Currently focuses on Go and MySQL/MariaDB
NOTE: The documentation below is outdated!!! We are working on a brand new documentation site. Stay tuned.
A quick example
Step 1: Define models
Models are defined in mingru-models. Let's create a simple user model user.ts
:
// ----------- User table model (user.ts) -----------
import * as mm from 'mingru-models';
class User extends mm.Table {
id = mm.pk();
name = mm.varChar(100);
sig = mm.text().nullable;
age = mm.int();
}
export default mm.table(User);
Step 2: Define actions
Create another file (userTA.ts
) for table actions and import the user table (user.ts
) we just defined above:
// ----------- User table actions (userTA.ts) -----------
import * as mm from 'mingru-models';
import user from './user';
export class UserTA extends mm.TableActions {
// Select a user by ID.
selectUser = mm.selectRow(user.id, user.name, user.sig).byID();
// Select all users and order by their names.
selectAllUsers = mm.selectRows(user.id, user.name, user.sig).orderByAsc(user.name);
// Select a single user signature field by ID.
selectUserSig = mm.selectField(user.sig).byID();
// Update a user by ID.
updateUser = mm.updateOne().setParams(user.name, user.sig).byID();
// Update all user signatures to an empty string.
updateAllSigToEmpty = mm.unsafeUpdateAll().set(user.sig, mm.sql`''`);
// Delete a user by ID.
deleteByID = mm.deleteOne().byID();
// Delete all users by a specified name.
deleteByName = mm.deleteSome().whereSQL(user.name.isEqualToParam());
// Delete all users.
deleteAll = mm.unsafeDeleteAll();
// Insert a new user.
insertUser = mm
.insertOne()
.set(user.sig, mm.sql`'Default signature!'`)
.setParams(user.name, user.age);
}
export default mm.tableActions(user, UserTA);
Step 3: Generate Go Code
Install mingru-tsconfig
:
yarn add mingru-tsconfig -D
Set extends
to mingru-tsconfig
in your local tsconfig.json
:
{
"extends": "mingru-tsconfig"
}
Create a mingru.ts
file, which will start the build process to generate Go and SQL code.
import * as mr from 'mingru';
// Import table actions.
import userTA from './userTA';
// Import tables if you need to generate CREATE TABLE SQL files.
import user from './user';
(async () => {
const dialect = new mr.MySQL();
// Build Go code to '../da/` directory.
const builder = new mr.Builder(dialect, './data_access_layer/', {
// Clean build directory on each build.
cleanBuild: true,
});
const actions = [userTA];
const tables = [user];
// Start the build process by calling the `build` method.
await builder.buildAsync(async () => {
// Generate Go source files.
await builder.buildActionsAsync(actions);
// Generate CREATE TABLE SQL files.
await builder.buildCreateTableSQLFilesAsync(tables);
});
})();
It's also recommended to use ts-node
and add a build command to package.json
scripts section:
{
"scripts": {
"build": "ts-node mingru.ts"
}
}
Now you can build your project using yarn build
.
Below is the code generated by mingru:
/******************************************************************************************
* This file was automatically generated by mingru (https://github.com/mgenware/mingru)
* Do not edit this file manually, your changes will be overwritten.
******************************************************************************************/
package da
import "github.com/mgenware/mingru-go-lib"
// TableTypeUser ...
type UserAGType struct {
}
// User ...
var User = &UserAGType{}
// ------------ Actions ------------
// DeleteAll ...
func (mrTable *UserAGType) DeleteAll(mrQueryable mingru.Queryable) (int, error) {
result, err := mrQueryable.Exec("DELETE FROM `user`")
return mingru.GetRowsAffectedIntWithError(result, err)
}
// DeleteByID ...
func (mrTable *UserAGType) DeleteByID(mrQueryable mingru.Queryable, id uint64) error {
result, err := mrQueryable.Exec("DELETE FROM `user` WHERE `id` = ?", id)
return mingru.CheckOneRowAffectedWithError(result, err)
}
// DeleteByName ...
func (mrTable *UserAGType) DeleteByName(mrQueryable mingru.Queryable, name string) (int, error) {
result, err := mrQueryable.Exec("DELETE FROM `user` WHERE `name` = ?", name)
return mingru.GetRowsAffectedIntWithError(result, err)
}
// InsertUser ...
func (mrTable *UserAGType) InsertUser(mrQueryable mingru.Queryable, name string, age int) (uint64, error) {
result, err := mrQueryable.Exec("INSERT INTO `user` (`sig`, `name`, `age`) VALUES ('Default signature!', ?, ?)", name, age)
return mingru.GetLastInsertIDUint64WithError(result, err)
}
// UserTableSelectAllUsersResult ...
type UserTableSelectAllUsersResult struct {
ID uint64
Name string
Sig *string
}
// SelectAllUsers ...
func (mrTable *UserAGType) SelectAllUsers(mrQueryable mingru.Queryable) ([]*UserTableSelectAllUsersResult, error) {
rows, err := mrQueryable.Query("SELECT `id`, `name`, `sig` FROM `user` ORDER BY `name`")
if err != nil {
return nil, err
}
result := make([]*UserTableSelectAllUsersResult, 0)
defer rows.Close()
for rows.Next() {
item := &UserTableSelectAllUsersResult{}
err = rows.Scan(&item.ID, &item.Name, &item.Sig)
if err != nil {
return nil, err
}
result = append(result, item)
}
err = rows.Err()
if err != nil {
return nil, err
}
return result, nil
}
// UserTableSelectUserResult ...
type UserTableSelectUserResult struct {
ID uint64
Name string
Sig *string
}
// SelectUser ...
func (mrTable *UserAGType) SelectUser(mrQueryable mingru.Queryable, id uint64) (*UserTableSelectUserResult, error) {
result := &UserTableSelectUserResult{}
err := mrQueryable.QueryRow("SELECT `id`, `name`, `sig` FROM `user` WHERE `id` = ?", id).Scan(&result.ID, &result.Name, &result.Sig)
if err != nil {
return nil, err
}
return result, nil
}
// SelectUserSig ...
func (mrTable *UserAGType) SelectUserSig(mrQueryable mingru.Queryable, id uint64) (*string, error) {
var result *string
err := mrQueryable.QueryRow("SELECT `sig` FROM `user` WHERE `id` = ?", id).Scan(&result)
if err != nil {
return result, err
}
return result, nil
}
// UpdateAllSigToEmpty ...
func (mrTable *UserAGType) UpdateAllSigToEmpty(mrQueryable mingru.Queryable) (int, error) {
result, err := mrQueryable.Exec("UPDATE `user` SET `sig` = ''")
return mingru.GetRowsAffectedIntWithError(result, err)
}
// UpdateUser ...
func (mrTable *UserAGType) UpdateUser(mrQueryable mingru.Queryable, id uint64, name string, sig *string) error {
result, err := mrQueryable.Exec("UPDATE `user` SET `name` = ?, `sig` = ? WHERE `id` = ?", name, sig, id)
return mingru.CheckOneRowAffectedWithError(result, err)
}
CREATE TABLE `user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`sig` TEXT NULL DEFAULT NULL,
`age` INT NOT NULL,
PRIMARY KEY (`id`)
)
CHARACTER SET=utf8mb4
COLLATE=utf8mb4_unicode_ci
;
Step 4: Use the generated code in your Go project
func main() {
// Open a DB connection on localhost.
db, err := sql.Open("mysql", "root:123456@/test")
if err != nil {
panic(err)
}
// Select all user profiles.
users, err := da.User.SelectAllUserProfiles(db)
if err != nil {
panic(err)
}
// Loop through the result.
for _, user := range users {
fmt.Printf("ID: %v, Name: %v, Sig: %v\n", user.ID, user.Name, user.Sig)
}
}
More examples
For a more detailed and runnable example, visit mingru-go-example
Advanced Topics
Default Values
MySQL doesn't allow you to use a non-constant value as a default value for a column because CREATE TABLE
doesn't allow it. mingru supports arbitrary default values for both CREATE
and UPDATE
actions by simply passing default values into generated SQL.
Pagination
limit
Pagination can be achieved by calling limit
following a call to selectRows
:
selectUsersWithLimit = mm.selectRows(user.id, user.name).limit();
Implementations should expose arguments to set the underlying SQL LIMIT
and OFFSET
values, here is the Go method signature generated by mingru from the action above:
func (mrTable *UserAGType) SelectUsersWithLimit(mrQueryable mingru.Queryable, limit int, offset int, max int) ([]*SelectUsersWithLimitResult, int, error)
selectPage
Pagination can also be done via selectPage
method, selectPage
usually generates a method built upon the SQL LIMIT
and OFFSET
clauses but exposes higher level arguments thus provides more convenience:
selectPagedUsers = mm.selectPage(user.id, user.name);
mingru converts the action above to the following Go func:
func (mrTable *UserAGType) SelectPagedUsers(mrQueryable mingru.Queryable, page int, pageSize int) ([]*SelectPagedUsersResult, bool, error)
Notice the limit
and offset
arguments are gone, page
and pageSize
are exposed instead. Also the second return value changed from rowsFetched
(int
) to hasNextPage
(bool
).