Thứ Ba, 22 tháng 10, 2013

MyBatis là gì?

Bạn cần phải có các software và tool sau:
- Java JDK 1.5+
- MyBatis version (https://code.google.com/p/mybatis/)
- MySQL (http://www.mysql.com/) hoặc CSDL quan hệ nào có thể kết nối bởi JDBC Driver
- Eclipse (http://www.eclipse.org) hoặc IDE khác ( tốt nhất là eclipse)
- Apache Maven build tool(nên dùng)

Getting started

  • MyBatis là gì?
  • Tại sao lại dùng MyBatis?
  • Cài đặt và cấu hình MyBatis

  1. MyBatis là gì?
MyBatis là một persistence framework mã nguồn mở được tạo ra nhằm đơn giản hóa việc kết nối đến CSDL của JDBC và cung cấp các API để thao tác với Database dễ dàng hơn.
Trước đây MyBatis được gọi là iBatis và được viết ra năm 2002 bởi Clinton Begin. Mybatis 3 được thiết kế lại từ iBatis, có hỗ trợ Mapper và Annotations.
MyBatis được sử dụng phổ biến là do tính đơn giản và dễ sử dụng của nó. Người sử dụng có thể thao tác dễ dàng với SQL mà không cần sử dụng đến code JDBC ở mức thấp(viết lặp lại nhiều và rất nản: open,close,createStatement..) MyBatis tự động xử lý câu lệnh SQL và lấy kết quả trả về ( từ các ResultSet của JDBC) gán vào các đối tượng java được xây dựng bởi lập trình viên (POJOs) hoặc lưu dữ liệu từ các đối tượng java xuống Database.

2. Tại sao lại dùng MyBatis?
Có rất nhiều Persistence framework được viết ra cho lập trình java, nhưng MyBatis được sử dụng nhiều vì các lý do sau:
  • Giảm thiểu đáng kể code JDBC
  • Dễ học và sử dụng
  • Có thể nhúng SQL query thuần
  • hỗ trợ để tích hợp với Spring và Guice framework
  • Hiệu năng truy vấn cao.


  • Giảm thiểu đáng kể code JDBC
Java cung cấp các JDBC API (Java DataBase Connectivity) để làm việc với CSDL quan hệ, nhưng mà code JDBC là ở mức thấp (có trộn lẫn SQL query vào code java) và lập trình viên thường phải viết đi viết lại nhiều đoạn code để thực hiện các thao tác với CSDL.
  • Dễ học và sử dụng
Vì MyBatis được thiết kế nhằm mapping các câu truy vấn SQL nên chỉ cần bạn có kiến thức về SQL và Java là có thể học được và sử dụng hiệu quả, và điều đó cũng không tốn nhiều thời gian bằng học các framework khác như Hibernate hoặc EJB. :D

  • Có thể nhúng SQL query thuần
MyBatis cho phép người dùng có thể viết câu lệnh SQL thông thường, ngoài ra còn cho phép người dùng tùy biến câu lệnh truy vấn theo các parameter truyền vào. Ví dụ tìm kiếm sinh viên theo ID, Name, Mark, nếu các parameter trên mà có thì thêm vào ở trong điều kiện WHERE,để thực hiện điều này trong JDBC rất vất vả là bạn phải if … else rồi cộng chuỗi sql, sau đó mới đưa vào đối tượng Statement hoặc preparedStatement. Nhưng với MyBatis bạn sẽ có cấu trúc điều khiển sử dụng rất rõ ràng và hiệu quả mà không gây ra lỗi khi bạn muốn thay đổi câu truy vấn, vấn đề này sẽ được đề cập sau.

  • hỗ trợ để tích hợp với Spring và Guice framework
MyBatis có thể tích hợp dễ dàng với Spring và Guice. Ví dụ các dự án dùng Spring thường đi kèm với persistence framework là Hibernate (ORM – Object Relation Mapping – nôm na là mapping các row trong DB thành các đối tượng trong java) hoặc MyBatis( có thể hiểu nôm na là mapping câu truy vấn SQL – để dễ so sánh với Hibernate :) :D)

  • Hiệu năng truy vấn cao.

Hiệu năng là một vấn đề cần quan tâm của một dự án phần mềm, trong đó đóng vai trò quan trọng của framework nào sử dụng để viết ra nó. MyBatis được đánh giá là có hiệu năng cao và nhiều developer sử dụng, vì MyBatis hỗ trợ các tính năng sau:

- Connection pool: điều này thì không phải bàn cãi, một ứng dụng sử dụng connection pool sẽ cải thiện tốc độ xử lý đáng kể và sử dụng tối đa một connection ( search google nhé :))
- Caching data: MyBatis sử dụng một cơ chế gọi là in-build cache mechanism để lưu kết quả của truy vấn vào SqlSession. Việc này rất hữu ích khi bạn sử dụng lại kết quả, thì MyBatis chỉ việc trả về kết quả trong cache mà không phải truy vấn lại Database lần nữa.
- MyBatis không sử dụng proxy cho việc kết nối nên sẽ có hiệu suất tốt hơn so với các framework ORM khác.

3. Cài đặt và cấu hình MyBatis

Việc cài đặt MyBatis rất đơn giản, bạn chỉ việc coppy file mybatis-x.x.x.jar vào thư mục lib của WEB-INF nếu bạn xây dựng project Dynamic Web hoặc thêm vào dependencies trong file pom.xml của project maven
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.2</version>
</dependency>

Để cấu hình MyBatis trong ứng dụng ta sẽ xây dựng một demo đơn giản mà tôi chắc chắn rằng bạn sẽ hiểu được ngay sau khi deploy được nó.

Ta sẽ tạo một demo có thể:
-  xem danh sách sinh viên
- tìm kiếm sinh viên theo id
- thêm 1 sinh viên vào csdl

Sử dụng MySQL với script sau:

CREATE TABLE STUDENT
(
  ID INT(11) NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(50) NOT NULL,  
  PRIMARY KEY (ID)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1;

Tạo một maven project như sau:





Cấu trúc chương trình như hình sau:


Chương trình ta sẽ sử dụng CSDL MySQL, ghi log thì sử dụng slf4j và log4j.

Các dependencies cần thiết trong file pom.xml là

   org.mybatis
   mybatis
   3.2.2
  
  
   mysql
   mysql-connector-java
   5.1.22
   runtime
  
  
   org.slf4j
   slf4j-api
   1.7.5
  
  
   org.slf4j
   slf4j-log4j12
   1.7.5
   runtime
  
  
   log4j
   log4j
   1.2.17
   runtime
  

Việc đầu tiên là tạo file cấu hình cho MyBatis, file mybatis-config.xml này phải đặt trong thư mục classpath của project thì mới đọc được, tôi sẽ đặt nó trong thư mục resources.

 
  
 
 
  
   
   
    
    
    
    
   
  
 
 
  
 


Tiếp đến cấu hình cho log. File log4j.properties cũng nằm trong thư mục resources.
log4j.rootLogger=ERROR, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%-5p] %c - %m%n

Việc cấu hình đã xong, bây giờ ta sẽ xây dựng lớp FactoryUtil để quản lý SqlSessionFactory.
package vn.ds.util;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;

public class FactoryUtil {
 private static SqlSessionFactory sessionFactory = getSessionFactory();
 private static Logger log = Logger.getLogger(FactoryUtil.class);

 public static SqlSessionFactory getSessionFactory() {
  if (sessionFactory == null) {
   InputStream stream;
   try {
    stream = Resources.getResourceAsStream("mybatis-config.xml");
    sessionFactory = new SqlSessionFactoryBuilder().build(stream);
   } catch (IOException e) {
    log.error("not find mybatis-config.xml");
    throw new RuntimeException(e.getCause());
   }
  }
  return sessionFactory;
 }

 public static SqlSession openSession() {
  return sessionFactory.openSession();
 }
}

Tiếp đến là tạo mapper. File StudentMapper.xml sẽ mapping các câu truy vấn vào bản g STUDENT bao gồm 2 câu SELECT và 1 câu INSERT. File StudentMapper.java là một interface chứa các method để thực hiện các tác vụ, các method này chính là các id trong tag select và insert của file StudentMapper.xml. Lưu ý là 2 file này sau khi biên dịch, phải nằm cũng một thư mục (file .class) ở đây ta đăt chung vào thư mục vn.ds.mapper.
package vn.ds.mapper;

import java.util.List;

import vn.ds.domain.Student;

public interface StudentMapper {
 List findAllStudents();

 Student findStudentById(Integer id);

 void insertStudent(Student student);
}


 
  
  
 
 
 
 
  INSERT INTO STUDENT(ID,NAME)
  VALUES(#{id},#{name})
 



domain
Chứa các lớp POJOs lưu trữ dữ liệu từ DB, các lớp này bao gồm các thuộc tính, setter và getter.
cụ thể lớp Student như sau:


package vn.ds.domain;

public class Student {
 private Integer id;
 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 "Student{'ID' : " + id + ", NAME : " + name + "}";
 }

}

Tiếp theo ta tạo lớp service để thực thi interface StudentMapper.


package vn.ds.service;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import vn.ds.domain.Student;
import vn.ds.mapper.StudentMapper;
import vn.ds.util.FactoryUtil;

public class StudentService {
 private Logger logger = LoggerFactory.getLogger(getClass());

 public List findAllStudents() {
  SqlSession session = FactoryUtil.openSession();
  try {
   StudentMapper mapper = session.getMapper(StudentMapper.class);
   return mapper.findAllStudents();
  } finally {
   session.close();
  }
 }

 public Student findStudentById(Integer studId) {
  logger.debug("Select Student By ID :{}", studId);
  SqlSession sqlSession = FactoryUtil.openSession();
  try {
   StudentMapper studentMapper = sqlSession
     .getMapper(StudentMapper.class);
   return studentMapper.findStudentById(studId);
  } finally {
   sqlSession.close();
  }
 }

 public void createStudent(Student student) {
  SqlSession sqlSession = FactoryUtil.openSession();
  try {
   StudentMapper studentMapper = sqlSession
     .getMapper(StudentMapper.class);
   studentMapper.insertStudent(student);
   sqlSession.commit();
  } finally {
   sqlSession.close();
  }
 }
}

Cuối cùng ta tạo một controller để gọi service trên

package vn.ds.controller;
import java.util.List;

import vn.ds.domain.Student;
import vn.ds.service.StudentService;

public class StudentController {
 public static void main(String[] args) {
  StudentService service = new StudentService();
  List list =service.findAllStudents();
  for(Student s : list)
  System.out.println(s.toString());
  Student student = new Student();
  student.setId(4);
  student.setName("Student 4");
  service.createStudent(student);
  System.out.println(service.findStudentById(3));
 }
}

source code: http://www.mediafire.com/download/aoi5zxqixdt3t2u/Mybatis1.rar

Thứ Ba, 15 tháng 10, 2013

Ví dụ đơn giản về Hibernate

Xây dựng ví dụ đơn giản với Hibernate, bao gồm các file sau:
 - hibernate.cfg.xml để cấu hình hibernate như chuỗi kết nối, mapping...
 - HibernateUtils để tạo và quản lý SessionFactory cho ứng dụng
- Class java POJO gồm properties và getter, setter đại diện cho table trong chương trình java
 - File mapping lớp java trên với table trong DB, có đuôi là hbm.xml. Cụ thể đây là file Product.hbm.xml
 - cuối cùng là lớp Main dùng để chạy chươn trình. Câu lệnh sql:
CREATE TABLE PRODUCT(
ID INT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(50)  NOT NULL,
PRICE INT NOT NULL,
PRIMARY KEY(ID)
)
Tạo dự án maven với các dependencies sau:

    
      mysql
      mysql-connector-java
      5.1.20
      compile
    
    
      org.hibernate
      hibernate-core
      4.0.1.Final
      compile
    
    
      org.slf4j
      slf4j-api
      1.6.4
      compile
    
    
      org.hibernate
      hibernate-entitymanager
      4.0.1.Final
      compile
    
  
vào phần src/main/resources tạo file cấu hình cho hibernate như sau: hibernate.cfg.xml
        

  
  
    
   com.mysql.jdbc.Driver
   jdbc:mysql://localhost:3306/abc
   root
   
    
    
    1
    
        
    org.hibernate.dialect.MySQLDialect
    
    
    thread
    
    
    org.hibernate.cache.NoCacheProvider
    
    
    false
    validate
    
    
    
    
  

tạo class HibernateUtils có nhiệm vụ tạo ra một SessionFactory quản lý các session chung cho toàn bộ ứng dụng, lưu ý ở đây ta tạo biến private static SESSION_FACTORY và sử dụng phương thức getSessionFactory để chắc chắn rằng tại mọi thời điểm chỉ có 1 đối tượng SessionFactory được tạo ra.

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {
 private static SessionFactory SESSION_FACTORY;

 public static SessionFactory getSessionFactory() {
  if (SESSION_FACTORY == null) {
   Configuration configuration = new Configuration();
   SESSION_FACTORY = configuration.configure().buildSessionFactory();
  }
  return SESSION_FACTORY;
 }
}

tạo lớp Product (POJO,Bean..) trong package domain gồm các thuộc tính và setter, getter



public class Product {
 private int id;
 private String name;
 private int price;

 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 int getPrice() {
  return price;
 }

 public void setPrice(int price) {
  this.price = price;
 }
 
 @Override
 public String toString() {
  return "Product[id: " + id + " name: " + name + " price: " + price + "]";
 }

}

tạo file mapping class Product thành bảng PRODUCT trong database: Product.hbm.xml lưu ý package trong mapping

 
  
  
    
 


Cuối cùng là phương thức main để test

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

import vn.ds.domain.Product;
import vn.ds.util.HibernateUtils;

public class ProductController {
 public void save(Product product) {
  SessionFactory sf = HibernateUtils.getSessionFactory();
  Session session = sf.openSession();
  session.beginTransaction();
  session.save(product);  
  session.getTransaction().commit();
  session.close();  
 }
 public void update(Product product) {
  SessionFactory sf = HibernateUtils.getSessionFactory();
  Session session = sf.openSession();
  session.beginTransaction();
  session.update(product);  
  session.getTransaction().commit();
  session.close();
 }
 public List read(){
  SessionFactory sf = HibernateUtils.getSessionFactory();
  Session session = sf.openSession();
  List emps = session.createQuery("from Product").list();  
  session.close();
  return emps;
 }
 public static void main(String[] args) {
  ProductController pc = new ProductController();
  ArrayList products = (ArrayList) pc.read();
  for(Product p : products) {
   System.out.println(p.toString());
  }
 }
}

Thứ Năm, 26 tháng 9, 2013

Export Excel, PDF trong SpringMVC

Xuất file excel ta dùng thư viện apache poi: http://poi.apache.org/Xuất file pdf ta dùng thư viện itextpdf: http://itextpdf.com/

Ví dụ này ta sẽ xuất ra dưới dạng bảng thống kê, tức là có title, có các header cho các column và data cho bảng đó, như vậy, ta sẽ xây dựng 2 lớp builder nhận vào các đối là
Title: 1 chuỗi String
header: 1 Vector
data: 1 Vector
Đây là lớp ExcelBuilder để export ra file excel:

package vn.diepviends.util;

import java.util.Map;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.springframework.web.servlet.view.document.AbstractExcelView;

public class ExcelBuilder extends AbstractExcelView {
 @Override
 protected void buildExcelDocument(Map model,
   HSSFWorkbook workbook, HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  // get data
  Vector vHeader = (Vector) model.get("header");
  Vector vData = (Vector) model.get("data");
  String sTitle = model.get("title").toString();
  // create a excel sheet
  HSSFSheet sheet = workbook.createSheet(sTitle);
  sheet.setDefaultColumnWidth(30);
  // create style for header cells
  CellStyle style = workbook.createCellStyle();
  Font font = workbook.createFont();
  font.setFontName("Times New Roman");
  style.setFillForegroundColor(HSSFColor.BLUE.index);
  style.setFillPattern(CellStyle.SOLID_FOREGROUND);
  font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
  font.setColor(HSSFColor.WHITE.index);
  style.setFont(font);
  // create header row
  HSSFRow title = sheet.createRow(0);
  title.createCell(0).setCellValue(sTitle);
  HSSFRow header = sheet.createRow(1);  
  for (int i = 0; i < vHeader.size(); i++) {
   header.createCell(i).setCellValue(vHeader.get(i).toString());
   header.getCell(i).setCellStyle(style);
  }
  // fill data
  int r = 2;
  for (int i = 0; i < vData.size(); i++) {
   Vector v = (Vector) vData.get(i);
   HSSFRow row = sheet.createRow(r++);
   for (int j = 0; j < v.size(); j++)
    row.createCell(j).setCellValue(v.get(j).toString());
  }
 }
}

Controller ta sẽ viết như sau:
@RequestMapping("/registerReport/export2excel")
 public String exportExcel(@RequestParam(value = "from") String from,
   @RequestParam(value = "to") String to, Model model) {
  ProvisionReportDAO dao = new ProvisionReportDAO();
  String title = "Register Report";
  Vector header = new Vector();
  Vector data = new Vector();
  ArrayList list = dao.getByDate(from, to);  
  header.add("Date");
  header.add("Quantity");
  header.add("Provision Type");
  header.add("provision Code");
  for(ProvisionReport it : list){
   Vector v = new Vector();
   v.add(it.getDate());
   v.add(it.getQuantity());
   v.add(it.getType());
   v.add(it.getCode());
   data.add(v);
  }  
  model.addAttribute("title", title);
  model.addAttribute("header", header);  
  model.addAttribute("data",data); 
  return "excelView";
 }

Với PDF thì ta phải xây dựng một lớp Abstract trung gian:
package vn.diepviends.util;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.AbstractView;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;

public abstract class AbstractITextPdfView extends AbstractView {

 public AbstractITextPdfView() {
  setContentType("application/pdf");
 }

 @Override
 protected boolean generatesDownloadContent() {
  return true;
 }
  
 @Override
 protected void renderMergedOutputModel(Map model,
   HttpServletRequest request, HttpServletResponse response) throws Exception {
  // IE workaround: write into byte array first.
  ByteArrayOutputStream baos = createTemporaryOutputStream();

  // Apply preferences and build metadata.
  Document document = newDocument();
  PdfWriter writer = newWriter(document, baos);
  prepareWriter(model, writer, request);
  buildPdfMetadata(model, document, request);

  // Build PDF document.
  document.open();
  buildPdfDocument(model, document, writer, request, response);
  document.close();

  // Flush to HTTP response.
  writeToResponse(response, baos);
 }

 protected Document newDocument() {
  return new Document(PageSize.A4);
 }
 
 protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException {
  return PdfWriter.getInstance(document, os);
 }
 
 protected void prepareWriter(Map model, PdfWriter writer, HttpServletRequest request)
   throws DocumentException {

  writer.setViewerPreferences(getViewerPreferences());
 }
 
 protected int getViewerPreferences() {
  return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
 }
 
 protected void buildPdfMetadata(Map model, Document document, HttpServletRequest request) {
 }
 
 protected abstract void buildPdfDocument(Map model, Document document, PdfWriter writer,
   HttpServletRequest request, HttpServletResponse response) throws Exception; 
}

Lớp PDFBuilder extends lớp Abstract trên:
package vn.diepviends.util;

import java.util.Map;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

public class PDFBuilder extends AbstractITextPdfView {

 @Override
 protected void buildPdfDocument(Map model, Document doc,
   PdfWriter writer, HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  // get data
  Vector vHeader = (Vector) model.get("header");
  Vector vData = (Vector) model.get("data");
  String sTitle = model.get("title").toString();
  
  doc.add(new Paragraph(sTitle));
  PdfPTable table = new PdfPTable(vHeader.size());
  table.setWidthPercentage(100.0f); 
  table.setSpacingBefore(10);
  // define font for table header row
  Font font = FontFactory.getFont(FontFactory.HELVETICA);
  font.setColor(BaseColor.WHITE);

  // define table header cell
  PdfPCell cell = new PdfPCell();
  cell.setBackgroundColor(BaseColor.BLUE);
  cell.setPadding(5);
  for (int i = 0; i < vHeader.size(); i++) {
   // write table header
   cell.setPhrase(new Phrase(vHeader.get(i).toString(), font));
   table.addCell(cell);
  }  
  // write table row data
  int x = vData.size();
  int y = vHeader.size();
  for(int i = 0; i


Controller ta sẽ viết như sau:
@RequestMapping("/registerReport/export2pdf")
 public String exportPdf(@RequestParam(value = "from") String from,
   @RequestParam(value = "to") String to, Model model) {
  ProvisionReportDAO dao = new ProvisionReportDAO();
  String title = "Register Report";
  Vector header = new Vector();
  Vector data = new Vector();
  ArrayList list = dao.getByDate(from, to);  
  header.add("Date");
  header.add("Quantity");
  header.add("Provision Type");
  header.add("provision Code");
  for(ProvisionReport it : list){
   Vector v = new Vector();
   v.add(it.getDate());
   v.add(it.getQuantity());
   v.add(it.getType());
   v.add(it.getCode());
   data.add(v);
  }  
  model.addAttribute("title", title);
  model.addAttribute("header", header);  
  model.addAttribute("data",data); 
  return "pdfView";
 }

Định nghĩa các view để hiển thị ra excel và pdf

     
    
     


Trong dispatcher của spring ta gọi view.xml vào:

        
 
 
  
  
         
        
   
   
    



và đây là kết quả:



Demo Job Scheduler sử dụng Quartz

maven dependences

    log4j
    log4j
    1.2.17
   
   
    org.quartz-scheduler
    quartz
    2.0.2
   

Xây dựng lớp TestJob implement Job của Quartz. lớp này để thực hiện một công việc mà ta cần làm sau một khoảng thời gian xác định. Cụ thể công việc ở đây là in ra dòng thông báo.
package vn.diepviends.job;

import org.apache.log4j.Logger;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class TestJob implements Job {
 private Logger log = Logger.getLogger(TestJob.class);
 public void execute(JobExecutionContext arg0) throws JobExecutionException {
  log.debug("Test job run successfully after 2s");    
 }

}
Trong lớp chính cần gọi ta cài đặt khoảng thời gian lặp lại cho job và gọi TestJob class để thực hiện
package vn.diepviends.job;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class JobScheduler {
 public static void main(String[] args) {
  try {

   // chi ra job can thuc hien
   JobDetail job = JobBuilder.newJob(TestJob.class)
     .withIdentity("testJob").build();

   // qui dinh thoi gian la 2s, lap lai mai mai
   Trigger trigger = TriggerBuilder
     .newTrigger()
     .withSchedule(
       SimpleScheduleBuilder.simpleSchedule()
         .withIntervalInSeconds(2).repeatForever())
     .build();

   // start
   SchedulerFactory schFactory = new StdSchedulerFactory();
   Scheduler sch = schFactory.getScheduler();
   sch.start();
   sch.scheduleJob(job, trigger);   

  } catch (SchedulerException e) {
   e.printStackTrace();
  }
 }
}
Và đây là kết quả khi run lớp JobScheduler


Cấu trúc chương trình:

Thứ Ba, 6 tháng 8, 2013

Lấy URL bằng đối tượng Request

Để lấy URL của website ví dụ: http://www.google.com.vn/ ta dùng cú pháp:

String  url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); 
Để lấy đường dẫn thực trên server ví dụ "E:\tomcat\demo\" ta dùng:

String path  = request.getSession().getServletContext().getRealPath("/")

Thứ Hai, 5 tháng 8, 2013

Coppy and Remove file trong Java

Trong lập trình nhiều khi chúng ta phải thao tác với file, ngoài việc đọc và ghi, thì còn phải coppy và delete file, delete file theo .extend
Class util  hỗ trợ mục đích của ta:
FileUtil.java
package vn.ds.util;

import java.io.*;

public class FileUtil {

 /**
  * Coppy data từ một file nguồn vào file đích
  */
 public static void copyFile(File source, File dest) throws IOException {

  if (!dest.exists()) {
   dest.createNewFile();
  }
  InputStream in = null;
  OutputStream out = null;
  try {
   in = new FileInputStream(source);
   out = new FileOutputStream(dest);
   // Transfer bytes from in to out
   byte[] buf = new byte[1024];
   int len;
   while ((len = in.read(buf)) > 0) {
    out.write(buf, 0, len);
   }

  } catch (Exception ex) {
   System.out.println("error");
  } finally {
   in.close();
   out.close();
  }
 }

 /**
  * Coppy tất cả các file trong thư mục nguồn đến thư mục đích
  */
 public static void copyDirectory(File sourceDir, File destDir)
   throws IOException {
  if (!destDir.exists()) {
   destDir.setWritable(true);
   destDir.mkdirs();
  } else {
   destDir.setWritable(true);
  }
  File[] children = sourceDir.listFiles();
  for (File sourceChild : children) {
   String name = sourceChild.getName();
   File destChild = new File(destDir, name);
   if (sourceChild.isDirectory()) {
    copyDirectory(sourceChild, destChild);
   } else {
    copyFile(sourceChild, destChild);
   }
  }
 }

 /**
  * xoá một thư mục (bao gồm các file trong thư mục đó)
  */
 public static boolean delete(File resource) throws IOException {
  if (resource.isDirectory()) {
   File[] childFiles = resource.listFiles();
   for (File child : childFiles) {
    delete(child);
   }
  }
  return resource.delete();
 }

 /**
  * xoá các file trong một thư mục có đuôi mở rộng là một parameter
  * 
  * @param String
  *            ext
  */
 public static void deleteFile(String folder, String ext) {

  GenericExtFilter filter = new GenericExtFilter(ext);
  File dir = new File(folder);

  // list out all the file name with .txt extension
  String[] list = dir.list(filter);

  if (list.length == 0)
   return;

  File fileDelete;

  for (String file : list) {
   String temp = new StringBuffer(folder).append(File.separator)
     .append(file).toString();
   fileDelete = new File(temp);
   boolean isdeleted = fileDelete.delete();
  }
 }

 /**
  * lớp filter để để lọc các file có đuôi mở rộng là một parameter được
  * truyền vào trong constructor method
  */
 public static class GenericExtFilter implements FilenameFilter {

  private String ext;

  public GenericExtFilter(String ext) {
   this.ext = ext;
  }

  public boolean accept(File dir, String name) {
   return (name.endsWith(ext));
  }
 }

}



Thks & Rgds!!!

Thứ Tư, 31 tháng 7, 2013

Tạo connection pool MySQL trong java

Trước tiên ta xây dựng lớp Logger để ghi lại những thông báo hoặc ném ra lỗi, điều này rất cần thiết đối với dự án lớn, đặc biết là có nhiều lớp, nhiều hàm, khi debug, fix error. có thể sử dụng lớp Logger của gói java.util hoặc org.apache.log4j, để đơn giản ta xây dựng lớp Logger gồm 2 method là log method để in thông báo và error method để thông báo lỗi.
public class Logger {
 private String classname;

 public Logger(String classname) {
  this.classname = classname;
 }

 public void log(String msg) {
  System.out.println("[" + this.classname + "] " + msg);
 }

 public void error(String msg) {
  System.out.println("[ERROR] [" + this.classname + "] " + msg);
 }
}

Bây giờ ta xây dựng lớp để tạo connections pool đến MySQL trong Java, sự quan trọng và cần thiết cũng như khái niệm về connection pool trong lập trình thì có thể search trên google.com. ở đây ta không đề cập đến. Lớp kết nối sẽ có tên là DBPool:
  public class DBPool {
    private static Logger logger = new Logger("DBPool"); // biến logger của lớp Logger trên
    private static LinkedList pool = new LinkedList(); // pool để chứa các connections
    public final static int MAX_CONNECTIONS = 10;  // số connection tối đa trong pool là 10, tuỳ ý!!
    public final static int INI_CONNECTIONS = 2; // số connection khi bắt đầu khởi tạo là 2
}
Các phương thức quan trọng trong lớp:
Phương thức tạo một kết nối:
public static Connection makeDBConnection() throws SQLException {
        Connection conn = null;
        try {
            Class.forName(DBConfig.getProperties("_mf_driver", ""));
            logger.log("URL:" + DBConfig.getProperties("_mf_url", ""));
            logger.log("User:" + DBConfig.getProperties("_mf_user", ""));
            conn = DriverManager.getConnection(
             DBConfig.getProperties("_mf_url", ""),
             DBConfig.getProperties("_mf_user", ""),
                DBConfig.getProperties("_mf_passwd", ""));
            
        } catch (ClassNotFoundException ex) {
            throw new SQLException(ex.getMessage());
        }catch (Exception e) {
   // TODO: handle exception
  }
        return conn;
    }
Phương thức khởi tạo số connections theo biến INI_CONNECTIONS:
public static void build(int number) {
        logger.log("Establishing " + number + " connections...");
        Connection conn = null;
        release();
        for (int i = 0; i < number; i++) {
            try {
                conn = makeDBConnection();
            } catch (SQLException ex) {
                logger.log("Error: " + ex.getMessage());                
            }
            if (conn != null) {
                pool.addLast(conn);
            }
        }
        logger.log("Number of connection: " + size());
    }
Phương thức lấy về connection, khi các class khác cần connect DB, thì chỉ cần gọi phương thức này là được:
public static Connection getConnection() {
        Connection conn = null;
        try{
         synchronized (pool) {
             conn = (java.sql.Connection) pool.removeFirst();
         }
         if (conn == null) {
          conn = makeDBConnection();
         }
         try {
             conn.setAutoCommit(true);
         } catch (Exception ex) {}
         
        } catch(Exception e){            
            logger.error("Method getConnection(): Error executing >>>" + e.toString());
            try {
             logger.log("Make connection again.");
    conn = makeDBConnection();
    conn.setAutoCommit(true);
   } catch (SQLException e1) {
   }
   logger.log(""+conn);
        }
        return conn;
    }

Phương thức đóng một kết nối có trong pool:
 public static void putConnection(java.sql.Connection conn) {
        try { // Ignore closed connection
            if (conn == null || conn.isClosed()) {
                logger.log("putConnection: conn is null or closed: " + conn);
                return;
            }
            if (pool.size() >= MAX_CONNECTIONS) {
                conn.close();
                return;
            }
        } catch (SQLException ex) {}

        synchronized (pool) {
            pool.addLast(conn);
            pool.notify();
        }
    }
Phương thức đóng và xoá tất cả connection trong pool:
public static void release() {
        logger.log("Closing connections in pool...");
        synchronized (pool) {
            for (Iterator it = pool.iterator(); it.hasNext(); ) {
                Connection conn = (Connection) it.next();
                try {
                    conn.close();
                }
                catch (SQLException e) {
                    logger.error(
                        "release: Cannot close connection! (maybe closed?)");
                }
            }
            pool.clear();
        }
        logger.log("Release connection OK");
    }
Ngoài ra còn có các phương thức khác trong lớp DBPool như sau:
//  phương thức close một connection và preparedStatement
public static void releaseConnection(Connection conn, PreparedStatement preStmt) {       
        putConnection(conn);
        try {
            if (preStmt != null) {
                preStmt.close();
            }
        } catch (SQLException e) {}
    }

    public static void releaseConnection(Connection conn, PreparedStatement preStmt, ResultSet rs) {
        releaseConnection(conn, preStmt);
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {}
    }

    public static void releaseConnection(Connection conn, PreparedStatement preStmt, Statement stmt, ResultSet rs) {
        releaseConnection(conn, preStmt, rs);
        try {
            if (stmt != null) {
                stmt.close();
            }
        } catch (SQLException e) {}
    }
    public static void releaseConnection(Connection conn, CallableStatement cs, ResultSet rs) {
     putConnection(conn);
        try {
            if (cs != null) {
             cs.close();
            }
            if(rs!=null){
             rs.close();
            }
        } catch (SQLException e) {}
    }
Để sử dụng lớp DBPool này ta thử demo với lớp EmployeeDAO dùng để lấy tất cả các nhân viên và để thêm một nhân viên.
public ArrayList getall() {
  String sql = "SELECT * FROM employee";
  ArrayList lst = new ArrayList();
  conn = DBPool.getConnection();
  try {
   psmt = conn.prepareStatement(sql);
   rs = psmt.executeQuery();
   while (rs.next()) {
    Employee employee = new Employee();
    employee.setId(rs.getInt("EmployeeId"));
    employee.setName(rs.getString("EmployeeName"));
    lst.add(employee);
   }
  } catch (SQLException e) {
   logger.error("Error: Not get all employee " + e.getMessage());
   e.printStackTrace();
  } finally {
   DBPool.releaseConnection(conn, psmt, rs);
  }
  return lst;
 }

  public void insert(Employee employee) {
  String sql = "INSERT INTO employee(name) VALUES(?)";
  conn = DBPool.getConnection();
  try {
   psmt = conn.prepareStatement(sql);
   psmt.setString(1, employee.getName());
   psmt.executeUpdate();
  } catch (SQLException e) {
   logger.error("Error: Not insert employee! " + e.getMessage());
   e.printStackTrace();
  } finally {
   DBPool.releaseConnection(conn, psmt);
  }
 }
Cuối cùng cần import các thư viện sau vào:
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.LinkedList;
Để connect tới được CSDL MySQL ta dùng gói mysql-connector-java_5.1.14.jar. còn nếu dùng maven thì có thể thêm doạn dependency sau:

 mysql
 mysql-connector-java
 5.1.14

Thanks & Rgds!