Beego ORM
Beego ORMは「DjangoライクなAPIでGoアプリケーションの生産性を向上させるORM」として開発された、Go言語エコシステムで人気の高いWebフレームワークBeegoの中核コンポーネントです。DjangoやSQLAlchemyから着想を得た直感的なAPIデザインにより、Code Firstアプローチでデータベース操作を簡素化。MySQL、PostgreSQL、SQLite3をサポートし、型安全性を保ちながら複雑なクエリ操作やリレーションシップ管理を効率的に実現します。
GitHub概要
beego/beego
beego is an open-source, high-performance web framework for the Go programming language.
トピックス
スター履歴
ライブラリ
Beego ORM
概要
Beego ORMは「DjangoライクなAPIでGoアプリケーションの生産性を向上させるORM」として開発された、Go言語エコシステムで人気の高いWebフレームワークBeegoの中核コンポーネントです。DjangoやSQLAlchemyから着想を得た直感的なAPIデザインにより、Code Firstアプローチでデータベース操作を簡素化。MySQL、PostgreSQL、SQLite3をサポートし、型安全性を保ちながら複雑なクエリ操作やリレーションシップ管理を効率的に実現します。
詳細
Beego ORM 2025年版はGo言語での企業レベルWebアプリケーション開発において、データベース層の標準ソリューションとして確立されています。構造体ベースのモデル定義、自動テーブル作成、クエリジェネレーション、トランザクション管理などの包括的な機能を提供。Goの型システムと密接に統合されているため、コンパイル時の型チェックによりランタイムエラーを最小化。リフレクションとコード生成を活用してGoの構造体を自動的にデータベーステーブルにマッピングし、保守性の高いデータアクセス層を構築できます。
主な特徴
- Code Firstアプローチ: Go構造体の定義からデータベーステーブルを自動生成
- 豊富なデータベースサポート: MySQL、PostgreSQL、SQLite3の幅広い対応
- 型安全なクエリ: Goの型システムを活用した安全なデータベース操作
- 自動Join機能: 関連テーブルの自動的な結合クエリ生成
- Raw SQLサポート: 複雑なクエリに対応するプレーンSQL実行機能
- トランザクション管理: 自動コミット・ロールバック機能付きトランザクション
メリット・デメリット
メリット
- Goの標準的な型システムとの優れた統合による高い型安全性
- DjangoライクなAPIによる学習コストの低減と開発効率の向上
- 自動テーブル作成機能による迅速なプロトタイピングとスキーマ管理
- 豊富なクエリビルダー機能による柔軟なデータベース操作
- Beegoフレームワークとの完全統合による一貫した開発体験
- リフレクションベースの自動マッピングによる保守性の向上
デメリット
- 大規模データ処理時のリフレクションオーバーヘッドによるパフォーマンス制約
- Goの慣習的なエラーハンドリングとは異なるORM固有のエラー処理
- 複雑なSQLクエリの表現に限界がありRaw SQLが必要な場合がある
- モデル構造の変更時にスキーマ移行の手動管理が必要
- データベース固有の最適化機能へのアクセスが制限される
- Beegoフレームワーク以外での使用時の統合コストが高い
参考ページ
書き方の例
基本セットアップ
package main
import (
"github.com/beego/beego/v2/client/orm"
_ "github.com/go-sql-driver/mysql" // MySQL ドライバー
_ "github.com/lib/pq" // PostgreSQL ドライバー
_ "github.com/mattn/go-sqlite3" // SQLite3 ドライバー
)
func init() {
// データベース登録
orm.RegisterDataBase("default", "mysql",
"user:password@tcp(localhost:3306)/database?charset=utf8mb4&parseTime=True&loc=Local", 30)
// PostgreSQLの場合
// orm.RegisterDataBase("default", "postgres",
// "host=localhost port=5432 user=postgres password=password dbname=mydb sslmode=disable")
// SQLite3の場合
// orm.RegisterDataBase("default", "sqlite3", "path/to/database.db")
// モデル登録
orm.RegisterModel(new(User), new(Profile), new(Post))
// テーブル自動作成(開発時のみ)
orm.RunSyncdb("default", false, true)
}
func main() {
// ORM インスタンス取得
o := orm.NewOrm()
// 基本的な使用例
user := &User{Name: "John Doe", Email: "[email protected]"}
id, err := o.Insert(user)
if err != nil {
panic(err)
}
fmt.Printf("User created with ID: %d\n", id)
}
モデル定義と基本操作
package models
import (
"time"
"github.com/beego/beego/v2/client/orm"
)
// ユーザーモデル
type User struct {
Id int `orm:"auto"` // 自動インクリメント主キー
Name string `orm:"size(100)"` // 文字列長制限
Email string `orm:"unique"` // ユニーク制約
Age int `orm:"null"` // NULL許可
IsActive bool `orm:"default(true)"` // デフォルト値
Created time.Time `orm:"auto_now_add;type(datetime)"` // 作成時自動設定
Updated time.Time `orm:"auto_now;type(datetime)"` // 更新時自動設定
// リレーションシップ
Profile *Profile `orm:"rel(one)"` // 1対1関係
Posts []*Post `orm:"reverse(many)"` // 1対多関係(逆参照)
}
// プロフィールモデル
type Profile struct {
Id int `orm:"auto"`
Bio string `orm:"type(text);null"`
User *User `orm:"reverse(one)"` // 逆参照1対1
}
// 投稿モデル
type Post struct {
Id int `orm:"auto"`
Title string `orm:"size(200)"`
Content string `orm:"type(text)"`
Published bool `orm:"default(false)"`
Created time.Time `orm:"auto_now_add;type(datetime)"`
User *User `orm:"rel(fk)"` // 外部キー
}
// テーブル名カスタマイズ
func (u *User) TableName() string {
return "users"
}
// CRUD操作の実装
func CreateUser(name, email string, age int) (*User, error) {
o := orm.NewOrm()
user := &User{
Name: name,
Email: email,
Age: age,
}
// Insert操作
id, err := o.Insert(user)
if err != nil {
return nil, err
}
user.Id = int(id)
return user, nil
}
func GetUserByID(id int) (*User, error) {
o := orm.NewOrm()
user := &User{Id: id}
err := o.Read(user)
if err != nil {
return nil, err
}
return user, nil
}
func GetUserByEmail(email string) (*User, error) {
o := orm.NewOrm()
user := &User{}
err := o.QueryTable("user").Filter("email", email).One(user)
if err != nil {
return nil, err
}
return user, nil
}
func UpdateUser(user *User) error {
o := orm.NewOrm()
_, err := o.Update(user)
return err
}
func DeleteUser(id int) error {
o := orm.NewOrm()
user := &User{Id: id}
_, err := o.Delete(user)
return err
}
// バリデーション(カスタム実装)
func (u *User) Valid() error {
if len(u.Name) < 2 {
return fmt.Errorf("name must be at least 2 characters")
}
if !strings.Contains(u.Email, "@") {
return fmt.Errorf("invalid email format")
}
if u.Age < 0 || u.Age > 150 {
return fmt.Errorf("age must be between 0 and 150")
}
return nil
}
高度なクエリ操作
package services
import (
"fmt"
"github.com/beego/beego/v2/client/orm"
)
// 複雑なクエリ操作
func GetUsersWithConditions(minAge, maxAge int, namePattern string) ([]*User, error) {
o := orm.NewOrm()
var users []*User
qs := o.QueryTable("user")
// 年齢範囲でフィルタ
if minAge > 0 {
qs = qs.Filter("age__gte", minAge)
}
if maxAge > 0 {
qs = qs.Filter("age__lte", maxAge)
}
// 名前の部分一致
if namePattern != "" {
qs = qs.Filter("name__icontains", namePattern)
}
// アクティブユーザーのみ
qs = qs.Filter("is_active", true)
// ソート
qs = qs.OrderBy("-created")
// 結果取得
_, err := qs.All(&users)
if err != nil {
return nil, err
}
return users, nil
}
// 集約クエリ
func GetUserStatistics() (map[string]interface{}, error) {
o := orm.NewOrm()
stats := make(map[string]interface{})
// 総ユーザー数
totalUsers, err := o.QueryTable("user").Count()
if err != nil {
return nil, err
}
stats["total_users"] = totalUsers
// アクティブユーザー数
activeUsers, err := o.QueryTable("user").Filter("is_active", true).Count()
if err != nil {
return nil, err
}
stats["active_users"] = activeUsers
// 平均年齢(Raw SQLを使用)
var avgAge float64
err = o.Raw("SELECT AVG(age) FROM users WHERE age > 0").QueryRow(&avgAge)
if err != nil {
return nil, err
}
stats["average_age"] = avgAge
return stats, nil
}
// リレーション込みの取得
func GetUserWithProfile(userID int) (*User, error) {
o := orm.NewOrm()
user := &User{Id: userID}
err := o.Read(user)
if err != nil {
return nil, err
}
// プロフィール情報を読み込み
_, err = o.LoadRelated(user, "Profile")
if err != nil {
return nil, err
}
return user, nil
}
func GetUsersWithPosts() ([]*User, error) {
o := orm.NewOrm()
var users []*User
// ユーザーと投稿を一緒に取得
_, err := o.QueryTable("user").RelatedSel().All(&users)
if err != nil {
return nil, err
}
// 各ユーザーの投稿を読み込み
for _, user := range users {
_, err = o.LoadRelated(user, "Posts")
if err != nil {
return nil, err
}
}
return users, nil
}
// 条件付きアップデート
func UpdateUsersStatus(condition map[string]interface{}, status bool) (int64, error) {
o := orm.NewOrm()
qs := o.QueryTable("user")
// 動的条件適用
for key, value := range condition {
qs = qs.Filter(key, value)
}
// 一括更新
return qs.Update(orm.Params{
"is_active": status,
"updated": time.Now(),
})
}
// ページネーション
func GetUsersPaginated(page, pageSize int) ([]*User, int64, error) {
o := orm.NewOrm()
var users []*User
qs := o.QueryTable("user").Filter("is_active", true).OrderBy("-created")
// 総件数取得
total, err := qs.Count()
if err != nil {
return nil, 0, err
}
// ページネーション適用
offset := (page - 1) * pageSize
_, err = qs.Limit(pageSize, offset).All(&users)
if err != nil {
return nil, 0, err
}
return users, total, nil
}
リレーション操作
// 1対1関係の操作
func CreateUserWithProfile(name, email, bio string) (*User, error) {
o := orm.NewOrm()
// トランザクション開始
err := o.Begin()
if err != nil {
return nil, err
}
// ユーザー作成
user := &User{
Name: name,
Email: email,
}
id, err := o.Insert(user)
if err != nil {
o.Rollback()
return nil, err
}
user.Id = int(id)
// プロフィール作成
profile := &Profile{
Bio: bio,
User: user,
}
_, err = o.Insert(profile)
if err != nil {
o.Rollback()
return nil, err
}
// コミット
err = o.Commit()
if err != nil {
return nil, err
}
user.Profile = profile
return user, nil
}
// 1対多関係の操作
func CreatePostForUser(userID int, title, content string) (*Post, error) {
o := orm.NewOrm()
// ユーザー存在確認
user := &User{Id: userID}
err := o.Read(user)
if err != nil {
return nil, fmt.Errorf("user not found: %v", err)
}
// 投稿作成
post := &Post{
Title: title,
Content: content,
User: user,
}
id, err := o.Insert(post)
if err != nil {
return nil, err
}
post.Id = int(id)
return post, nil
}
// Join操作
func GetPostsWithUserInfo() ([]map[string]interface{}, error) {
o := orm.NewOrm()
var maps []orm.Params
// Join クエリ
_, err := o.Raw(`
SELECT p.id, p.title, p.content, p.published, p.created,
u.name as user_name, u.email as user_email
FROM posts p
INNER JOIN users u ON p.user_id = u.id
WHERE p.published = ?
ORDER BY p.created DESC
`, true).Values(&maps)
if err != nil {
return nil, err
}
// map[string]interface{} に変換
results := make([]map[string]interface{}, len(maps))
for i, m := range maps {
results[i] = make(map[string]interface{})
for k, v := range m {
results[i][k] = v
}
}
return results, nil
}
実用例
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/server/web"
)
// コントローラー実装
type UserController struct {
web.Controller
}
// GET /users - ユーザー一覧取得
func (c *UserController) GetAll() {
page, _ := strconv.Atoi(c.GetString("page", "1"))
pageSize, _ := strconv.Atoi(c.GetString("page_size", "10"))
users, total, err := GetUsersPaginated(page, pageSize)
if err != nil {
c.Data["json"] = map[string]interface{}{
"error": err.Error(),
}
c.Ctx.ResponseWriter.WriteHeader(http.StatusInternalServerError)
c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"users": users,
"total": total,
"page": page,
"page_size": pageSize,
}
c.ServeJSON()
}
// GET /users/:id - ユーザー詳細取得
func (c *UserController) Get() {
idStr := c.Ctx.Input.Param(":id")
id, err := strconv.Atoi(idStr)
if err != nil {
c.Data["json"] = map[string]interface{}{
"error": "Invalid user ID",
}
c.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
c.ServeJSON()
return
}
user, err := GetUserWithProfile(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"error": "User not found",
}
c.Ctx.ResponseWriter.WriteHeader(http.StatusNotFound)
c.ServeJSON()
return
}
c.Data["json"] = user
c.ServeJSON()
}
// POST /users - ユーザー作成
func (c *UserController) Post() {
var userRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
Bio string `json:"bio"`
}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &userRequest)
if err != nil {
c.Data["json"] = map[string]interface{}{
"error": "Invalid JSON format",
}
c.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
c.ServeJSON()
return
}
// バリデーション
if userRequest.Name == "" || userRequest.Email == "" {
c.Data["json"] = map[string]interface{}{
"error": "Name and email are required",
}
c.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
c.ServeJSON()
return
}
// ユーザー作成
user, err := CreateUserWithProfile(userRequest.Name, userRequest.Email, userRequest.Bio)
if err != nil {
c.Data["json"] = map[string]interface{}{
"error": err.Error(),
}
c.Ctx.ResponseWriter.WriteHeader(http.StatusInternalServerError)
c.ServeJSON()
return
}
c.Data["json"] = user
c.Ctx.ResponseWriter.WriteHeader(http.StatusCreated)
c.ServeJSON()
}
// データベース初期化
func InitDatabase() {
// 開発環境でのテストデータ作成
o := orm.NewOrm()
// サンプルユーザー作成
users := []*User{
{Name: "Alice Johnson", Email: "[email protected]", Age: 28},
{Name: "Bob Smith", Email: "[email protected]", Age: 35},
{Name: "Carol Brown", Email: "[email protected]", Age: 22},
}
for _, user := range users {
_, err := o.Insert(user)
if err != nil {
log.Printf("Failed to create user %s: %v", user.Name, err)
} else {
log.Printf("Created user: %s", user.Name)
}
}
}
// メイン関数
func main() {
// ルーティング設定
web.Router("/users", &UserController{}, "get:GetAll;post:Post")
web.Router("/users/:id", &UserController{}, "get:Get")
// データベース初期化
InitDatabase()
log.Println("Server starting on :8080")
web.Run(":8080")
}