데이터 베이스 애플리케이션 개발은 C나 PHP에서 직접 mysql, postgresql API를 호출하는 방식으로 해왔기 때문에, ORM(Object-relational mapping)은 나에게 생소하다. 예컨데 나는 PHP나 C에서 persistence layer를 처리하는 Model-1 방식만을 경험한 셈이다. ORM은 데이터베이스 Model-3로 iBatis, Hibernat와 같은 persistence 담당하는 프레임워크를 이용해서 데이터 베이스를 처리하는 방식이다.
sequel은 루비언어를 위한 데이터베이스 툴킷으로 ORM 레이어를 포함하고 있다. 전공분야는 아니지만 이왕 sinatra를 하게 된거, 배워두면 쓸모가 있겠다 싶어서 간단히 살펴보기로 했다.
Sequel의 기능들
Sequel은 thread safe 하며, connection pooling과 SQL 쿼리, 테이블 스키마를 위한 간결한 DSL을 제공한다.
Sequel은 루비 객체에 레코드를 맵핑하고, 관련 레코드를 처리하기 위한 완전한 ORM 레이어를 가지고 있다.
Sequel은 prepared statement, bound variables, stored procedures, 트랜잭션 isolation, savepoint, slave/master 설정, 데이터베이스 sharding과 같은 진보된 데이터베이스 기능을 지원한다.
Sequel은 ADO, Amalgalite, CUBRID, DataObjects, DB2, DBI, Firebird, IBM_DB, Infomix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle, postgrSQL, SQLite3, Swift[[FootNote(Object storage의 그 swift인가? 한번 확인해 봐야 겠다.), TinyTDS를 지원한다.
ORM의 장점과 단점
장점
생산성 향상 : 반복적인 (그리고 익숙하지 않은)sql문을 사용할 필요가 없다.
개발시간 단축
특정 DBMS 벤더에 종속되지 않는다. 일반적으로 ORM은 DBMS 벤더에 종속적이지 않다.
단점
ORM을 새로 배워야 한다.
실제 코드가 하는 일이 무언지 이해하지 못할 수 있다. 개발자는 SQL을 사용할 때 보다, 프로그램 코드에 대한 제어권을 잃어 버리게 된다.
require 'rubygems'
require 'sequel'
DB = Sequel.sqlite # memory database
DB.create_table :items do
primary_key :id
String :name
Float :price
end
items = DB[:items] # Create a dataset
# Populate the table
items.insert(:name => 'abc', :price => rand * 100)
items.insert(:name => 'def', :price => rand * 100)
items.insert(:name => 'ghi', :price => rand * 100)
# Print out the number of records
puts "Item count: #{items.count}"
# Print out the average price
puts "The average price is: #{items.avg(:price)}"
sequel 사용
Cheat sheet
기본적인 사용법 위주로 살펴본다. 테스트를 위해서 mysql과 sqlite3를 사용했다. 테스트를 위해서 mysql과 sqlite gem을 설치하자.
SQLite는 메모리에 데이터베이스를 올릴 수 있다. 빠르긴 하지만 persistency하지 않다. 연결을 끊으면, 메모리의 데이터도 날아가 버린다. 매개변수없이 만들면, 메모리에 데이터베이스를 올릴 수 있다.
DB = Sequel.sqlite
RAW SQL 구문 사용
DB = Sequel.connect('mysql://root:password@127.0.0.1/test')
DB.run "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT NOT NULL)
DB.run "INSERT INTO users VALUES ('yundream', '30')"
dataset = DB[:users]
dataset.each do | r |
puts r
end
dataset.all # => [{...}, {...}, ...]
dataset.first # => {...}
Filtering
같은 값 찾기
dataset = DB[:users]
# SELECT * FROM users WHERE name = 'yundream'
dataset.where(:name => 'yundream').each do |row|
puts "#{row[:name]}: #{row[:age]}"
end
크거나 작은 값 찾기
dataset.where{age > 35}.each do | row |
puts "#{row[:name]}: #{row[:age]}"
end
포함하는 값 찾기
# SELECT * FROM users WHERE age >= 35 and age <= 40
dataset.where(:age => 35..40).each do | row |
puts "#{row[:name]}: #{row[:age]}"
end
# SELECT * FROM users WHERE age = 45 or age = 43
dataset.where(:age => [45, 43]).each do | row |
puts "#{row[:name]}: #{row[:age]}"
end
dataset.where(:id=>other_dataset.select(:other_id))
Ordering
# SELECT * FROM users ORDER BY name
dataset.order(:name).each do | row |
puts row[:name]
end
# SELECT * FROM users ORDER BY name DESC
dataset.reverse_order(:name).each do | row |
puts row[:name]
end
Limit / Offset
# SELECT * FROM users WHERE age >= 35 and age <= 40 limit 10
dataset.where(:age => 35..40).limit(10).each do | row |
puts "#{row[:name]}: #{row[:age]}"
end
# SELECT * FROM users WHERE age >= 35 and age <= 40 limit 10,5
dataset.where(:age => 35..40).limit(10,5).each do | row |
puts "#{row[:name]}: #{row[:age]}"
end
Join
Join 예제를 위해 2개의 테이블을 만들었다.
Employee 테이블
LastName
DepartmentID
Rafferty
31
Jones
33
Steinberg
33
Robinson
34
Smith
34
John
NULL
Department 테이블
31
영업부
33
기술부
34
사무부
35
마케팅
INNER Join을 수행했다.
DB = Sequel.connect('mysql://root:password@127.0.0.1/test')
# SELECT * FROM `employee` INNER JOIN `department`
# ON (`department`.`DepartmentID` = `employee`.`DepartmentID`);
dataset = DB[:employee].join(:department, :DepartmentID => :DepartmentID)
dataset.each do | row |
puts "#{row[:LastName]} : #{row[:DepartmentName]}"
end
# 결과
# afferty : 영업부
# Jones : 기술부
# Steinberg : 기술부
# Robinson : 사무부
# Smith : 사무부
# UPDATE users SET age = age + 1;
dataset = DB[:users]
dataset.update(:age => :age + 1)
# DELETE FROM users WHERE name = 'yundream'
dataset.where(:name => 'yundream').delete
Schema Manipulation
DB.create_table :categories do
primary_key :id
String :name
end
DB.create_table :mytest do
primary_key :id
String :name, :unique => true, :null => false
TrueClass :active, :default => true
foreign_key :category_id, :categories
DateTime :created_at
index :created_at
end
DB.drop_table mytest
DB.create_table :address do
String :zipcode
enum :system, :elements => ['mac', 'linux', 'windows']
end
Aliasing
# SELECT name AS user_name FROM users
dataset.select(Sequel.as(:name, :user_name)).each do |row|
pp row
end
Transactions
DB.transaction do
dataset.insert(:name => 'sang.yun', :age => 35)
dataset.insert(:name => 'kong.kim', :age => 34)
end
# 두개 모두 insert되거나 두개 모두 실패하한다. (All or nothing)
DB.transaction do
raise "some error occurred"
end # 에러가 발생하면 롤백
Contents
sequel에 대하여
Sequel의 기능들
ORM의 장점과 단점
지원 데이터베이스
설치
예제 - 1
sequel 사용
Cheat sheet
데이터베이스 열기
SQLite 인 메모리 데이터베이스
RAW SQL 구문 사용
Insert rows
Retrieve rows
Filtering
같은 값 찾기
크거나 작은 값 찾기
포함하는 값 찾기
Ordering
Limit / Offset
Join
Aggregate functions methods
Update/Delete
Schema Manipulation
Aliasing
Transactions
참고
Recent Posts
Archive Posts
Tags