ORM eloquent /
laravel ORM 을 이용할 때 주의할 점
원하는대로 query 를 만들어주지 않는다.
아래와 같은 query 를 원했다.
SELECT *
FROM orders AS t1
JOIN user AS t2
ON t1.user_id = t2.id
JOIN order_products AS t3
ON t1.id = t3.order_id
WHERE t1.group_id IN (10)
ORDER BY t1.id
이것을 ORM 식으로 표현해보려고 아래처럼 만들었다. 여기서는 laravel(php) 을 사용했다. 그래서 orm 의 모양은 간단하게 보인다. 그런데 실질적으로 이 ORM 에서 만들어지는 query 를 살펴보면 그렇지 못하다.
// for debug
DB::enableQueryLog();
Order::whereIn('group_id', $param['ids'])
->with('user', 'groupOrderProducts.materialBarcodes') // Nested Eager Loading
->orderBy('id')
->get();
// 출력
dd(DB::getQueryLog());
물론 이것은 잘못 만든 code 라 생각한다. with 를 사용하는 것은 확실히 SELECT
를 여러번 할 것을 표현하는 것에 가깝기 때문이다. JOIN
을 할 생각이었다면, 그냥 ->join
을 걸었어야 한다.
여하튼 위의 query 를 돌리면 아래처럼 4번의 query 를 수행한다.
array:4 [
0 => array:3 [
"query" => "select * from "orders" where "group_id" in (?) and "orders"."deleted_at" is null order by "id" asc"
"bindings" => array:1 [
0 => 1
]
"time" => 0.33
]
1 => array:3 [
"query" => "select * from "users" where "users"."id" in (1)"
"bindings" => []
"time" => 0.28
]
2 => array:3 [
"query" => "select "order_products".*, "orders"."group_id" as "laravel_through_key" from "order_products" inner join "orders" on "orders"."id" = "order_products"."purchase_id" where "orders"."deleted_at" is null and "orders"."status" = ? and "order_products"."claim_id" is null and "order_products"."final_qty" > ? and "orders"."group_id" in (1, 2)"
"bindings" => array:2 [
0 => 1
1 => 0
]
"time" => 0.34
]
3 => array:3 [
"query" => "select "barcodes" from "materials" where 0 = 1"
"bindings" => []
"time" => 0.17
]
]
ORM classes
////////////////////////////////////////
class Order extends Model
{
...
public function user()
{
return $this->belongsTo(User::class);
}
public function groupOrderProducts()
{
return $this->hasManyThrough(
OrderProduct::class,
Order::class,
'group_id',
'Order_id',
'id', // $this->id
'id' // Order:class -> id
)
->where('orders.status', 1)
->whereNull('order_products.claim_id')
->where('order_products.final_qty', '>', 0);
}
}
////////////////////////////////////////
class User extends Model
{
...
public function orders()
{
return $this->hasMany(Order::class);
}
}
////////////////////////////////////////
class OrderProduct extends Model
{
...
public function materialBarcodes() {
return $this->belongsTo(Material::class)->select('barcodes');
}
}
결론, 이럴때는 안쓰는 것이 낫다.
개인적으로 query 와 가까운 ORM 을 선호한다. 이 관점에서 이야기를 하자면 이번처럼 복잡한 query 를 사용하는 경우에 orm 은 단순한 query 용도로 좋을듯 하다. 여럿이서 code 를 공유하는 경우에 ORM 으로 중복된 query 를 coding 하는 것을 잘 조절할 수는 있겠지만, 반면에 DB 의 부하는 심해질 수 있다. 그리고 사실 query 내부를 볼 수 없으니, 동작의 이해도도 떨어진다.
그래서 ORM 의 relationship 기능의 사용은 주의가 필요하다. 필자는 개인적으로 orm 의 복잡한 기능들을 좋아하지 않지만, 좀 더 간략한 query 에서는 좋은 이해를 줄 수 있고, code 의 재사용성도 높여줄 것이라고는 본다.
물론 그 이전에 ORM 의 특징중 하나인 DB Software를 옮길때의 이점도 있겠지만, 사실 이부분은 대체로 query 가 표준에 맞춰져 있는 상황에서 생각보다 큰 이점이 되지 않을 것으로 본다.
차라리 이 부분에서는 DBBuilder 정도가 어울린다고 생각한다.
댓글 없음:
댓글 쓰기