데이터베이스

관계형 데이터베이스에 접근하기 위해서는 사용하고자 하는 데이터베이스 서버를 위한 샤드가 필요합니다. crystal-lang/crystal-db 패키지는 다양한 드라이버를 통합하는 API를 제공합니다.

다음 패키지는 모두 crystal-db 패키지와 호환됩니다.

이 항목에서는 crystal-db의 API와 Postgres, MySQL, SQLite의 차이로 인해 변경해 주어야 하는 SQL 명령어를 보겠습니다.

일부 드라이버에는 Postgres의 LISTENNOTIFY 같은 추가 기능이 있을 수도 있습니다.

샤드 설치

위 목록에서 적절한 드라이버를 골라 shard.yml에 샤드를 추가합니다.

crystal-lang/crystal-db를 명시적으로 추가할 필요는 없습니다.

이 항목에서는 crystal-lang/crystal-mysql을 사용합니다.

dependencies:
  mysql:
    github: crystal-lang/crystal-mysql

데이터베이스 열기

DB.open로 연결 URI를 통해 데이터베이스에 손쉽게 연결할 수 있습니다. URI의 스키마가 사용될 드라이버를 결정합니다. 다음 예제는 root 사용자로 비밀번호 blank를 가지고 test란 이름의 로컬 MySQL 데이터베이스에 연결하는 예제입니다.

require "db"
require "mysql"

DB.open "mysql://[email protected]/test" do |db|
  # ... db로 쿼리를 보냄
end

다른 연결 URI도 사용할 수 있습니다.

  • sqlite3:///path/to/data.db
  • mysql://user:[email protected]:port/database
  • postgres://server:port/database

Database#close를 호출한다는 전제 하에 블락을 받지 않는 DB.open을 사용할 수도 있습니다.

require "db"
require "mysql"

db = DB.open "mysql://[email protected]/test"
begin
  # ... db로 쿼리를 보냄
ensure
  db.close
end

실행

Database#exec으로 SQL 구문을 실행합니다.

db.exec "create table contacts (name varchar(30), age int)"

데이터를 보낼 때 인자를 넘김으로써 SQL 인젝션을 방지할 수 있습니다.

db.exec "insert into contacts values (?, ?)", "John", 30
db.exec "insert into contacts values (?, ?)", "Sarah", 33

주의: pg 드라이버를 사용할 때는 ? 대신 $1, $2 등을 사용합니다.

쿼리

쿼리를 실행하여 결과를 얻기 위해서는 Database#query를 이용하며, Database#exec에서와 같은 방식으로 인자를 사용합니다.

Database#queryResultSet를 반환하며 ResultSet은 사용된 후 연결을 닫야 합니다. Database#open과 마찬가지로 블락을 넘겨 호출한다면 ResultSet은 묵시적으로 닫힙니다.

db.query "select name, age from contacts order by age desc" do |rs|
  rs.each do
    # ... ResultSet의 각 행에 대하여 실행
  end
end

데이터베이스로부터 값을 읽을 때에는 크리스탈이 사용할 만한 타입 정보가 없습니다. 데이터베이스에서 얻을 수 있는 데이터의 타입 T를 넘겨 rs.read(T)를 호출해야 합니다.

db.query "select name, age from contacts order by age desc" do |rs|
  rs.each do
    name = rs.read(String)
    age = rs.read(Int32)
    puts "#{name} (#{age})"
    # => 영희 (33)
    # => 홍길동 (30)
  end
end

#query를 기반으로 구현된 간편한 메서드들이 다수 있습니다..

한 번에 여러 열을 읽을 수도 있습니다.

name, age = rs.read(String, Int32)

또는 한 행만을 읽을 수 있습니다.

name, age = db.query_one "select name, age from contacts order by age desc limit 1", as: { String, Int32 }

ResultSet을 명시적으로 다루지 않으면서도 스칼라 값을 읽을 수 있습니다.

max_age = db.scalar "select max(age) from contacts"

데이터베이스에서 구문을 실행하는 메서드들은 전부 DB::QueryMethods에 정의되어 있습니다.

results matching ""

    No results matching ""