วันอังคารที่ 26 เมษายน พ.ศ. 2554

Spring Framework and Hibernate on Annotation

แปลกใจตัวเองไม่ได้เขียนอันนี้ขึ้นมา ทั้งที่น่าจะใช้บ่อย Spring Framework และ Hibernate config on Annotation
แต่เอาเหอ ถึงจะขี้เกียจนิดหน่อย แต่มาเริ่มกันดีกว่า ว่าจะทำอะไรก่อนดี บทนี้น่าจะต่อจาก

Java Spring framework 3 Quickstart

แต่ในบทนี้จะไม่พูดถึงเรื่อง coltroller จะพูดเรื่อง ผูก service hibernate กับ spring เป็นหลัก
อันดับแรก ให้สร้าง Web Project หรือ Java Web Application ด้วย Netbean หรือ eclipse(jee) ตามแต่ถนัดนะ อันนี้คงไม่ลง detail ไม่อยากมีภาคสอง(จะยากเกินเดี๋ยวไม่จบ)

จากนั้นก็ไปที่ file : web/WEB-INF/web.xml เริ่มแก้ไข xml ให้เป็นเช่นนี้

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
    <!-- Spring Framework Config -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

จากนั้น ก็ config application file กับต่อเลย file : web/WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    <import resource="/datasource.xml" />
    <import resource="/hibernate.xml" />
    
    <context:annotation-config/>
    <context:component-scan base-package="th.go.nhso.projecttracking" />
    
</beans>



ตามด้วย config database datasource config file : web/WEB-INF/datasource.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
  
    <bean id="dataSource" 
         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/projecttracking" />
        <property name="username" value="develop" />
        <property name="password" value="password" />
    </bean>

</beans>


ต่อจากนั้น ก็ hibernate config file : web/WEB-INF/hibernate.xml
ในส่วนนี้จะทำการ create และ update table ใน database ให้เอง 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <constructor-arg ref="sessionFactory" name="sessionFactory" />
    </bean>
    
    <!-- Hibernate session factory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
        <property name="schemaUpdate" value="true"/>
        <property name="packagesToScan" value="th.go.nhso.projecttracking.model"/>
    </bean>
    
</beans>



อันดับต่อไป อันนี้ไม่ค่อยสำคัญมากในหัวเรื่องนี้ view file : dispatcher-servlet.xml
ถ้าใช้ netbean สร้าง web project ดึง spring framework เป็น plugin ก็จะมี file นี้โดยไม่ต้องเขียนเอง ส่วนใหญ่เราจะไม่ค่อยได้ยุ่งกับ file นี้มากนัก ใช้เป็นแค่ทางผ่านไป controller เฉย ๆ เพราะ concept web เดิม เราต้องเขียน serverlet config ผูกกับชื่อ (ถ้าไม่เข้าใจกลับไปดู servlet พื้นฐาน) แต่ในที่นี้เหมือนเราทำรอบเดียว

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
    
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="index.htm">indexController</prop>
            </props>
        </property>
    </bean>
    
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
    
    <!--
    The index controller.
    -->
    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="index" />
    
</beans>

นี่ขนาดใช้ annotation ช่วยแระ ยังมี xml file config เหลือขนาดนี้ -_-"

ต่อมาก็คงเป็น DAO class ที่ทำการติดต่อ database เริ่มจาก

BaseDao.java
package th.go.nhso.projecttracking.dao;

public interface BaseDao {
    public void insert(Object... models);
    public Long insert(Object model);
    public void update(Object... models);
    public void delete(Class clazz, Long... ids);
    public Object getById(Class clazz, Long id);
}


ProcessDao.java
package th.go.nhso.projecttracking.dao;

public interface ProcessDao extends BaseDao {
}

BaseDaoImpl.java
package th.go.nhso.projecttracking.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;

public abstract class BaseDaoImpl implements BaseDao {

    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Override
    public Object getById(Class clazz, Long id) {
        return (Object) hibernateTemplate.get(clazz, id);
    }

    @Override
    public void insert(Object... models) {
        for (Object model : models) {
            hibernateTemplate.save(model);
        }
    }

    @Override
    public Long insert(Object model) {
        return (Long) hibernateTemplate.save(model);
    }

    @Override
    public void update(Object... models) {
        for (Object model : models) {
            hibernateTemplate.update(model);
        }
    }

    @Override
    public void delete(Class clazz, Long... ids) {
        for (Long id : ids) {
            Object o = hibernateTemplate.get(clazz, id);
            if (null != o) {
                hibernateTemplate.delete(o);
            }
        }
    }

    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }
}



ProcessDaoImpl.java
package th.go.nhso.projecttracking.dao;

public interface ProcessDao extends BaseDao {
    
}

BaseEntity.java
package th.go.nhso.projecttracking.model;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;

@MappedSuperclass
public class BaseEntity implements Serializable {
    
    @Column
    private String createBy;
    
    @Column
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date createDate;
    
    @Column
    private String updateBy;
    
    @Column
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date updateDate;

    
    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getUpdateBy() {
        return updateBy;
    }

    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }
    
}

Process.java
package th.go.nhso.projecttracking.model;

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

@Entity
@Table(name="PROCESS")
public class Process extends BaseEntity {
    
    public Process(){}
    
    @Id
    @GeneratedValue
    private Long id;
    
    @Column
    private String code;
    
    @Column
    private String description;

    @Column
    private Long projectId;

    @Column
    private Long projectPlanId;

    public Long getProjectId() {
        return projectId;
    }

    public void setProjectId(Long projectId) {
        this.projectId = projectId;
    }

    public Long getProjectPlanId() {
        return projectPlanId;
    }

    public void setProjectPlanId(Long projectPlanId) {
        this.projectPlanId = projectPlanId;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
    
}

เราไปดู test กันดีกว่า ทำแล้วก็ต้อง test ได้
จริง ๆ ก็จะมี service รองรับ ก่อนถึง dao แต่ตอนนี้จะ test ว่าที่ทำขึ้นมาสามารถ insert ได้หรือเปล่าไปก่อน
ในส่วน config location อาจต้องแก้ path สำหรับ เรียกดู applicationContext เอง แต่ในที่นี้ ใช้วิธี copy มาที่ source path ก่อน
BaseDaoTest.java
package th.go.nhso.projecttracking.dao.test;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
public abstract class BaseDaoTest {
    
}

ProcessDaoImplTest
package th.go.nhso.projecttracking.dao.test;

import java.util.Date;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import th.go.nhso.projecttracking.model.Process;
import th.go.nhso.projecttracking.dao.ProcessDao;

public class ProcessDaoImplTest extends BaseDaoTest {
    
    @Autowired
    private ProcessDao processDao;

    public ProcessDao getProcessDao() {
        return processDao;
    }

    public void setProcessDao(ProcessDao processDao) {
        this.processDao = processDao;
    }

    @Test
    public void testSomeMethod() {
        Process p = new Process();
        p.setCode("001");
        p.setProjectId(new Long(1));
        p.setProjectPlanId(new Long(1));
        p.setDescription("detail001");
        p.setCreateBy("staff");
        p.setCreateDate(new Date());
        processDao.insert(p);
    }
}

run test file ถ้าไม่มี error ก็ ok ผ่าน คร่าว ๆ ก็น่าจะมีแค่นี้ก่อนครับ

ไม่มีความคิดเห็น: