Magical CyberAll about programming

ปัญหา Hibernate, JPA ไม่สามารถ join table ที่ไม่มีความสัมพันธ์กัน

ตามหลัก ORM (Hibernate, JPA) การที่ Entity 2 ตัวจะ join กันได้จะต้องมีความสัมพันธ์ระหว่างกันเท่านั้น คือเราต้องสร้างสิ่งที่เรียกว่า Entity Graph (พวก one-to-many, many-to-one อะไรประมาณนั้น) หากต้อง Query left join ด้วย Hibernate สมมุติผมมี table

  • member เก็บข้อมูลสมาชิก
  • log เก็บข้อมูลการเข้าใช้งาน

table member

table log

โจทย์คือ 2 table ไม่มีความสัมพันธ์ต่อกัน แล้ว DBA ไม่อนุญาตให้ alter เพื่อเพิ่ม fk ด้วย ถ้าตามปกติเราสามารถ query left join ตามปกติด้วยคำสั่ง

select * from member m left join log l on m.id = l.member_id;

sql normal join

หากนำ Query นี้ไปใช้บน Hibernate, JPA จะเกิด error

Hibernate Join Example

ผมสร้าง Entity เพื่อทดสอบดังนี้

package com.magicalcyber.hellohibernate.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "member")
public class Member {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Integer id;

	@Column(length = 200)
	private String name;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Member [id=" + id + ", name=" + name + "]";
	}

}

และ

package com.magicalcyber.hellohibernate.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "log")
public class Log {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Integer id;

	@Column(name = "member_id")
	private Integer memberId;

	@Column(length = 200)
	private String msg;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public Integer getMemberId() {
		return memberId;
	}

	public void setMemberId(Integer memberId) {
		this.memberId = memberId;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	@Override
	public String toString() {
		return "Log [id=" + id + ", memberId=" + memberId + ", msg=" + msg + "]";
	}

	
}

และเมื่อผมทำการ Query ใน Hibernate ที่ต่ำกว่า 5.2.6.Final ด้วยคำสั่ง

Query query = session.createQuery("from Member m left join Log l on m.id = l.memberId");
		List list = query.list();
		if (list.isEmpty()) {
			log.info("Not found data");
		} else {
			log.info("Found data: " + list.size());
		}

ก็จะเกิด error ดังนี้
hibernate join error
แต่ก่อน Hibernate คาดหวังว่า เมื่อพบ left join มันจะต้อง join กับ Entity เท่านั้น แต่พอเจอตัวแปร member_id ที่เป็นแค่ฟิลด์ มันจึง join ต่อไม่ได้ ตามปกติแล้วใน Entity ของ Log จะต้องประกาศ @OneToMany และกำหนด join_column = “member_id”

แต่ว่าหากเปลี่ยนมาใช้ 5.1.0.Final ขึ้นไป โดยผมใช้ 5.2.10.Final และยังใช้โค้ดเก่า ก็จะไม่พบปัญหานี้แล้ว
hibernate join success

เคสนี้ยังง่าย หากเจอ @ManyToMany ที่มันต้องสร้าง Table กลางเพื่อเก็บความสัมพันธ์ของสอง Table หล่ะก็เรื่องยาวมาก เพราะเราไม่ได้ออกแบบไว้ตั้งแต่แรก (ตามโจทย์คือระบบเดิมที่เคยทำงานมานาน มันมีข้อมูลเดิมเยอะแล้ว)

ส่วนฝั่งของ JPA ก็เกิด error แบบเดียวกัน และยังไม่ได้รับการแก้ไข มีแค่วิธีแก้ไขเฉพาะหน้าเท่านั้น เช่นใช้วิธี cross join แทนหรือ native sql เป็นต้น

อ้างอิง

Comments

comments

Tags: , ,

Leave a Reply

Your email address will not be published. Required fields are marked *