การใช้งาน Spring Framework + Hibernate 3 ขั้นพื้นฐานสุดๆ
เห็นทางพี่ @roofimon เขียนบทความเกี่ยวกับการใช้งาน Spring + iBatis มา 2 ตอน ( เห็นว่ายังมีอีกหลายตอน ) ผมเป็นคนหนึ่งที่เคยใช้ Hibernate มาบ้าง
ก็เลยคิดว่าน่าจะมีบทความ Spring + Hibernate == GORM ( แอบขายของเล็กน้อย อิอิ ) …
โดยผมใช้ Hibernate Annotation แทน เนื่องจากผมไม่ค่อยชอบการเขียน Configuration ของ model/persistence class เท่าไรนัก และที่สำคัญผมชอบเขียน
Annotation เป้นการส่วนตัวด้วยครับ
ออกนอกเรื่องมากไปแล้วครับ เข้าเรื่องกันเลยดีกว่า
ก่อนอื่นผู้ที่เข้าใจบทความนี้ได้ดียิ่งขึ้น ควรมีพื้นฐานของ Spring Framework และ Hibernate มาบ้างครับ ….
Software and Library ที่ใช้
1. Java SE 5.0 up
2. Spring Framework 2.5 up
3. Hibernate 3 [ Core + Annotation ]
4. Apache Common DBCP 1.2.2
5. IDE => Eclipse 3.3
เพื่อให้เข้าใจง่ายๆ เนื้อหาผมจะอธิบายดังนี้ครับ
1. การออกแบบและใช้งาน Hibernate Annotation รวมถึงการทดสอบ
2. การใช้ Spring Framework ร่วมกับ Hibernate Annotation
เริ่มต้นกันเลย
1. การออกแบบและใช้งาน Hibernate Annotation รวมถึงการทดสอบ
1.1 สิ่งที่ผมจะ Demo จะเป็นเพียงการสร้าง Model/Persistence class ง่ายๆ ขึ้นมา ไม่มีอะไรซับซ้อน ประกอบไปด้วย CRUD ทั่วๆ ไป ครับ หน้าตา Table เป็นดังนี้
CREATE TABLE SPRING66_USER( USER_ID NUMBER NOT NULL, USER_NAME VARCHAR2(100 BYTE), PASSWORD VARCHAR2(100 BYTE) )
1.2 ออกแบบระบบไว้ดังนี้ครับ
- Model/Persistence class : User ทำหน้าที่นำเสนอข้อมูลจาก Table SPRING66_User
- DAO : UserDAO [ interface ] เป็น interface สำหรับกำหนด methods การทำงาน
- Implement DAO : UserDAOHibernate implements UserDAO เป็น class สำหรับ implements ส่วนการทำงานต่างๆ
- Utilities class : HibernateUtil สำหรับจัดการ Hibernate Session
- Unit Test class : UserDAOHibernate Test เป็น class สำหรับใช้ทดสอบการทำงาน
1.3 การ coding จะตาม 1.2 เลยครับ แต่ถ้าจะใช้งาน Hibernate จะต้องมี Hibernate Configuration file คือ hibernate.cfg.xml ซึ่งใช้กำหนดการติดต่อไปยัง Database
รวมทั้งกำหนด Persistence classes ที่ทำการ mapping ไปยัง Tables ต่างๆ ใน Database ครับ ซึ่งจะมีหน้าตาดังนี้
# hibernate.cfg.xml <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="connection.url">jdbc:oracle:thin:@<hostname/ip>:1521:<sid> </property> <property name="connection.username">username</property> <property name="connection.password">password</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.OracleDialect</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">none</property> <!-- Mapping --> <mapping class="com.spring66.model.User" /> </session-factory> </hibernate-configuration>
คำอธิบาย
- ส่วนการจัดการ Database Connection ซึ่งผมใช้ Oracle ดังนั้นสิ่งที่จำเป็นของ Demo นี้คือ JDBC Driver ของ Oracle
- เปลี่ยน Dialect เป็น OracleDialect
- ส่วนที่สำคัญอีกอันคือ mapping จะเป็นการอ้างถึง Model/Persistence class ที่จะใช้ จากตัวอย่างคือ User
ต่อมาทำการสร้าง Model/Persistence Class ชื่อ User.java ดังนี้
#User
@Entity
@Table( name="SPRING66_USER" )
public class User {
@Id @GeneratedValue
private Long user_id;
@Column ( name="user_name" )
private String username;
private String password;
/** Setter/Getter methods **/
}
คำอธิบาย
@Entity เป็นการบอกให้รู้ว่า class นี้คือ Model/Persistence class
@Table ใช้เพื่อ mapping ชื่อ table เข้ากับชื่อ class ผ่าน attribute name แต่โดยค่า default แล้วชื่อจะเหมือนกัน
@Id เป็นตัวบอกว่า property/column นั้นๆ เป็น Primary Key
@GeneratedValue จะเป็นตัวกำหนด running number ของ Primary Key โดยจะขึ้นอยู่กับชนิดของ Database ที่เราใช้ครับ จากตัวอย่างผมใช้ Oracle ดังนั้น
จำเป็นจะต้องสร้าง sequence ชื่อ hibernate_sequence มารองรับครับ
@Column จะใช้ในการ mapping ชื่อ column จะเป็นลักษณะเดียวกับ @Table
ในการออกแบบ Classes ต่างๆ นั้นผมใช้ DAO แบบง่ายดังนี้
#UserDAO เป็น interface เพื่อกำหนด methods การทำงานต่างๆ ต่อ User มีหน้าตาดังนี้
public interface UserDAO {
public void insert( User user );
}
ดังนั้น จำเป็นจะต้องสร้าง class มา implements UserDAO ซึ่งตอนนี้เราใช้งานผ่าน Hibernate อย่างเดียว ผมจึงตั้งชื่อว่า UserDAOHibernate มีหน้าตาดังนี้
#UserDAOHibernate
public class UserDAOHibernate implements UserDAO {
public void insert( User user ) {
/** TO DO **/
}
}
สิ่งที่จะขาดไปเสียไม่ได้คือ class HibernateUtil ทำหน้าที่จัดการ Hibernate Session [ ใครก็ตามที่เริ่มต้นเขียน Hibernate ต้องเจอ class หน้าตาแบบนี้แน่ๆ ]
#HibernateUtil
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
ดังนั้นมาทำการ coding UserDAOHibernate ให้เสร็จสิ้นกันไปเลย
@Override
public void insert(User user) {
try {
/** Getting the Session Factory and session */
SessionFactory sf = HibernateUtil.getSessionFactory();
Session session = sf.getCurrentSession();
/** Starting the Transaction */
Transaction tx = session.beginTransaction();
/** Saving User */
session.save(user);
/** Commiting the changes */
tx.commit();
/** Closing Session */
sf.close();
} catch( Exception e ) {
e.printStackTrace();
}
}
คำอธิบาย
ขั้นตอนของทำงานเป็นดังนี้
1. สร้าง Hibernate Session ขึ้นมา
2. สร้างตัวจัดการ Transaction จาก Hibernate Session
3. เริ่มต้น Transaction
4. บันทึกข้อมูลของ User
5. Commit Transaction
ลองสร้าง Unit Test ขึ้นมา เพื่อทดสอบระบบงานหน่อยนึงครับ
#UserDAOHibernateTest
public class UserDAOHibernateTest extends TestCase {
private User user = null;
private UserDAO dao = null;
protected void setUp() throws Exception {
super.setUp();
dao = new UserDAOHibernate();
}
protected void tearDown() throws Exception {
super.tearDown();
dao = null;
}
public void testSaveRecord() throws Exception {
user = new User();
user.setUsername("Somkiat");
user.setPassword("Password");
dao.insert(user);
Assert.assertNotNull("primary key assigned", user.getUser_id());
}
}
เท่านี้น่าจะพอมองเห็นภาพการใช้งาน Hibernate 3 + Annotation เพียวๆ กันไปแล้ว
แต่
มีชาวบ้านเข้าเห็นว่า ใน method inser UserDAOHibernate จะมี try/catch ไปทำโล่ห์อะไร ทำให้ code รกรุงรังไปหมด รวมทั้งจะไปจัดการ Hibernate Session,
Transaction อะไรบ่อยๆ [ ให้สังเกตว่า อะไรที่เราเขียนบ่อยๆ ซ้ำๆ จะนำไปสร้างเป็น Utilities Class หรือ Library ]
และในตอนนั้นก็มี Spring Framework ขึ้นมา เข้ามาขอรับหน้าที่ในการจัดการ Hibernate Session, Transaction …. etc. เอง
ซึ่งมั่นใจได้ว่าไม่มีหลุดแน่ๆ [ เขาขี้โม้ไว้อย่างนั้น ] ดังนั้น ไอ้เรามันคนเชื่อง่าย ก็เอามาใช้สิครับ …….
ว่าแล้วเรามาดูต่อกันไปเลยว่า Spring Framework มันมาช่วยให้โลกสงบสุขขึ้นไหม
จากแนวคิดของ Spring Framework คือ Dependency Injection และ IoC แต่มันจะยิงไปที่ไหนนั้นจะผ่านตัวจัดการคือ Bean Factory หรือโรงงานสร้างถั่วนั่นเอง
โดย Bean Factory จะสร้างสิ่งต่างๆ ได้นั้นจะต้องอ่านจาก Configuration file ดังนั้นมาดูหน้าตาของ Configuration file ว่าเป็นยังไง
#applicationContext.xml <beans> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@<hostname/ip>:1521:<sid>" /> <property name="username" value="username" /> <property name="password" value="password" /> </bean> <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="myDataSource" /> <property name="annotatedClasses"> <list> <value>com.spring66.model.User</value> </list> </property> <property name="hibernateProperties"> <value>hibernate.dialect=org.hibernate.dialect.OracleDialect</value> </property> </bean> <bean id="UserDAO" class="com.spring66.dao.UserDAOHibernateWithSpring"> <property name="sessionFactory" ref="mySessionFactory" /> </bean> </beans>
คำอธิบาย
จะมีถั่วอยู่ 3 เม็ดคือ
- myDataSource ทำหน้าที่จัดการ Database Connection โดยจะใช้งานผ่าน DBCP ซึ่งเป็นตัวจัดการ Connection Pool
- mySessionFactory ทำหน้าที่จัดการ Hibernate Session ตรงส่วนนี้เราไม่ต้องไปเขียน HibernateUtil อีกแล้ว ลดงานไปได้อีกใช่หรือเปล่า ??
- มีส่วนที่น่าสนใจคือ annotatedClasses property จะใช้สำหรับการกำหนด Model/Persistence classes ให้ Hibernate รู้จัก
- UserDAO ส่วนนี้จะถูกยิง หรือ inject มาจากผู้ใช้งาน ซึ่งกำหนดให้ยิงมาที่ class UserDAOHibernateWithSpring จะมีหน้าตาคล้ายๆ class UserDAOHibernate
แต่จะแตกต่างกันตรงที่ใช้ APIs ของ Spring Framework ซึ่งจะอธิบายต่อไป
มาดูส่วนของ class UserDAOHibernateWithSpring ซึ่งจะถูกยิง หรือ inject ผ่าน bean ชื่อ UserDAO ในการ implements นั้นผมจะใช้ HibernateDaoSupport APIs
มาช่วย ทำให้ code มันสั้นจนไม่น่าเชื่อว่าจะทำงานได้ ดังนี้
#UserDAOHibernateWithSpring
public class UserDAOHibernateWithSpring extends HibernateDaoSupport implements UserDAO {
@Override
public void insert(User user) {
getHibernateTemplate().save(user);
}
}
สั้นไหมครับ …. ไม่น่าเชื่อว่าจะทำงานได้ !!!
ต่อมาลองเขียน Unit Test ขึ้นมาอีกสักตัวครับ ชื่อ UserDAOHibernateWithSpringTest มีหน้าตาดังนี้
#UserDAOHibernateWithSpringTest
คำอธิบายขั้นตอนการทำงาน
1. ดึงข้อมูลจาก Configuration file ชื่อ applicationContext.xml
2. ทำการยิง หรือ inject ไปยัง UserDAO
dao = (UserDAO) ctx.getBean(“UserDAO”);
3. โดย DAO ที่ถูกใช้งานคือ UserDAOHibernateWithSpring ตาม Configuration file นั่นเอง
4. ทำงานบันทึกข้อมูล
เป็นอันเสร็จสิ้นการใช้งาน Spring Framework + Hibernate 3 ขั้นพื้นฐานสุดๆ ครับ
Reference Website
http://static.springframework.org/spring/docs/2.5.x/reference/orm.html

ชอบจริงๆ Hibernate Annotation
สุดยอดเลยครับ สั้นได้ใจความจริงๆ
ปล.1 แต่ขัดใจนิดหน่อยครับ JAVA มันเป็นกาแฟ ดังนั้น BeanFactory มันน่าจะเป็นโรงงานเม็ดกาแฟนะครับ >>> เป็นโรงงานถั่วไปซะแล้ว T-T
ปล.2 ล้อเล่นนะครับ ไร้สาระไปอย่างงั้นอย่าใสใจ แฮะๆ