Reading Time: 7 minutes

Chào mừng bạn đến với hướng dẫn tích hợp Hibernate với Tomcat JNDI DataSource. Cách đây không lâu, chúng ta đã cùng nhau tìm hiểu cách sử dụng công cụ ORM Hibernate trong ứng dụng Java độc lập. Hôm nay, chúng ta sẽ tìm hiểu cách tích hợp Hibernate với DataSource trong môi trường servlet container Tomcat.

tích hợp Hibernate với Tomcat JNDI DataSource

Cách dùng Hibernate trong ứng dụng web khá đơn giản, bạn chỉ cần cấu hình các thuộc tính DataSource trong tệp cấu hình Hibernate. Bước đầu tiên là chuẩn bị cơ sở dữ liệu kiểm thử và thiết lập JNDI DataSource trong Tomcat container.

Hibernate DataSource JNDI: Thiết lập cơ sở dữ liệu và ví dụ

Trong ví dụ này, chúng ta sẽ sử dụng MySQL. Đoạn script sau đây tạo một bảng đơn giản và chèn một số giá trị mẫu. Tệp employee.sql:

sql

CREATE TABLE `Employee` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `role` varchar(20) DEFAULT NULL,
  `insert_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;

INSERT INTO `Employee` (`id`, `name`, `role`, `insert_time`)
VALUES
    (3, 'Pankaj', 'CEO', NOW());

INSERT INTO `Employee` (`id`, `name`, `role`, `insert_time`)
VALUES
    (14, 'David', 'Developer', NOW());

Tên schema cơ sở dữ liệu là TestDB

Cấu hình Tomcat JNDI DataSource

Để cấu hình Tomcat khởi tạo DataSource, chúng ta cần chỉnh sửa các tệp server.xmlcontext.xml.

Tệp server.xml:

<Resource name="jdbc/MyLocalDB"
      global="jdbc/MyLocalDB"
      auth="Container"
      type="javax.sql.DataSource"
      driverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost:3306/TestDB"
      username="pankaj"
      password="pankaj123"
      maxActive="100"
      maxIdle="20"
      minIdle="5"
      maxWait="10000"/>

Thêm cấu hình Resource này vào phần tử GlobalNamingResources của tệp server.xml.

Tệp context.xml:

<ResourceLink name="jdbc/MyLocalDB"
              global="jdbc/MyLocalDB"
              auth="Container"
              type="javax.sql.DataSource" />

Thêm phần <ResourceLink> trên vào context.xml. Cấu hình này cho phép ứng dụng truy cập tài nguyên JNDI với tên jdbc/MyLocalDB.

Sau đó, khởi động lại máy chủ. Nếu cấu hình chính xác, sẽ không có lỗi nào xuất hiện trong log của Tomcat. Nếu thông tin cấu hình sai, ví dụ mật khẩu không đúng, log máy chủ sẽ hiển thị lỗi tương ứng. Ngoài ra, cần đảm bảo tệp jar driver MySQL được đặt trong thư mục lib của Tomcat, nếu không Tomcat sẽ không thể tạo kết nối cơ sở dữ liệu và log sẽ báo lỗi ClassNotFoundException.

Bây giờ cơ sở dữ liệu và thiết lập JNDI trên Tomcat đã sẵn sàng, chúng ta có thể bắt đầu xây dựng ứng dụng web sử dụng Hibernate.

Ví dụ Hibernate DataSource: Dự án Dynamic Web Project

Tạo một dự án web động (Dynamic Web Project) trong Eclipse, sau đó cấu hình thành dự án Maven. Cấu trúc của dự án sẽ giống như hình minh họa như sau:

Tích hợp Hibernate với Tomcat JNDI DataSource

Lưu ý, tôi đang sử dụng Tomcat 7 để triển khai dự án và đã thêm nó vào build path, nhờ vậy không cần bổ sung riêng thư viện Servlet API vào dự án. Tomcat 7 hỗ trợ chuẩn Servlet 3 và chúng ta sẽ sử dụng annotation để tạo servlet. Nếu bạn chưa quen với annotation trong Servlet 3, bạn có thể tham khảo hướng dẫn Servlet cho người mới bắt đầu. Bây giờ, chúng ta sẽ lần lượt xem qua từng thành phần.

Các thư viện Maven sử dụng cho Hibernate

<project xmlns="<https://maven.apache.org/POM/4.0.0>" xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>"
	xsi:schemaLocation="<https://maven.apache.org/POM/4.0.0> <https://maven.apache.org/xsd/maven-4.0.0.xsd>">
	<modelVersion>4.0.0</modelVersion>
	<groupId>HibernateDataSource</groupId>
	<artifactId>HibernateDataSource</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	
	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.3.5.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.0.5</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

Trong ví dụ này, tôi sử dụng phiên bản Hibernate 4.3.5.Final. Thư viện hibernate-core được thêm vào để sử dụng Hibernate. Thư viện mysql-connector-java cũng được thêm vì cơ sở dữ liệu sử dụng là MySQL, dù phạm vi được đặt là provided do thư viện này đã có sẵn trong Tomcat container. Ngay cả khi không khai báo các thư viện phụ thuộc cho MySQL driver, dự án vẫn có thể biên dịch và chạy bình thường. Tuy vậy, chúng ta vẫn nên khai báo trong pom.xml để giúp người xem dễ dàng nhận biết rằng dự án sử dụng MySQL.

Cấu hình DataSource trong Hibernate

Tệp cấu hình Hibernate hibernate.cfg.xml với DataSource có nội dung sau:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"<https://hibernate.org/dtd/hibernate-configuration-3.0.dtd>">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.datasource">java:comp/env/jdbc/MyLocalDB</property>
        <property name="hibernate.current_session_context_class">thread</property>

        <!-- Ánh xạ với lớp model sử dụng annotation -->
	<mapping class="com.journaldev.servlet.hibernate.model.Employee"/>
    </session-factory>
</hibernate-configuration>

Thuộc tính hibernate.connection.datasource được chỉ định làm tên DataSource mà Hibernate sẽ sử dụng cho các thao tác cơ sở dữ liệu.

Ví dụ lớp Model cho DataSource

Như trong tệp cấu hình Hibernate, chúng ta đang sử dụng annotation trong lớp model Employee. Lớp model bean như sau. Employee.java

package com.journaldev.servlet.hibernate.model;

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name="Employee",
	   uniqueConstraints={@UniqueConstraint(columnNames={"ID"})})
public class Employee {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="ID", nullable=false, unique=true, length=11)
	private int id;

	@Column(name="NAME", length=20, nullable=true)
	private String name;

	@Column(name="ROLE", length=20, nullable=true)
	private String role;

	@Column(name="insert_time", nullable=true)
	private Date insertTime;

	// Các phương thức getter/setter
	public int getId() { return id; }
	public void setId(int id) { this.id = id; }
	public String getName() { return name; }
	public void setName(String name) { this.name = name; }
	public String getRole() { return role; }
	public void setRole(String role) { this.role = role; }
	public Date getInsertTime() { return insertTime; }
	public void setInsertTime(Date insertTime) { this.insertTime = insertTime; }
}

Lớp model bean này giống với phiên bản được dùng trong bài viết hướng dẫn Hibernate dành cho người mới bắt đầu. Bạn có thể tham khảo hướng dẫn đó nếu cần làm rõ cách dùng annotation.

Cấu hình Servlet Listener cho JNDI DataSource trên Tomcat

SessionFactory của Hibernate cần được khởi tạo để sử dụng trong ứng dụng và phải được hủy khi ứng dụng web dừng hoạt động, nơi phù hợp nhất để thực hiện việc này là trong một lớp cài đặt ServletContextListener.

HibernateSessionFactoryListener.java

package com.journaldev.servlet.hibernate.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.jboss.logging.Logger;

@WebListener
public class HibernateSessionFactoryListener implements ServletContextListener {
	public final Logger logger = Logger.getLogger(HibernateSessionFactoryListener.class);

    public void contextDestroyed(ServletContextEvent servletContextEvent) {
    	SessionFactory sessionFactory = (SessionFactory) servletContextEvent.getServletContext().getAttribute("SessionFactory");
    	if(sessionFactory != null && !sessionFactory.isClosed()){
    		logger.info("Closing sessionFactory");
    		sessionFactory.close();
    	}
    	logger.info("Released Hibernate sessionFactory resource");
    }

    public void contextInitialized(ServletContextEvent servletContextEvent) {
    	Configuration configuration = new Configuration();
    	configuration.configure("hibernate.cfg.xml");
    	logger.info("Hibernate Configuration created successfully");

    	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
    	logger.info("ServiceRegistry created successfully");
    	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
    	logger.info("SessionFactory created successfully");

    	servletContextEvent.getServletContext().setAttribute("SessionFactory", sessionFactory);
    	logger.info("Hibernate SessionFactory Configured successfully");
    }
}

Nếu chưa quen với servlet listener, bạn có thể xem hướng dẫn chi tiết về Servlet Listener.

Ví dụ triển khai Servlet với Hibernate Tomcat JNDI

Chúng ta sẽ viết một servlet đơn giản, trong đó ID nhân viên được truyền qua tham số request và servlet sẽ truy vấn thông tin nhân viên từ cơ sở dữ liệu. Tất nhiên, Hibernate sẽ được sử dụng để thực hiện truy vấn và lấy dữ liệu nhân viên. GetEmployeeByID.java

package com.journaldev.servlet.hibernate;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.jboss.logging.Logger;

import com.journaldev.servlet.hibernate.model.Employee;

@WebServlet("/GetEmployeeByID")
public class GetEmployeeByID extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	public final Logger logger = Logger.getLogger(GetEmployeeByID.class);
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		int empId = Integer.parseInt(request.getParameter("empId"));
		logger.info("Request Param empId="+empId);
		
		SessionFactory sessionFactory = (SessionFactory) request.getServletContext().getAttribute("SessionFactory");
		
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		Employee emp = (Employee) session.get(Employee.class, empId);
		tx.commit();
		PrintWriter out = response.getWriter();
        response.setContentType("text/html");
        if(emp != null){
        out.print("<html><body><h2>Employee Details</h2>");
        out.print("<table border=\\"1\\" cellspacing=10 cellpadding=5>");
        out.print("<th>Employee ID</th>");
        out.print("<th>Employee Name</th>");
        out.print("<th>Employee Role</th>");
        
            out.print("<tr>");
            out.print("<td>" + empId + "</td>");
            out.print("<td>" + emp.getName() + "</td>");
            out.print("<td>" + emp.getRole() + "</td>");
            out.print("</tr>");
        out.print("</table></body><br/>");
        
        out.print("</html>");
        }else{
        	out.print("<html><body><h2>No Employee Found with ID="+empId+"</h2></body></html>");
        }
	}

}

Đây là một lớp servlet rất đơn giản. Annotation @WebServlet được sử dụng để định nghĩa mẫu URI cho servlet.

Kiểm thử ứng dụng ví dụ Hibernate DataSource với Tomcat JNDI

Ứng dụng đã sẵn sàng, chỉ cần xuất dưới dạng tệp WAR và triển khai trên container Tomcat. Dưới đây là một số ảnh chụp màn hình khi chúng ta gọi servlet của ứng dụng.

Tích hợp Hibernate với Tomcat JNDI DataSourceTích hợp Hibernate với Tomcat JNDI DataSource

Tích hợp Hibernate với Tomcat JNDI DataSource

Lưu ý, tham số request empId được truyền qua query string của URL. Bên cạnh đó, chúng ta có thể quan sát log mà ứng dụng ghi lại trong nhật ký của máy chủ.

May 08, 2014 8:14:16 PM org.hibernate.cfg.Configuration configure
INFO: HHH000043: Configuring from resource: hibernate.cfg.xml
May 08, 2014 8:14:16 PM org.hibernate.cfg.Configuration getConfigurationInputStream
INFO: HHH000040: Configuration resource: hibernate.cfg.xml
May 08, 2014 8:14:16 PM org.hibernate.cfg.Configuration doConfigure
INFO: HHH000041: Configured SessionFactory: null
May 08, 2014 8:14:16 PM com.journaldev.servlet.hibernate.listener.HibernateSessionFactoryListener contextInitialized
INFO: Hibernate Configuration created successfully
May 08, 2014 8:14:16 PM com.journaldev.servlet.hibernate.listener.HibernateSessionFactoryListener contextInitialized
INFO: ServiceRegistry created successfully
May 08, 2014 8:14:16 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
May 08, 2014 8:14:17 PM org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
May 08, 2014 8:14:17 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
May 08, 2014 8:14:17 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
May 08, 2014 8:14:17 PM com.journaldev.servlet.hibernate.listener.HibernateSessionFactoryListener contextInitialized
INFO: SessionFactory created successfully
May 08, 2014 8:14:17 PM com.journaldev.servlet.hibernate.listener.HibernateSessionFactoryListener contextInitialized
INFO: Hibernate SessionFactory Configured successfully
May 08, 2014 8:14:32 PM com.journaldev.servlet.hibernate.GetEmployeeByID doGet
INFO: Request Param empId=3
May 08, 2014 8:15:22 PM com.journaldev.servlet.hibernate.GetEmployeeByID doGet
INFO: Request Param empId=3

Khi hủy triển khai ứng dụng hoặc dừng máy chủ, trong log của máy chủ sẽ xuất hiện thông tin về việc hủy SessionFactory.

May 08, 2014 11:31:16 PM com.journaldev.servlet.hibernate.listener.HibernateSessionFactoryListener contextDestroyed
INFO: Closing sessionFactory
May 08, 2014 11:31:16 PM com.journaldev.servlet.hibernate.listener.HibernateSessionFactoryListener contextDestroyed
INFO: Released Hibernate sessionFactory resource

Trên đây là ví dụ hoàn chỉnh về Hibernate DataSource với Tomcat container. Hy vọng nội dung này rõ ràng, dễ áp dụng. Chúng ta có thể tải dự án mẫu từ liên kết bên dưới và tự thử nghiệm để tìm hiểu sâu hơn.

Tải về dự án Hibernate DataSource

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất