1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

データベース初心者向け:PrismaとSQLAlchemy+Alembicの完全比較ガイド

Posted at

データベース初心者向け:PrismaとSQLAlchemy+Alembicの完全比較ガイド 🗄️

こんにちは!データベースとORMについて学び始めた方向けに、PrismaとSQLAlchemy+Alembicの違いを分かりやすく説明します。

目次

  1. ORMって何?なぜ必要なの?
  2. データベースマイグレーションとは
  3. Prisma入門
  4. SQLAlchemy + Alembic入門
  5. 徹底比較:コード例で見る違い
  6. どちらを選ぶべき?決断のガイド
  7. はじめてのセットアップガイド

ORMって何?なぜ必要なの?

簡単な例え話

ORMを理解するために、レストランの例で考えてみましょう。

データベース = キッチン(料理を作る場所)
アプリケーション = お客様(料理を食べたい人)
ORM = ウェイター(お客様とキッチンの間を取り持つ人)

お客様(アプリケーション)が「ハンバーガーください」と言ったとき、ウェイター(ORM)が「厨房に、牛肉パティとバンとレタスを組み合わせて作ってください」と適切な指示を出してくれます。

ORMがない場合(生のSQL)

-- ユーザー作成
INSERT INTO users (name, email, created_at) 
VALUES ('田中太郎', 'tanaka@example.com', NOW());

-- ユーザー検索
SELECT * FROM users WHERE email = 'tanaka@example.com';

-- ユーザー情報更新
UPDATE users SET name = '田中花子' WHERE id = 1;

ORMがある場合

Prisma (TypeScript):

// ユーザー作成
const user = await prisma.user.create({
  data: {
    name: '田中太郎',
    email: 'tanaka@example.com'
  }
});

// ユーザー検索
const user = await prisma.user.findFirst({
  where: { email: 'tanaka@example.com' }
});

// ユーザー情報更新
const updatedUser = await prisma.user.update({
  where: { id: 1 },
  data: { name: '田中花子' }
});

SQLAlchemy (Python):

# ユーザー作成
user = User(name='田中太郎', email='tanaka@example.com')
session.add(user)
session.commit()

# ユーザー検索
user = session.query(User).filter(User.email == 'tanaka@example.com').first()

# ユーザー情報更新
user.name = '田中花子'
session.commit()

データベースマイグレーションとは

引っ越しの例で理解しよう

マイグレーションは「引っ越し」のようなものです。

例:ユーザーテーブルに年齢カラムを追加

Before(マイグレーション前):

id name email
1 田中太郎 tanaka@example.com

After(マイグレーション後):

id name email age
1 田中太郎 tanaka@example.com null

Prisma入門

Prisma は TypeScript/JavaScript 用のモダンなORMです。

特徴

  • 型安全性(タイプセーフ)
  • 直感的なAPI
  • 強力な開発ツール
  • 自動補完が優秀

Prisma Schemaの例

// schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  name  String
  email String  @unique
  posts Post[]
  
  @@map("users")
}

model Post {
  id      Int    @id @default(autoincrement())
  title   String
  content String
  userId  Int
  user    User   @relation(fields: [userId], references: [id])
  
  @@map("posts")
}

Prismaの基本操作

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Create(作成)
const user = await prisma.user.create({
  data: {
    name: '佐藤次郎',
    email: 'sato@example.com',
    posts: {
      create: [
        { title: '初めての投稿', content: 'よろしくお願いします!' }
      ]
    }
  }
});

// Read(読み込み)
const users = await prisma.user.findMany({
  include: {
    posts: true
  }
});

// Update(更新)
const updatedUser = await prisma.user.update({
  where: { id: 1 },
  data: { name: '佐藤花子' }
});

// Delete(削除)
await prisma.user.delete({
  where: { id: 1 }
});

Prismaのマイグレーション

# 開発環境でスキーマを変更
npx prisma db push

# マイグレーションファイル作成
npx prisma migrate dev --name add_age_to_users

# 本番環境にデプロイ
npx prisma migrate deploy

SQLAlchemy + Alembic入門

SQLAlchemy は Python の老舗で強力なORMです。Alembicはマイグレーションツールです。

特徴

  • 非常に柔軟で高機能
  • Pythonエコシステムとの親和性が高い
  • 学習コストは高めだが、慣れると強力
  • Core(低レベル)とORM(高レベル)の両方をサポート

SQLAlchemyのモデル定義

# models.py
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String, nullable=False)
    email = Column(String, unique=True, nullable=False)
    
    # リレーション
    posts = relationship("Post", back_populates="user")

class Post(Base):
    __tablename__ = 'posts'
    
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String, nullable=False)
    content = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))
    
    # リレーション
    user = relationship("User", back_populates="posts")

SQLAlchemyの基本操作

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# データベース接続設定
engine = create_engine('postgresql://user:password@localhost/dbname')
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# 使用例
def create_user_with_post():
    db = SessionLocal()
    
    try:
        # Create(作成)
        user = User(name='鈴木一郎', email='suzuki@example.com')
        db.add(user)
        db.flush()  # idを取得するため
        
        post = Post(title='SQLAlchemyの投稿', content='こんにちは!', user_id=user.id)
        db.add(post)
        db.commit()
        
        # Read(読み込み)
        users = db.query(User).join(Post).all()
        
        # Update(更新)
        user.name = '鈴木花子'
        db.commit()
        
        # Delete(削除)
        db.delete(user)
        db.commit()
        
    except Exception as e:
        db.rollback()
        raise
    finally:
        db.close()

Alembicのマイグレーション

# alembic/versions/001_add_age_to_users.py
from alembic import op
import sqlalchemy as sa

def upgrade():
    """年齢カラムを追加"""
    op.add_column('users', sa.Column('age', sa.Integer, nullable=True))

def downgrade():
    """年齢カラムを削除"""
    op.drop_column('users', 'age')
# マイグレーション作成
alembic revision --autogenerate -m "add age to users"

# マイグレーション実行
alembic upgrade head

# 一つ前の状態に戻す
alembic downgrade -1

徹底比較:コード例で見る違い

1. セットアップの簡単さ

Prisma:

# 1. Prismaをインストール
npm install prisma @prisma/client

# 2. 初期化
npx prisma init

# 3. スキーマ定義してマイグレーション
npx prisma db push

SQLAlchemy + Alembic:

# 1. パッケージをインストール
pip install sqlalchemy alembic psycopg2-binary

# 2. Alembic初期化
alembic init alembic

# 3. 設定ファイル編集
# 4. モデル定義
# 5. マイグレーション作成・実行
alembic revision --autogenerate -m "initial"
alembic upgrade head

2. データの関連付け(リレーション)

同じ操作:ユーザーと投稿を一緒に作成

Prisma:

const userWithPosts = await prisma.user.create({
  data: {
    name: '山田太郎',
    email: 'yamada@example.com',
    posts: {
      create: [
        { title: '投稿1', content: '内容1' },
        { title: '投稿2', content: '内容2' }
      ]
    }
  },
  include: {
    posts: true
  }
});

SQLAlchemy:

user = User(name='山田太郎', email='yamada@example.com')
post1 = Post(title='投稿1', content='内容1')
post2 = Post(title='投稿2', content='内容2')

user.posts = [post1, post2]

db.add(user)
db.commit()
db.refresh(user)

3. 複雑なクエリ

同じ操作:投稿数が3つ以上のユーザーを取得

Prisma:

const activeUsers = await prisma.user.findMany({
  where: {
    posts: {
      some: {}  // 投稿がある
    }
  },
  include: {
    _count: {
      select: { posts: true }
    }
  }
});

// より複雑な場合は生のクエリも使用可能
const result = await prisma.$queryRaw`
  SELECT u.*, COUNT(p.id) as post_count 
  FROM users u 
  LEFT JOIN posts p ON u.id = p.user_id 
  GROUP BY u.id 
  HAVING COUNT(p.id) >= 3
`;

SQLAlchemy:

from sqlalchemy import func

# ORM方式
active_users = db.query(User).join(Post)\
    .group_by(User.id)\
    .having(func.count(Post.id) >= 3)\
    .all()

# Core方式(より低レベル)
from sqlalchemy import select, text

stmt = select(User).join(Post)\
    .group_by(User.id)\
    .having(func.count(Post.id) >= 3)

result = db.execute(stmt).scalars().all()

# 生のSQL
result = db.execute(text("""
    SELECT u.*, COUNT(p.id) as post_count 
    FROM users u 
    LEFT JOIN posts p ON u.id = p.user_id 
    GROUP BY u.id 
    HAVING COUNT(p.id) >= 3
""")).fetchall()

4. トランザクション処理

Prisma:

// 自動トランザクション
const result = await prisma.$transaction([
  prisma.user.create({
    data: { name: '取引者1', email: 'trader1@example.com' }
  }),
  prisma.user.create({
    data: { name: '取引者2', email: 'trader2@example.com' }
  })
]);

// 手動トランザクション
const result = await prisma.$transaction(async (tx) => {
  const user1 = await tx.user.create({
    data: { name: '取引者1', email: 'trader1@example.com' }
  });
  
  const user2 = await tx.user.create({
    data: { name: '取引者2', email: 'trader2@example.com' }
  });
  
  return { user1, user2 };
});

SQLAlchemy:

# 基本的なトランザクション
try:
    user1 = User(name='取引者1', email='trader1@example.com')
    user2 = User(name='取引者2', email='trader2@example.com')
    
    db.add(user1)
    db.add(user2)
    db.commit()
except Exception:
    db.rollback()
    raise

# コンテキストマネージャー使用
from contextlib import contextmanager

@contextmanager
def transaction(db):
    try:
        yield db
        db.commit()
    except Exception:
        db.rollback()
        raise

with transaction(db) as tx:
    user1 = User(name='取引者1', email='trader1@example.com')
    user2 = User(name='取引者2', email='trader2@example.com')
    tx.add(user1)
    tx.add(user2)

どちらを選ぶべき?決断のガイド

選択フローチャート

詳細な比較表

項目 Prisma SQLAlchemy + Alembic
学習コスト 🟢 低い 🟡 中〜高い
開発スピード 🟢 高い 🟡 中程度
型安全性 🟢 完全 🟡 部分的(mypy使用時)
柔軟性 🟡 中程度 🟢 非常に高い
パフォーマンス 🟡 良い 🟢 優秀
コミュニティ 🟡 成長中 🟢 成熟
ドキュメント 🟢 優秀 🟢 豊富
デバッグ 🟢 簡単 🟡 中程度

使い分けの指針

Prismaを選ぶべき場合:

  • TypeScript/JavaScriptプロジェクト
  • スタートアップや小〜中規模チーム
  • 開発スピードを重視
  • 型安全性を重視
  • データベース設計がシンプル〜中程度

SQLAlchemy + Alembicを選ぶべき場合:

  • Pythonプロジェクト
  • 複雑なデータベース操作が必要
  • 既存のPythonエコシステムとの統合
  • 最高のパフォーマンスが必要
  • 経験豊富な開発チーム

実際のプロジェクト例:

Project A(Eコマースサイト)

  • 規模:中小企業
  • 技術:Next.js + TypeScript
  • 選択:Prisma
  • 理由:型安全性とスキーマの可視化が重要

Project B(データ分析プラットフォーム)

  • 規模:大企業
  • 技術:FastAPI + Python
  • 選択:SQLAlchemy + Alembic
  • 理由:複雑なクエリとPandasとの連携が必要

はじめてのセットアップガイド

Prismaのセットアップ

1. プロジェクト作成

mkdir my-prisma-project
cd my-prisma-project
npm init -y
npm install prisma @prisma/client typescript ts-node @types/node

2. Prisma初期化

npx prisma init

3. スキーマ定義(schema.prisma)

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  name  String
  email String  @unique
  posts Post[]
}

model Post {
  id      Int    @id @default(autoincrement())
  title   String
  content String
  userId  Int
  user    User   @relation(fields: [userId], references: [id])
}

4. 環境変数設定(.env)

DATABASE_URL="postgresql://username:password@localhost:5432/mydb"

5. データベース作成とクライアント生成

npx prisma db push
npx prisma generate

6. 基本的な使用例(main.ts)

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
  // ユーザー作成
  const user = await prisma.user.create({
    data: {
      name: 'テストユーザー',
      email: 'test@example.com'
    }
  });

  console.log('作成されたユーザー:', user);

  // 全ユーザー取得
  const users = await prisma.user.findMany();
  console.log('全ユーザー:', users);
}

main()
  .catch((e) => console.error(e))
  .finally(async () => {
    await prisma.$disconnect();
  });

SQLAlchemy + Alembicのセットアップ

1. プロジェクト作成

mkdir my-sqlalchemy-project
cd my-sqlalchemy-project
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

2. パッケージインストール

pip install sqlalchemy alembic psycopg2-binary python-dotenv

3. プロジェクト構造

my-sqlalchemy-project/
├── alembic/
├── models.py
├── database.py
├── main.py
├── alembic.ini
└── .env

4. データベース接続設定(database.py)

import os
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from dotenv import load_dotenv

load_dotenv()

DATABASE_URL = os.getenv("DATABASE_URL")

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

5. モデル定義(models.py)

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from database import Base

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, nullable=False)
    email = Column(String, unique=True, index=True, nullable=False)
    
    posts = relationship("Post", back_populates="user")

class Post(Base):
    __tablename__ = "posts"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, nullable=False)
    content = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey("users.id"))
    
    user = relationship("User", back_populates="posts")

6. Alembic初期化

alembic init alembic

7. alembic.ini設定

[alembic]
sqlalchemy.url = postgresql://username:password@localhost:5432/mydb

8. env.py設定(alembic/env.py)

from alembic import context
from sqlalchemy import engine_from_config, pool
from models import Base

target_metadata = Base.metadata

# 以下は既存のコードを使用...

9. マイグレーション作成・実行

alembic revision --autogenerate -m "初期テーブル作成"
alembic upgrade head

10. 基本的な使用例(main.py)

from sqlalchemy.orm import Session
from database import engine, SessionLocal
from models import Base, User, Post

# テーブル作成
Base.metadata.create_all(bind=engine)

def create_user_example():
    db = SessionLocal()
    try:
        # ユーザー作成
        user = User(name="テストユーザー", email="test@example.com")
        db.add(user)
        db.commit()
        db.refresh(user)
        
        print(f"作成されたユーザー: {user.id}, {user.name}")
        
        # 全ユーザー取得
        users = db.query(User).all()
        print("全ユーザー:")
        for u in users:
            print(f"  {u.id}: {u.name} ({u.email})")
            
    except Exception as e:
        db.rollback()
        print(f"エラー: {e}")
    finally:
        db.close()

if __name__ == "__main__":
    create_user_example()

まとめ

PrismaとSQLAlchemy + Alembicは、どちらも優秀なORMですが、それぞれに特徴があります:

Prisma の強み

  • 🚀 学習コストが低い - 初心者でも短時間で習得可能
  • 🔒 完全な型安全性 - TypeScriptとの親和性が抜群
  • 🛠️ 優秀な開発ツール - Prisma Studioなどが使いやすい
  • 📈 高い開発効率 - 直感的なAPIで素早く開発

SQLAlchemy + Alembic の強み

  • 🎯 極めて柔軟 - 複雑な要件にも対応可能
  • 高性能 - 最適化されたクエリが書ける
  • 🐍 Pythonエコシステム - 他のライブラリとの統合が容易
  • 📚 豊富な機能 - 長年の実績による安定性

どちらを選んでも大丈夫!

重要なのは、プロジェクトの要件とチームのスキルに合わせて選択することです。

  • 迷ったらPrisma:TypeScript/JavaScriptを使っていて、学習コストを抑えたい場合
  • 迷ったらSQLAlchemy:Pythonを使っていて、柔軟性を重視する場合

どちらを選んでも、モダンなアプリケーション開発に必要な機能は十分にカバーされています。まずは小さなプロジェクトで試してみて、慣れてから大きなプロジェクトに適用することをお勧めします!


この記事が、あなたのORM選択の参考になれば幸いです。質問やフィードバックがありましたら、お気軽にコメントください! 🚀

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?