ถ้าจะว่าไปมันก็คือการ Map จาก Java Class => Database Table นั่นเองครับ คือเราไม่จำเป็นต้องเขียนคำสั่ง SQL เพื่อ Create Table เอง เราแค่เขียน java class ขึ้นมา จากนั้น framework มันก็จะจัดการแปลงไปเป็น table ให้เราเองโดยอัตโนมัติ เวลาที่เราดึงค่ามาจาก database มันก็จะแปลงจาก row มาเป็น object ให้เราเอง อาจจะเรียกว่าการทำ ORM หรือ Object Relational Mapping นั่นเองครับ เรามาดูวิธีการสร้างกันเลยดีกว่า
หลักการสร้าง
Entity Class (EclipseLink)
- เป็น java class ตั้งชื่อว่าอะไรก็ได้ (ให้สื่อกับความหมายนั้นๆ)
- implements Serializable
- มี annotation @Entiry อยู่บน class
- Entity Class ต้องมี default constructor ที่ไม่มี parameter เท่านั้น เช่น public Category(){ }
- ในกรณีที่ต้องการกำหนดค่าบางอย่างของ Table ให้ใช้ annotation @Table เช่นการกำหนดชื่อให้ table จะใช้ attribute name แต่ถ้าไม่ได้กำหนดชื่อ default table name จะเป็นชื่อของ entity class นั้นๆ การตั้งชื่อ table ให้ระวังเรื่อง reserve word ของ database แต่ละเจ้า
- กำหนด attribute
- จะต้องมี attribute อย่างน้อย 1 attribute ที่มี annotation @Id เพื่อบอกว่าเป็น primary key
- มี method getter และ setter เพื่อ get และ set ค่าให้กับแต่ละ attribute
- ในการกำหนดค่าแต่ละ attribute หรือแต่ละ column ให้ใช้ annotation @Colum อยู่บน attribute นั้นๆ
- การ map relationshipให้สร้าง attribute ที่มี data type เป็น Entity Class ที่ต้องการ map ด้วย จากนั้นก็ใส่ annotation Mapping ต่างๆ ไว้บน attribute นั้นๆ เช่น
- @OneToOne
- @OneToMany
- @ManyToOne
- @ManyToMany
ตัวอย่างที่ 1 ยังไม่มี foreign key หรือการ Map ใดๆ ทั้งสิ้น
1. เขียน java class ขึ้นมา สมมติผมจะสร้างเป็น Entity UserForum (Map to Table UserForum)
UserForum.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Temporal; import javax.persistence.Version; /** * * @author Redcrow */ @Table(name = "USER_FORUM") //เป็นการตั้งชื่อ table ว่าให้ชื่อ USER_FORUM @Entity //เป็นการประกาศว่า java class นี้เป็น entity class เพื่อให้ framework มาอ่านข้อมูลของ class นี้ไปสร้างเป็น table public class UserForum implements Serializable { @Id //ระบุว่า field นี้เป็น primary key //@TableGenerator เป็นการระบุว่าให้ database ทำการสร้าง id หรือ primary key ให้เองโดยอัตโนมัติ โดย ให้ squence id นั้นเก็บไว้ที่ table ORPHANDRG_SEQ ที่มีค่าของ column NAME เท่ากับ USER_FORUM @TableGenerator(name = "UserForum_GEN", table = "ORPHANDRG_SEQ", pkColumnName = "NAME", valueColumnName = "VALUE", pkColumnValue = "USER_FORUM") @GeneratedValue(generator = "UserForum_GEN", strategy = GenerationType.TABLE) @Column(name = "USER_FORUM_ID") //กำหนดให้ column นี้มีชื่อว่า USER_FORUM_ID ถ้าไม่กำหนดมันจะเป็น ID ให้เองโดยอัตโนมัติ private Integer id; @Version //เป็นการกำหนด version ของ row เพื่อป้องกันปัญหา optimistic locking ที่อาจจะเกิดขึ้น optimistic locking คือปัญหาการ update ข้อมูลเก่าทับข้อมูลที่ใหม่กว่า ซึ่งมันไม่ควรที่จะเกิดขึ้น private Integer version; //information @Column(unique=true) //คือการกำหนดว่าให้ column นี้เป็น unique มันก็คือ SQL unique นั่นเอง private String userName; @Column(length=30, nullable=false) //กำหนดความยาวตัวอักษรเป็น 30 ตัวอักษร และห้ามเป็น null private String fullName; //ในกรณีนี้ถ้าไม่ได้ใส่ @Column ชื่อ column นี้จะเป็น FULLNAME เองโดยอัตโนมัติ private String email; private String image; @Temporal(javax.persistence.TemporalType.TIMESTAMP) //เป็นการ map ให้ java date => time stamp ของ database private Date joinDate; private Boolean enable = false; private Boolean hidden = false; public UserForum() { } public UserForum(Integer id) { this.id = id; } //getter and setter method @Override public String toString() { return fullName; } //hash code กับ equal ใช้สำหรับการ compare object @Override public int hashCode() { int hash = 3; hash = 29 * hash + Objects.hashCode(this.id); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final UserForum other = (UserForum) obj; if (!Objects.equals(this.id, other.id)) { return false; } return true; } }ตัวอย่างที่ 2 การ Map One to One
ตัวอย่างนี้ ผมขอใช้ Entity Class ใหม่น่ะครับ
Staff.java
package com.na5cent.blogspot.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import org.eclipse.persistence.annotations.ReadOnly; /** * * @author Redcrow */ @Entity @ReadOnly //เป็นการกำหนดว่า Entity นี้หรือ table นี้สามารถอ่านข้อมูลได้เพียงอย่างเดียวเท่านั้น @Table(name = "STAFFS") public class Staff implements Serializable { @Id @Column(name = "STAFF_ID") private String staffId; @Version private Integer version; private String staffName; private String account; private String password; ... ... ... }User.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Version; /** * * @author Redcrow */ @Entity @Table(name = "USERS") public class User implements Serializable { @Id @Column(name = "USER_ID") @GeneratedValue(strategy = GenerationType.AUTO) //Generate Auto ตัวนี้ใช้ในการ test เท่านั้น private Integer id; @Version private Integer version; @OneToOne //อันนี้คือ foreign key ครับ ซึ่งชี้ไปที่ primay key ของ table STAFFS ซึ่งก็คือ STAFF_ID นั่นเอง private Staff staff; private String name; private String userName; private String userType; //getter and setter method @Override public int hashCode() { int hash = 7; hash = 59 * hash + (this.id != null ? this.id.hashCode() : 0); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final User other = (User) obj; if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) { return false; } return true; } }ตัวอย่างที่ 3 การ Map One To Many หรือ Many To One
Post.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Temporal; import javax.persistence.Version; import org.apache.commons.lang3.StringUtils; /** * * @author Redcrow */ @Table(name = "POST_FORUM") @Entity public class Post implements Serializable { @Id @TableGenerator(name = "Post_GEN", table = "ORPHANDRG_SEQ", pkColumnName = "NAME", valueColumnName = "VALUE", pkColumnValue = "POST_FORUM") @GeneratedValue(generator = "Post_GEN", strategy = GenerationType.TABLE) @Column(name = "POST_ID") private Integer id; @Version private Integer version; //information private String title; @Lob //ฟิลด์นี้ คือการเก็บข้อมูลเป็น Large Object Binary private String content; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date postDate; @ManyToOne //เป็น foreign key ที่ชี้ไปที่ primary key ของ table CATEGORY ซึ่งก็คือ CATEGORY_ID private Category category; @OneToMany(mappedBy = "post")//เป็น bidirectional mapping ที่ map กลับไปยัง table COMMENT_FORUM ฟิลด์ COMMENT_ID private List<Comment> comments; //getter and setter method ... ... ... }Comment.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Temporal; import javax.persistence.Version; import org.apache.commons.lang.StringUtils; /** * * @author Redcrow */ @Table(name = "COMMENT_FORUM") @Entity public class Comment implements Serializable { @Id @TableGenerator(name = "Comment_GEN", table = "ORPHANDRG_SEQ", pkColumnName = "NAME", valueColumnName = "VALUE", pkColumnValue = "COMMENT_FORUM") @GeneratedValue(generator = "Comment_GEN", strategy = GenerationType.TABLE) @Column(name = "COMMENT_ID") private Integer id; @Version private Integer version; //information @Lob private String content; @ManyToOne //เป็น foreign key ที่ชี้ไปที่ primary key ของ table POST_FORUM ซึ่งก็คือ POST_FORUM_ID private Post post; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date commentDate; ... ... ... }Category.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Version; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; /** * * @author Redcrow */ @Table(name = "CATEGORY") @Entity public class Category implements Serializable { @Id @TableGenerator(name = "Category_GEN", table = "ORPHANDRG_SEQ", pkColumnName = "NAME", valueColumnName = "VALUE", pkColumnValue = "CATEGORY") @GeneratedValue(generator = "Category_GEN", strategy = GenerationType.TABLE) @Column(name = "CATEGORY_ID") private Integer id; @Version private Integer version; //information @Column(name = "CATEGORY_NAME") private String categoryName; //เป็น bidirectional mapping ที่ชี้กลับไปที่ table POST_FORUM ฟิดล์ POST_ID @OneToMany(mappedBy = "category" , fetch= FetchType.LAZY) //FetchType.LAZY คือ ตอนที่ดึงข้อมูลของ Category มาไม่ต้องเอา post ทั้งหมดมาด้วย แต่ถ้าอยากให้ post ทั้งหมดมาด้วยก็ต้องใช้ FetchType.EAGER private List<Post> posts; ... ... ... }ตัวอย่างที่ 4 การ map Many To Many
Category.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Version; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; /** * * @author Redcrow */ @Table(name = "CATEGORY") @Entity public class Category implements Serializable { @Id @TableGenerator(name = "Category_GEN", table = "ORPHANDRG_SEQ", pkColumnName = "NAME", valueColumnName = "VALUE", pkColumnValue = "CATEGORY") @GeneratedValue(generator = "Category_GEN", strategy = GenerationType.TABLE) @Column(name = "CATEGORY_ID") private Integer id; @Version private Integer version; //information @Column(name = "CATEGORY_NAME") private String categoryName; //join @ManyToMany(fetch= FetchType.EAGER)//ใช้ keyword ManyToMany //FetchType.EAGER คือการโหลดข้อมูล UserForum ที่เกี่ยวกับ Category นี้มาทั้งหมด เมื่อมีการใหลด Category นี้ขึ้นมาใช้งาน //JoinTable เป็นการสร้าง table กลางขึ้นมาครับ โดยให้มีชื่อว่า ANSWER ซึ่งประกอบไปด้วย primary key 2 ฟิลด์ คือ ฟิลด์ CATEGORY ที่ชี้ไปที่ CATEGORY_ID หรือ primary key ของ table CATEGORY และฟิลด์ USER_FORUM ที่ชี้ไปที่ USER_FORUM_ID หรือ primary key ของ table USER_FORUM @JoinTable(name = "ANSWER", joinColumns = { @JoinColumn(name = "CATEGORY", referencedColumnName = "CATEGORY_ID")}, inverseJoinColumns = { @JoinColumn(name = "USER_FORUM", referencedColumnName = "USER_FORUM_ID")}) private List<UserForum> answers; //ตรงนี้ได้กล่าวไปแล้วในตัวอย่างที่ 3 @OneToMany(mappedBy = "category", fetch= FetchType.LAZY) private List<Post> posts; ... ... ... }UserForum.java
package com.blogspot.na5cent.model; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Temporal; import javax.persistence.Version; /** * * @author Redcrow */ @Table(name = "USER_FORUM") @Entity public class UserForum implements Serializable { @Id @TableGenerator(name = "UserForum_GEN", table = "ORPHANDRG_SEQ", pkColumnName = "NAME", valueColumnName = "VALUE", pkColumnValue = "USER_FORUM") @GeneratedValue(generator = "UserForum_GEN", strategy = GenerationType.TABLE) @Column(name = "USER_FORUM_ID") private Integer id; @Version private Integer version; //information @Column(unique=true) private String userName; @Column(length=30, nullable=false) private String password; //join //เป็น bidirectional mapping แบบ many to many ที่ชี้กลับไปที่ table Category ฟิดล์ answers และให้เป็น FetchType.EAGER //ตัว ฟิดล์ answers เป็น case sensitive ฝั่งนั้นเป็นยังไง ฝั่งนี้ต้องพิมพ์แบบนั้น ตัวเล็กตัวใหญ่มีผลครับ @ManyToMany(mappedBy = "answers", fetch = FetchType.EAGER) private List<Category> categories; ... ... ... }
เยี่ยมเลยครับ
ตอบลบขอบคุณครับ
ตอบลบขอบคุณค่ะ
ตอบลบขอบคุณครับ
ตอบลบ