Thứ Hai, 4 tháng 8, 2014

Một số câu hỏi java core

Một số câu hỏi java core
  1. What is OPP? 4 main feeatures of OPP? 
  2. Compare overloading and overriding?
  3. Compare abstract class and interface?
  4. What is static variable, static method, static block?
  5. What is equals(), hashCode(), toString() method?
  6. Compare deep coppy and shallow coppy?
  7. What is final class/method/variable?
  8. What is java generic? Compare java.util.List, Map, Set?
  9. Compare ArrayList and Vector?
  10. How to sort a collection?
  11. Compare mutable and immutable object?
  12. Compare throw and thows? checked and unchecked exception?
  13. What is thread? how many ways to create a new thread?
  14. What is synchronized keyword? deadlock?

Gửi mail trong spring mvc

Ví dụ này mình sẽ demo cách gửi mail (gmail) trong Spring MVC  sử dụng javax.mail.
Nội dung bao gồm:

  • Gửi mail với from, to, subject, body truyền vào.
  • Gửi mail với template được tạo sẵn.
  • Gửi mail với một file đính kèm.
Demo:





Steps:
Trước hết thêm thư viện của javax.mail.

   javax.mail
   mail
   1.4.4
  

Mình sẽ tạo một service để thực hiện việc send mail.
@Service("MailService")
public class MailService {
 @Autowired
 private MailSender mailSender;
 @Autowired
 private SimpleMailMessage templateMailMessage;
 @Autowired
 private JavaMailSenderImpl javaMailSender;
}
Hàm để gửi mail theo from, to, subject, body:
public void sendMail(String from, String to, String subject, String body) {
  SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
  simpleMailMessage.setFrom(from);
  simpleMailMessage.setTo(to);
  simpleMailMessage.setSubject(subject);
  simpleMailMessage.setText(body);
  mailSender.send(simpleMailMessage);
 }
Hàm gửi mail theo một template được định nghĩa sẵn. Ta sẽ truyền vào tên và blog cho template body.
public void sendMailWithTemplate(String name, String blog) {
  SimpleMailMessage message = new SimpleMailMessage(templateMailMessage);
  message.setText(String.format(templateMailMessage.getText(), name, blog));
  mailSender.send(message);
 }
Hàm gửi mail và attach một file đính kèm( truyền path của file)
public void sendMailWithAttachment(String name, String blog, String filePath) {
  MimeMessage message = javaMailSender.createMimeMessage();
  try {
   MimeMessageHelper helper = new MimeMessageHelper(message, true);
   helper.setFrom(templateMailMessage.getFrom());
   helper.setTo(templateMailMessage.getTo());
   helper.setSubject(templateMailMessage.getSubject());
   helper.setText(String.format(templateMailMessage.getText(), name, blog));
   FileSystemResource file = new FileSystemResource(filePath);
   helper.addAttachment(file.getFilename(), file);
  } catch (MessagingException e) {
   throw new MailParseException(e);
  }
  javaMailSender.send(message);
 }
Bây giờ ta sẽ viết controller để gọi service.
@Controller
public class MailController {
 private static final Logger logger = LoggerFactory.getLogger(MailController.class);
 @Autowired
 MailService mailService;

 @RequestMapping("sendmail")
 public String send(HttpServletRequest request) {
  String from = "abc@gmail.com";
  String to = "def@gmail.com";
  String subject = "hello";
  String body = "content of my email";
  String name = "diepdv";
  String blog = "http://diepviends.blogspot.com";
  String filePath = request.getSession().getServletContext().getRealPath("/") + "/WEB-INF/classes/hello.txt";
  logger.info("send mail simple");
  mailService.sendMail(from, to, subject, body);
  logger.info("send mail with template");
  mailService.sendMailWithTemplate(name, blog);
  logger.info("send mail with file attachment");
  mailService.sendMailWithAttachment(name, blog, filePath);
  return "success";
 }
}
Đối với file attach: file hello.txt là file được đọc từ resources.
Bây giờ đến phần quan trọng nhất. config mail trong servlet:

  
   
    classpath:mailconfig.properties
    classpath:mailtemplate.properties
   
  
 
 
 
  
  
  
  
  
   
    ${mail.smtp.auth}
    ${mail.smtp.starttls.enable}
    ${mail.smtp.ssl.trust}
   
  
 
 
 
  
  
  
  
 
File mailconfig.properties
mail.host=smtp.gmail.com
mail.post=587
mail.username=abc@gmail.com
mail.password=1234567890
mail.smtp.auth=true
mail.smtp.starttls.enable=true
mail.smtp.ssl.trust=smtp.gmail.com
File mailtemplate.properties
mail.from=abc@gmail.com
mail.to=def@gmail.com
mail.subject=Send mail example
mail.text=Xin chào %s, \u0111ây là n\u1ED9i dung mail template, tham kh\u1EA3o thêm t\u1EA1i %s.
Source code: https://www.mediafire.com/?7qvk2di9osc71sa

Thứ Bảy, 31 tháng 5, 2014

Clean code

Có nhiều lập trình viên cứ tâm niệm một lập trình viên pro là người làm chủ được công nghệ, biết nhiều thứ, nhiều framework và nhiều ngôn ngữ lập trình. Quả thật điều trên là không sai nhưng chưa đúng (:D.)
Như chúng ta đều biết, chương trình là tập hợp các đoạn mã được lập trình viên tổ chức lại theo các qui tắc logic toán học.(thuật toán). Người lập trình viên như những người thợ, lựa chọn các cách thức xây dựng, tổ chức các đoạn mã để tạo nên một chương trình hoàn hảo. Như vậy sự khác biệt giữa lập trình viên pro và những lập trình viên khác không phải là biết nhiều công nghệ mà quan trọng là họ tổ chức các đoạn code của họ cách thông minh, quản lý tốt và hiệu quả.
Robert C. Martin đã tập hợp các qui tắc giúp cho lập trình viên tổ chức code một cách thông minh và rõ ràng trong cuốn Clean Code: A Handbook of Agile Software Craftsmanship . Anh em nếu theo nghiệp thợ xây thì có lẽ cuốn sách này là không thể thiếu. Mình có đọc qua và xin tổng kết một số vấn đề trong cuốn sách, mọi người xem qua để nâng cao khả năng code của mình. Ai chịu khó đọc hết cuốn sách thì tự nhiên thấy tầm của mình tăng lên một cách bất ngờ, cứ như ngộ ra chân lý từ bí kíp võ công vậy. (rofl)
Các mục bao gồm:
  • Clean code! What? Why? 
  • Meaningful names 
  • Comments 
  • Functions 
  • Objects and Data structures 
  • Error Handling 
  • Unit tests 

 Clean code là gì và tại sao phải clean code? 


Clean code được đánh giá theo các tiêu chuẩn sau:

- Đáp ứng được mục đích yêu cầu của đoạn code, giải quyết được yêu cầu đặt ra
- Dễ dàng mở rộng và nâng cấp
- Đơn giản, dễ hiểu, người khác có thể dễ dàng đọc được code của mình
- Không được thừa thãi, trùng lặp code
- Happy: Nếu bạn viết được một đoạn code tốt, bạn sẽ cảm thấy hạnh phúc khi đọc nó, khi người khác nhìn vào, viết code cũng như xây nhà hay chơi nhạc. Tất cả là nghệ thuật (:D)

Vì sao phải clean code?

     Công việc lập trình cũng tương tự như xây dựng một căn nhà, nếu không tính toán, tổ chức quản lý tốt code của mình, bạn sẽ bị áp lực bởi kinh phí và thời gian. Code mà rườm rà, loằng ngoằng, tổ chức không rõ ràng, nếu bài toán đơn giản, ít code, thì không sao, nhưng nếu bài toán phức tạp, có hàng nghìn dòng code, hang trăm class, rồi thời gian deadline, change requirement… những áp lực đó sẽ làm cho bạn phát điên, xoắn đít lên, và khi đó bạn sẽ hối hận hoặc hổ thẹn khi nhìn lại code của mình.
Đây là kinh nghiệm từ bản thân mình. Lập trình viên, nếu ko clean code, thì sẽ bị áp lực từ những dòng code, khách hang, quản lý, tester khiến cho chết yểu mất thôi (:P) vì thế muốn sống thọ thì hãy tìm cách clean code. (:v)
"Sản phẩm là một ánh xạ của người thợ". Một lập trình viên có tính cách lười biếng, luộm thuộm, thì khi code cũng sẽ loằng ngoằng, viết nhiều code chỉ thực hiện một công việc đơn giản. Vì thế nếu bạn cố gắng tìm cách tổ chức code của mình, dần dần bạn sẽ tập được tính cách của mình: suy nghĩ rõ ràng, đơn giản, tư duy thẳng và cẩn thận... rất nhiều lợi ích phải không? hehe

Meaningful Names


     Việc đầu tiên trong mục tiêu clean code đó là đặt một cái tên thật ý nghĩa cho variables, functions, class...
Cái tên  phải thể hiện được ý định của mình, tên của các đối tượng trên trước hết phải trả lời được các câu hỏi quan trọng như: Why it exists? What it does? How it is used?
Tên không được vô nghĩa, hoặc làm lạc hướng ý nghĩa, ví dụ những tên sau đây là ko nên chút nào:
int y;
int m;
int d;
Student[] studentList;
Student s;
Những tên biến trên đây là viết tắt và không nói lên ý nghĩa của nó, riêng biết studentList là kiểu array, thì không được đặt là studentList vì dễ gây hiểu lầm, trừ khi nó là kiểu List. Trong trường hợp này nên đặt như sau:
int year;
int month;
int day;
Student[] students;
Student student;
Tùy ngôn ngữ khác nhau thường có một xu thế đặt tên khác nhau. Nhưng theo mình đặt tên theo camelCase (cú pháp lưng lạc đà) là tâm niệm nhất:
- Tên biến không nên bắt đầu là dấu gạch ngang ( _ )
- Tên biến hoặc hàm bắt đầu là chữ thường, các từ tiếp theo thì viết hoa chữ đầu tiên
- Tên class thì viết Hoa chữ cái đầu tiên.
- Tên package thì viết thường tất cả
- Tên không nên quá dài, có độ ngắn phù hợp, và nêu bật ý nghĩa.
- Tên hàm phải nêu bật được chức năng của hàm đó, ví dụ tìm kiếm sinh viên, thêm sinh viên, vì thế nên bắt đầu là một động từ và danh từ đi kèm
- Tên biến, class là những danh từ hoặc cụm danh từ.
Ví dụ:
int number;
class Student{}
void findStudentById(){}
package vn.ds.entity;

Comments


Comments là những dòng chú thích mà lập trình viên viết để giải thích ý nghĩa của đoạn code mình viết ra. Thật là tồi tệ nếu lập trình viên không chịu viết comment, nhất là comment về classes và functions. Điều này đặc biệt cần thiết khi làm teamwork, để những người khác hiểu được ý đồ của tác giả. Java có javadoc hỗ trợ rất mạnh việc comment, ví dụ:

/**
  * convert Date to String by partner  
  * 
  * {@link String} dateString = convertDateToString(date, "yyyyMMdd")
  * 
* @param date * @param format * @return date string */ public static String convertDateToString(Date date, String formatString) { if (null == date || null == formatString || "".equals(formatString.trim())) { return null; } simpleDateFormat = new SimpleDateFormat(formatString); return simpleDateFormat.format(date); }
Khi comment không nên dư thừa, ồn ào, hoặc là đánh lạc hướng, mô tả sai.

Functions


Các function có nhiệm vụ thực hiện các tác vụ được giao trong chương trình, nói một cách hoa mỹ là: "các function kể cho chúng ta biết về câu chuyện của hệ thống" (rofl)
Các qui tắc khi xây dựng một function là:
- Qui tắc đầu tiên là function nên có kích thước nhỏ, mỗi function không được quá 80 dòng code, và không được quá 3 vòng lặp.
- Function chỉ thực hiện một chức năng. Nhiều lập trình viên có thói quen thực thi rất nhiều thứ trong một function, y như hồi chúng ta mới học C và viết hàm main, điều đó là không nên chút nào. Ví dụ nếu muốn xóa sinh viên trong database, thông thường ta sẽ tìm kiếm các sinh viên đó và sau đó sẽ xóa đi. Nếu ai đó thực hiện 2 việc đó trong một function thì không nên chút nào.
- Function thực thi code theo qui tắc "stepdown"
- Một function không nên có hơn 3 arguments, nếu có nhiều tham số giống nhau thì có thể gom lại thành List hoặc Object. Cũng không nên có tham số truyền vào là một flag( true-false) 
- Function nên có giá trị trả về (return), để thể hiện kết quả function, trừ những private method trong class.
- Nên lựa chọn case-switch thay cho nhiều câu lệnh if-else
- Nếu phải thực hiện hơn 2 vòng lặp (for, while) thì nên viết ra một function khác.
Nói tóm lại một function chuẩn là: nhỏ(20 dòng), có từ 0-3 tham số, và chỉ thực hiện một nhiệm vụ duy nhất.

Objects and Data structures


  • Objects
Objects có chức năng mô tả cá đối tượng và lưu trữ dữ liệu, vì thế một object nên:
- Ẩn đi data bên trong và thể hiện method ra bên ngoài.
- Dễ dàng tạo mới (new) một object.
- khó có thể tạo mới một behavior cho Object.
  • Data structures
- Thể hiện dữ liệu ra bên ngoài
- dễ dàng tạo mới một behavior
- khó có thể tạo mới một data structure

Error handling


Ngoại lệ xảy rất nhiều trong chương trình, và việc xử lý ngoại lệ là cần thiết mà mỗi lập trình viên phải thực hiện, nếu không xử lý ngoại lệ thì chương trình thường sẽ bị "chết", hoặc xảy ra lỗi, xử lý sai mà bản thân lập trình viên sẽ không phát hiện hoặc tìm ra khi debug, và thường gây khó chịu cho người dùng cuối(end-user).
Các ngôn ngữ lập trình hiện đại cung cấp 2 cơ chế là ném ra lỗi (throws) và xử lý nếu xảy ra lỗi (try-catch)
Hầu hết các lập trình viên đều sử dụng vô tội vạ try-catch dẫn đến tốc độ của chương trình chậm lại, và nhìn vào rất rối rắm.
Vậy khi nào thì throw và khi nào thì try-catch?
Thông thương một chương trình tập hợp rất nhiều modules, layers, chúng ta nên sử dụng throw khi hàm gây ra ngoại lệ là hàm của tầng thấp nhất(data access, logic), và try-catch được sử dụng trong tầng cao nhất. Tức là những hàm nào mà được gọi thì nên throw khi có exception, còn những hàm làm nhiệm vụ cuối cùng trong chuỗi xử lý thì xử lý try-catch.
Không được sử dụng try-catch ở nhưng trường hợp có thể check được trong code, ví dụ như check null, empty.
Có một điểm đáng lưu ý nữa là không nên sử dụng Exception chung, mà phải chỉ ra được loại của exception ( nguyên nhân gây ra). Ví dụ, không nên viết thế này:
try {
 Date date = sdf.parse(strDate); 
} catch (Exception e) {   
  }
Ở đây sử dụng lớp ngoại lệ chung Exception là không nên, mà nên thay vào đó bởi ngoại lệ ParseException.
Ngoài ra, khi dùng try-catch, phải xử lý bên trong khối lệnh catch. thông thường gồm 2 phần: xử lý gì đó để chương trình tiếp tục chạy, và đưa ra một thông báo cho người dùng biết (nếu cần thiết)

Unit tests


Unit test giúp cho lập trình viên kiểm tra được các lỗi cơ bản trong lập trình mà test thực nghiệm tổng thể khó phát hiện ra, cho nên viết unit test là rất cần thiết và bắt buộc ( rất tiếc là ở việt nam ta ít người viết, trừ khi bị bắt buộc. :P)
Với ý nghĩa quan trọng trên, unit test phải được viết song song và độc lập với code, điều này sẽ tránh được rất nhiều lỗi mà khi code lập trình viên bỏ qua hoặc không thể phát hiện ra được. Hiện nay các ngôn ngữ lập trình đều có các thư viện giúp viết unit test nhanh chóng và chính xác như JUnit, NUnit
Một Unit test nên theo qui tắc F.I.R.S.T
  • Fast
  • Independent
  • Repeatable: Có thể chạy lại trên các môi trường khác nhau mà không phải config lại
  • Self-Validation: Bản thân unit test phải được validate, thật là buồn cười khi unit test phải validate lại thì chẳng khác nào bạn đang viết lại toàn bộ code của mình. :D
  • Timly: Unit test phải được update kịp thời khi code được chỉnh sửa.
Trên đây là những gì mình tổng kết lại từ cuốn sách  Clean Code: A Handbook of Agile Software Craftsmanship  và những kinh nghiệm từ bản thân.
"Cuộc đời lập trình viên là những chặng đường xây dựng, và những dòng mã là thể hiện tất cả của con người chúng ta. Vì thế hãy hoàn thiện bản thân mình qua chính những đứa con mà mình tạo ra"



Thanks & Regards!!!!

Thứ Năm, 17 tháng 4, 2014

Nói thêm về MVC

Trong lập trình mô hình MVC được áp dụng phổ biến hiện nay. Ta đều đã nghe nói đến nó,  làm thử với nó. nhưng mà mỗi người lại có một cách tổ chức không giống nhau dẫn đến không phát huy hết sức mạnh của MVC.
Đành rằng MVC là Model-View-Controller. nhưng mà cụ thể trong một project phức tạp thì việc phân chia các module cho khoa học là cả một quá trình tích lũy kinh nghiệm cũng như khả năng tư duy và logic của lập trình viên. mình sẽ nói qua một vài điều cơ bản về cách tổ chức mô hình MVC trong web application (theo ý kiến cá nhân thôi)

Controller:

Controller là module có nhiệm vụ nhận các request từ client, check permission, sau đó lấy dữ liệu từ model, và quyết định view format nào để hiển thị (html, xml, json..).
Các controller thông thường xử lý các action cở bản sau: index, list, show, create, save, edit, update, và delete
Với nhiệm vụ của controller như vậy, thì nên viết controller càng ít code, rõ ràng càng tốt. Không nên thực hiện bất cứ logic, queries hoặc validate nào trong controller.
Đối với các controller đại diện cho một Entity, nên đặt tên theo cú pháp: <Entity>Controller: StudentController, CustomerController

View:

View có chức năng hiển thị dữ liệu khi server trả về kết quả cho request của client. Vì thế view có thể là HTML, XML, JSON... , thường là các markup language nên view càng đơn giản, rõ ràng càng tốt. không nên viết lẫn các mã script trong view, ví dụ như <%%>, <??> , javascript nên tách  ra thành các file references, css không nên viết trong selector mà xây dựng thành các class.
Nên xây dựng thành các layout cho các chức năng khác nhau như admin, user...
 Ngoài ra có những phần chung cho nhiều view thì nên xây dựng thành package taglib riêng. ví dụ pagging, sorting, errors, messages...

Model:

Model là module xử lý logic nghiệp vụ, cũng như tương tác với Database, vì thế việc thiết kế và xây dựng Model khoa học là rất quan trọng
Entity: Trước tiên Model phải có 1 package chứa các Entity mapping với các Table trong Database, nên xây dựng một BaseEntity class để cho các Entity kế thừa.
public class BaseEntity implements Serializable {
 private static final long serialVersionUID = 654960331737867949L;
 @Id
 @Column(name = "id")
 protected Long id;

 @Column(name = "version")
 @Version
 private Long version;

 @Override
 public int hashCode() {

 }

 @Override
 public boolean equals(Object object) {
 }

 @Override
 public String toString() {

 }
}
Logic/Service: xử lý các logic nghiệp vụ
Repository: Thực thi các truy vấn, tương tác với database.
Dto: Các class dto chứa các fields như entity, nhưng được tùy biến để có thể sử dụng trong các trường hợp cần thêm bớt trường nào. (không được sửa entity. bắt buộc giống với Table)
Validation: các class chứa các method dùng để validate dữ liệu từ người dùng. hầu hết các framework đều hỗ trợ validate nên các class này có thể extend hoặc implement từ framework.

General:

Ngoài ra trong ứng dụng còn có các phần common sử dụng chung
Exception: Các class này rất quan trọng, dùng để xử lý exception, đặc biệt là RuntimeException.
Logging: xử lý để ghi log cho ứng dụng
Util: Chứa các lớp xử lý cho các common chung của ứng dụng như DateUtil, StringUtil, upload/ download..

Demo:

Thanks & Regards!!!

Thứ Năm, 20 tháng 2, 2014

Conflict giữa Apache basic authentication và spring security

Nếu Tomcat có sử dụng Apache basic authentication để xác thực. nếu ứng dụng sử dụng tomcat trên, và có tích hợp với spring security thì sẽ có conflict giữa 2 dạng xác thực này. Mình đã gặp lỗi này và phải mất 1 tiếng chạy đi hỏi lại network, rồi test lại chương trình. chỉ cần thay tag cấu hình <http> cho spring security thì sẽ không config.
thay

<http auto-config="true">
 <form-login login-page="/login" default-target-url="/index"
 authentication-failure-url="/failure"  />
 <logout logout-success-url="/login" />
 <access-denied-handler error-page="/denied"/>
</http>
bằng cách bỏ auto-config đi


<http>
 <form-login login-page="/login" default-target-url="/index"
 authentication-failure-url="/failure"  />
 <logout logout-success-url="/login" />
 <access-denied-handler error-page="/denied"/>
</http>

Tham khảo tại đây: http://docs.spring.io/autorepo/docs/spring-security/3.1.4.RELEASE/reference/ns-config.html

Thứ Ba, 11 tháng 2, 2014

Asian japanese chinese korean characters in ITextPDF


   com.itextpdf
   itextpdf
   5.4.5
  
  
   com.itextpdf
   itext-asian
   5.2.0
  

để write các ký tự utf-8 trong itextpdf ta sử dụng:
BaseFont base = BaseFont.createFont("KozMinPro-Regular", "UniJIS-UCS2-H", BaseFont.NOT_EMBEDDED);
  Font font = new Font(base, 12, Font.NORMAL);
  Phrase p = new Phrase("部品カタログプロテクター無プロテクター無プロテクター無プロテクター無",font);
  document.add(new Paragraph(p));
Result:

Thứ Năm, 2 tháng 1, 2014

Xử lý File Upload trong Spring MVC

Để upload file từ client lên server ta sử dụng thư viện apache common-io và giao diện MultipartResolver của Spring.
Example lấy từ đây http://diepviends.blogspot.com/2013/12/xu-ly-form-trong-spring-mvc.html
Đầu tiên add các thư viện cần thiết vào
mở file pom.xml thêm các dependency sau:

            commons-io
            commons-io
            2.1
        

            javax
            javaee-web-api
            6.0
            provided
        
Cấu hình bean cho dispatcher servlet
Vào /WEB-INF/spring/appServlet/servlet-context.xml định nghĩa bean xử lý MultipartFile

bây giờ cấu hình cho dispatcher servlet xử lý multipart vào web.xml thêm đoạn như sau:

  appServlet
  org.springframework.web.servlet.DispatcherServlet
  
   contextConfigLocation
   /WEB-INF/spring/appServlet/servlet-context.xml
  
  1
  
   5000000
  
 
Xong việc cấu hình.
xử lý file upload thì có 2 cách:
Cách 1: lưu trực tiếp file vào DB, dữ liệu file được lưu trữ là kiểu binary
Cách 2: lưu file vào một thư mục nào đó trên server và sau đó lưu trữ đường dẫn vào DB
Cách thứ 2 thường được sử dụng đối với ứng dụng có upload image từ client
Trong ví dụ này, ta sẽ demo cả 2 cách xử lý trên (cũng tương tự nhau)
Ví dụ của ta sẽ lưu dữ liệu image của product ( kiểu byte) vào trường image (cách 1)
và upload ảnh vào thư mục /resources/upload trên server sau đó lưu đường dẫn của file ảnh được upload lên server vào trường imagePath.(cách 2)
Cụ thể Product class sẽ như sau:
package vn.ds.store.domains;

import java.io.Serializable;

public class Product implements Serializable {
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private String name;
 private String price;
 private byte[] image;
 private String imagePath;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getPrice() {
  return price;
 }

 public void setPrice(String price) {
  this.price = price;
 }

 public byte[] getImage() {
  return image;
 }

 public void setImage(byte[] image) {
  this.image = image;
 }

 public String getImagePath() {
  return imagePath;
 }

 public void setImagePath(String imagePath) {
  this.imagePath = imagePath;
 }
 
}
File createProduct.jsp sẽ được sửa như sau:
  
  
create
Name
Price
Image
Chú ý thêm thuộc tính cho form tag để xử lý upload: enctype="multipart/form-data"
file listProduct.jsp sẽ là:

   
List of products
Name Price Image imagePath
${p.name} ${p.price}
Bây giờ ta sẽ viết phần quan trọng nhất - controller xử lý upload ProductController.java
package vn.ds.store.controllers;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import vn.ds.store.domains.Product;
import vn.ds.store.validators.ProductValidator;

@Controller
@RequestMapping("product")
public class ProductController {

 private ProductValidator validator = new ProductValidator();

 @RequestMapping(value = "create", method = RequestMethod.GET)
 public String doGet(Model model) {
  model.addAttribute("product", new Product());
  return "createProduct";
 }

 @RequestMapping(value = "save", method = RequestMethod.POST)
 public String doPost(@ModelAttribute Product product, Model model,
   BindingResult errors,
   @RequestParam(value = "file", required = false) MultipartFile file,
   HttpServletRequest request) {
  validator.validate(product, errors);
  if (errors.hasErrors()) {
   return "createProduct";
  }
  if (file != null) {
   byte[] fileContent = null;
   try {
    InputStream inputStream = file.getInputStream();    
    if (inputStream == null)
     System.out.println("File inputstream is null");
    // cach 1 - luu truc tiep
    fileContent = IOUtils.toByteArray(inputStream);    
    product.setImage(fileContent);    
    // cach 2 - upload vao thu muc
    String path = request.getSession().getServletContext().getRealPath("/") + "resources/upload/";
    FileUtils.forceMkdir(new File(path));
    File upload = new File (path + file.getOriginalFilename());
    file.transferTo(upload);
    String imagePath = request.getContextPath() + "/resources/upload/" + file.getOriginalFilename();
    product.setImagePath(imagePath);
    request.getSession().setAttribute("product", product);
    IOUtils.closeQuietly(inputStream);
   } catch (IOException ex) {
    System.out.println("Error saving uploaded file");
   }
  }
  ArrayList lst = new ArrayList();
  lst.add(product);
  model.addAttribute("products", lst);
  return "listProduct";
 }

 @RequestMapping(value = "/image", method = RequestMethod.GET)
 @ResponseBody
 public byte[] downloadImg(HttpServletRequest request) {
  Product product = (Product) request.getSession()
    .getAttribute("product");
  if (product != null)
   return product.getImage();
  else
   return null;
 }
}
Ở đây, ta lưu dữ liệu vào session để lấy ra sau (thông thường là lưu vào database, ở đây đang là demo). Cách 1 ta sẽ lưu dữ liệu vào biến image bằng hàm
IOUtils.toByteArray(inputStream); 
Sau đó ta định nghĩa một url mà để gọi đến trong src của thẻ img
url này chỉ gửi xuống client dữ liệu của image nên ta sử dụng @ResponseBody của spring hỗ trợ để làm việc đó (search google.com để biêt thêm chi tiết), dưới jsp ta chỉ cần gọi đến url này
Cách 2 ta sẽ coppy ảnh vào thư mục /resoures/upload để lấy đường dẫn vật lý trên server để tạo file ta dùng lệnh này:
String path = request.getSession().getServletContext().getRealPath("/") + "resources/upload/";
và để hiển thị file trên html, thẻ img chỉ hiển thị đường dẫn internet, ta sẽ lấy đường dẫn theo lệnh sau:
String imagePath = request.getContextPath() + "/resources/upload/" + file.getOriginalFilename();
và cuối cùng lệnh coppy data
file.transferTo(upload);
Kết quả:



source code:
https://www.mediafire.com/?4b23hkdzfhwil4g

Thanks & Regards!

Thứ Tư, 1 tháng 1, 2014

Ckeditor trong Spring MVC

Để áp dụng một rich-text ta sử dụng ckeditor: http://ckeditor.com/download (basic package, standard package, full package) và jquery http://jquery.com/download/ example lấy từ đây: http://diepviends.blogspot.com/2013/12/xu-ly-form-trong-spring-mvc.html
Trước tiên ta thêm ckeditor và jquery vào resources

Trong Product class ta thêm description về product đó
package vn.ds.store.domains;

import java.io.Serializable;

public class Product implements Serializable {
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private String name;
 private String price;
 private String description;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getPrice() {
  return price;
 }

 public void setPrice(String price) {
  this.price = price;
 }

 public String getDescription() {
  return description;
 }

 public void setDescription(String description) {
  this.description = description;
 }
}
Thêm link của ckeditor và jquery



apply ckeditor thì có 2 cách:
 Cách 1: sử dụng ngay trong hàm ready của jquery

Cách 2: viết script trong body tag
    Description


    
 


   
Kết quả:


source code:
http://www.mediafire.com/download/br9fc35go9f572t/Store_ckeditor.rar
Thanks & Regard!

Template với Apache Tiles trong Spring MVC

Khi xây dựng một ứng dụng web, ta thường đặt nội dung trang web trong các template, để trình bày theo một bố cục nhất định. Tương tự như trong aspx có master page,  công nghệ jsp cũng hộ trợ template nhờ thẻ <%@include file ="xxx.jsp"%>
Nếu không sử dụng cách trên, Apache Tiles là một lựa chọn phổ biến trong jsp technology.
Apache Tiles là một fw độc lập, có thể tích hợp với nhiều fw khác như Struts, JSF, Spring..
Có thể tìm hiểu thêm thông tin trên google và trên trang chủ của Apache Tiles https://tiles.apache.org/
Example sẽ lấy từ đây: http://diepviends.blogspot.com/2013/12/xu-ly-form-trong-spring-mvc.html
Ví dụ của chúng ta sẽ trình bày theo bố cục sau:


Đầu tiên import các thư viện cần thiết-> thêm dependency vào pom.xml

  
   org.apache.tiles
   tiles-core
   2.2.2
   
    
     commons-logging
     commons-logging
    
   
  
  
   org.apache.tiles
   tiles-jsp
   2.2.2
  
Hiện tại Tiles đã có version 3, trong bài viết này ta sử dụng tiles 2 (đang rất phổ biến trong các ứng dụng j2ee)
Ta xây dựng các phần cho template:
header.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
Header
menu.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
menu
footer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
footer
default.jsp
<tiles:insertAttribute name="title" />
Ở đây ta dùng tag
để chèn nội dung vào tương tự như
<%@include file ="xxx.jsp"%>
và import vào taglib
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>

Tiếp theo ta cấu hình cho dispatcher servlet


    
        
    
    
    
        
            
                /WEB-INF/layouts/layouts.xml
                
                /WEB-INF/views/**/views.xml
            
        
    
Ở trên tất cả các controller khi trả về view name thì spring sẽ hiển thị view là tiles, các định nghĩa của tiles sẽ nằm trong các file views.xml trong thư mục /WEB-INF/views/ (kể cả thư mục con) và layout để hiển thị tham chiếu từ file layouts.xml trong thư mục /WEB-INF/layouts/
layouts.xml

	
		
		
		
		
		
	
	

để định nghĩa cho template ta chỉ cần put attribute cần đưa vào layout, ví dụ ở đây, template thì header, menu, footer là cố định, còn title và content thì được truyền vào (định nghĩa trong views.xml) views.xml

    
        
        
    
    
        
        
    

Mỗi trang cần áp dụng template thì ta sẽ định nghĩa trong views.xml. trong ví dụ thì ta định nghĩa cho màn create product và hiển thị danh sách product
Kết quả:

Source code: http://www.mediafire.com/download/dodubbe20yaxz0m/Store_tiles.rar
Thanks & Regard!

Thứ Ba, 31 tháng 12, 2013

Java Peristence Framework - MyBatis, Hibernate or JPA

Lập trình cơ sở dữ liệu là một trong những khía cạnh quan trọng của lập trình. Trong java ta đã từng làm quen với JDBC (Java DataBase Connectivity) dùng để kết nối đến một Datasource, và trong ứng dụng Enterprise ta có EJB (Enterprise Java Beans). Nhưng với sự phát triển của ngành lập trình, ORM (Object-Relational Mapping) ra đời như một sự cải tiến cho JDBC. ORM cho phép lập trình dễ dàng hơn, tuân thủ chặt chẽ lý thuyết lập trình hướng đối tượng, phù hợp với ứng dụng n-tier. Trong các framework ORM cho java nổi trội hơn cả là MyBatis, Hibernate, và JPA (sự cải tiến của EJB, trở thành chuẩn của Java EE). Vậy chúng ta cùng thử 1 so sánh nho nhỏ về 3 công cụ nổi tiếng và mạnh mẽ này.

1. Persistence Layer
Trước hết chúng ta nói qua một chút về persistence layer.

Các ứng dụng enterprise thường được xây dựng theo kiến trúc nhiều tầng, ví dụ như Presentation layer, Bussiness Layer, Persistence Layer. Tầng Persistence sẽ là tầng kết nối với DB, là cầu nối giúp ứng dụng tương tác lên cơ sở dữ liệu như INSERT, UPDATE data, hoặc lưu giữ data cho ứng dụng được get (SELECT) từ DB.
Như vậy một Persistence layer sẽ bao gồm sau:
- Tạo session
Persistence layer có nhiệm vụ tạo các phiên kết nối từ ứng dụng đến Cơ sở dữ liệu, mà qua các phiên này, ứng dụng sẽ tương tác được với DB, các phiên kết nối được thực hiện khi các connections được thiết lập.
- Thực thi query
Khi các connections được thiết lập, tầng persistence có nhiệm vụ thực thi các truy vấn của ứng dụng lên cơ sở dữ liệu như update, insert và select data, call store procedure... thông qua các phương thức được xây dựng và cung cấp trong persistence layer
- Mapping
Persistence layer sẽ ánh xạ cấu trúc DB, tables, columns, rows với các class, object của java, và nhờ dó, các java object có nhiệm vụ lưu trữ thông tin của các thực thể trong tables khi truy vấn (SELECT) hoặc lưu giữ thông tin từ người dùng và cập nhật vào DB(INSERT,UPDATE). ví dụ một mapping như sau:
CREATE TABLE PRODUCT (
ID INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(255) NOT NULL,
PRICE INT NOT NULL
)
Một table có cấu trúc như trên sẽ được ánh xạ bởi class sau:
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 String getPrice() {
   return price;
  }

  public void setPrice(String price) {
   this.price = price;
  }
}
 - Transaction and Exception
Khi truy vấn dữ liệu sẽ xảy ra các trường hợp các truy vấn được thực thi không thành công( truy vấn theo batch, ngắt kết nối....) những rủi ro xảy ra cho ứng dụng và dữ liệu trong DB khi truy vấn thất bại, hoặc connection bị ngắt kết nối thì peristence phải có nhiệm vụ phục hồi tình trạng trước đó của dữ liệu, hoặc đưa ra các ngoại lệ, các hướng dẫn, log để thông báo cho ứng dụng.
- Cache
Để phục vụ tốt hơn cho việc truy vấn ( hiệu suất, tốc độ, tính tương thích..) thông thường persistence layer sẽ load một metadata của Database vào ứng dụng, các mapping giữa các đối tượng trong ứng dụng với các tables thông qua các config, xml file, hoặc tạo ra các đối tượng, được lưu trữ như cache để có thể truy vấn lại mà không phải kết nối đến DB... Diều này sẽ làm cải thiện tốc độ cho chương trình nhưng đôi khi lại gây ra một sự cồng kềnh, phức tạp.
Java cung cấp JDBC để xây dựng tầng persistence, các  JDBC API sẽ sử dụng các SQL query được viết chung với code java và thực thi nó thông qua các command, statement... Tuy nhiên những ORM fw sau này được xây dựng đã lược bỏ sự phức tạp của JDBC, thay vào đó, bản thân các ORM sẽ định nghĩa các API cho riêng mình, giúp cho developer khỏi bận tâm hơn việc thao tác DB, mà tập trung vào xây dựng bussiness cho ứng dụng.
2. MyBatis
MyBatis là sự cải tiến của phiên bản IBatis 2, là một persistence framework, tự động mapping giữa SQL database và Java object thông qua các config được định nghĩa trong file xml. Là một persistence framework nhưng MyBatis khá đơn giản, dễ học và có kích thước nhỏ, số lượng các package ít hơn nhiều so với các persistence fw khác. MyBatis thao tác trực tiếp với câu lệnh SQL, nên dù là một persistence fw nhưng MyBatis chỉ có các tính năng như Create session, query data. còn Transaction thì không cũng cấp mà dùng của SQL. Ngoài ra MyBatis cũng không lưu trữ cache như Hibernate hoặc JPA. Các config để mapping được đặt trong các file xml, theo các cấu trúc của mybatis. Là một persistence framework nhưng thực ra Mybatis không truy vấn trên java object như các framework khác, mà truy vấn trược tiếp trên SQL, sau đó kết quả trả về sẽ mapping với java object thông qua resutset mapping. tính năng này ở Hibernate và JPA cũng có nhưng Mybatis đã tận dụng và phát huy sức mạnh, khiến cho framework này dù đơn giản, dễ học và phát triển ứng dụng nhanh nhưng tốc độ truy vấn cao, dễ dàng tùy biến với những query phức tạp, và không gây khó khăn hay ức chế cho dev. Vì thế nếu xây dựng ứng dụng mà yêu cầu thời gian phát triển ngắn, không phức tạp, dễ bảo trì, và dễ tìm hiểu cho dev thì mybatis là lựa chọn tối ưu nhất
3. Hibernate
Hibernate là một persistence fw được phát triển bởi Gavin King năm 2001 theo mô hình entity bean của EJB2, fw này sẽ mapping DB với các POJOs(Plain Old Java Object) và sẽ thao tác trên các oject đó. Hibernate sử dụng ngôn ngữ truy vấn riêng của mình là HQL (Hibernate Query language), tượng tự SQL, nhưng các truy vấn của Hibernate đều thực hiện trên object, điều này khá mới mẻ và khó tiếp cận đối với những ai chưa quen làm việc với ORM. Ngoài ra hibernate hỗ trợ rất lớn provider cho hầu hết các hệ quản trị cơ sở dữ liệu thông dụng, hỗ trợ Transaction, lưu cache.. Nên nếu một ứng dụng mà yêu cầu tương thích với nhiều datasource hỗ trợ truy vấn tự động, mạnh mẽ và an toàn, hoặc có thể thay đổi cấu hình mà không ảnh hưởng tới code bên trong thì Hibernate là một sự lựa chọn tốt hơn hẳn so với Mybatis
4. JPA
Ngoài Mybatis và Hibernate, JPA cũng là một persistence fw được ưu thích, JPA 1.0 được giới thiệu năm 2006, phiên bản JPA 2.0 ra đời năm 2009 và mới đây nhất ngày 22/04/2013 thì JPA 2.1 chính thức ra mắt.
Về cơ bản JPA khá giống với Hibernate, cả hai đều là nỗ lực khắc phục nhược điểm của EJB, đều sử dụng mô hình entity bean để mapping với database, và truy vấn trên các object đó. Tuy nhiên, điểm khác biệt là JPA sử dụng ngôn ngữ truy vấn JPQL(Java Persistence Query Language) - tương đồng với HQL, ngoài ra JPA không sử dụng riêng lẻ được, mà phải đi kèm với một provider khác để config, ví dụ như EclipseLink, Oracle TopLink, Hibernate. JPA trở thành chuẩn persistence của java, các API được định nghĩa trong package javax.pesistence. Nếu yêu cầu xây dựng ứng dụng tuân thủ chặt chẽ quy tắc của java enterprise, được hỗ trợ bởi java thì JPA là một đối thủ nặng ký đối với Hibernate
5. Tổng kết
Trên đây là vài sơ lược về MyBatis, Hibernate và JPA, sau này ta sẽ có những mô tả chi tiết và cụ thể hơn về mỗi fw.
Như vậy:
- MyBatis phù hợp với các ứng dụng có yêu cầu thời gian xây dựng ngắn, chi phí phát triển thấp, đơn giản và dễ học.
- Hibernate vs JPA phù hớp với các ứng dụng tuân thủ chặt chẽ quy tắc của ORM, hỗ trợ kết nối đến nhiều cơ sở dữ liệu, dễ điều khiển. Ngoài ra sự hộ trợ tài liệu, trợ giúp bởi cộng đồng dev của Hibernate và JPA (đặc biệt là Hibernate) là rất lớn. Tuy nhiên để xây dựng một ứng dụng với Hibernate hoặc JPA mất rất nhiều thời gian phát triển, không kể thời gian dài tìm hiểu công nghệ, và phải hiểu rõ, phân tích tường tận mọi khía cạnh của ứng dụng. Đặc biệt là mô hình ER của dữ liệu. vì các quan hệ của các bảng(1-1,1-n,n-n) được định nghĩa trong các entity, qua đó JPQL(hay HQL) mới có thể truy vấn được. Ngay cả việc xử lý kết quả lấy từ nhiều tables(SELECT nhiều tables) cũng gây ra khá nhiều rắc rối cho những người mới bắt đầu. Tuy nhiên các persistence sử dụng entity bean vẫn được ưa chuộng hơn cả.
Thanks & Regard!

Thứ Hai, 30 tháng 12, 2013

Theme trong Spring MVC

Một ứng dụng web đôi khi chúng ta cần thay đổi lại theme của giao diện, ví dụ như color, image, style..
Spring hỗ trợ thay đổi giao diện bằng interface org.springframework.ui.context.ThemeSource và load theme được định nghĩa trong resources bằng org.springframework.ui.context.support.ResourceBundleThemeSource
trong ví dụ này ta có 2 theme là default và blue, hiển thị sẽ như sau:
default


blue
và 2 flie css 
blue.css
body{
background-color:#5cf;
}
default.css
body{
 background-color:#fff;
}

Ta sẽ tạo 2 file properties trong thư mục scr/main/resources/themes
blue.properties
theme=resources/css/themes/blue.css
default.properties
theme=resources/css/themes/default.css
Các file này sẽ chỉ ra style nào được áp dụng qua tham số key = theme
Điều quan trọng nhất là cấu hình cho spring xử lý như mong muốn của ta
vào file /WEB-INF/spring/appServlet/servlet-context.xml
thêm vào đoạn config sau:

   
  
   
    
  
 
 
 
  
   
  
  
 
Trong đoạn config

   
  
   
    
   
ta khai báo bean của lớp org.springframework.web.servlet.theme.ThemeChangeInterceptor để có thể xử lý theme, khi người dụng truyền vào paramerer có name là "theme" bean "themeSource"
 
 
 
   
sẽ load file resource từ forder themes với basenamePrefix = ""
Bạn có thể sửa lại tên file theo prefix nào đó ví dụ: xxx_blue.properties thì bạn sẽ khai báo là basenamePrefix = "xxx" Bean "themeResolver"
   
  
  
sẽ lưu theme hiện tại vào cookie, ngoài ra khai báo theme default là "default" (tùy chọn)
Cuối cùng trong file jsp ta sẽ cấu hình như sau:
createProduct.jsp:
diepviends blog
" type="text/css"/>

diepviends blog
" type="text/css"/>

Source code:
https://www.mediafire.com/?gz4eq9u94pfqt03

Thks & rgds!

i18n trong Spring MVC

Trong bài này ta sẽ xây dựng website đa ngôn ngữ i18n (Internationalization)
Cấu trúc thư mục sẽ như sau:

trong thư mục /WEB-INF/i18n ta sẽ tạo 2 file properties application.properties và application_vi.properties lưu trữ các label cho ngôn ngữ mặc định  và ngôn ngữ tiếng việt ( ngôn ngữ mặc định đây là tùy chọn, cấu hình trong servlet-context.xml)
file application_vi.properties
file application.properties

và sửa lại view để hỗ trợ hiển thị theo ngôn ngữ tùy chọn
createProduct.jsp
listProduct.jsp

ở đây ta dùng <spring:message code="application.name" /> được hỗ trợ bởi spring, khai báo taglib là <%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> để lấy message theo code được khai báo trong properties
Bây giờ sẽ cấu hình để ứng dụng có thể load các label theo key mà ta đã định nghĩa trong file properties
mở file config: /WEB-INF/spring/appServlet/servlet-context.xml
thêm vào đoạn sau:
 
 
  
 
 
 
  
 
 
  
  
 
 

bean localeChangeInterceptor sẽ xử lý yêu cầu về load resource theo ngôn ngữ được lựa chọn, dựa vào parameter là "lang"
bean localeResolver sẽ lưu ngôn ngữ hiện tại vào cookie của trình duyệt, ngôn ngữ mặc định đây là "en" (tùy chọn)
và bean messageSource sẽ load file properties được định nghĩa trong WEB-INF/i18n/, các file được load là application*.properties.
sau khi Run As project
vào theo các url sau để xem kết quả
http://localhost:8080/store/product/create?lang=en
http://localhost:8080/store/product/create?lang=vi
source code
https://www.mediafire.com/?skzzpktm4c5pmwm
Thks & rgds!

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

Xử lý form trong Spring MVC

Trong phần này ta sẽ làm ví dụ xử lý dữ liệu lấy từ form ở trang JSP.
File->new->Spring project->Spring MVC Project
cấu trúc thư mục sẽ như sau:

Nội dung:
Sử dụng domain sau để lưu trữ thông tin của Product
package vn.ds.store.domains;

import java.io.Serializable;

public class Product implements Serializable {
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private String name;
 private String price;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getPrice() {
  return price;
 }

 public void setPrice(String price) {
  this.price = price;
 }
}


Chương trình sẽ lấy dữ liệu khi người dùng nhập vào

 và in ra danh sách


dữ liệu nhập vào sẽ được validate như hình sau

Đầu tiên ta xây dựng file properties để lưu trữ thông báo cho chương trình.
file product-messages.properties sẽ được tạo ra ở thư mục src/main/resources

các parameter được tryền vào theo các vị trí {0} {1}...
Để spring đọc được file resources này ta định nghĩa thêm 1 bean  là thể hiện của lớp org.springframework.context.support.ReloadableResourceBundleMessageSource để đọc messages.
vào file WEB-INF/spring/appServlet/servlet-context.xml thêm đoạn sau:

  
 

Toàn bộ file servlet-context.xml sẽ như thế này:


 
 
 
 

 
 

 
 
  
  
 
 
 
 
 
  
 
 



Xong phần cấu hình, giờ sẽ xây dựng một lớp ProductValidator để valid dữ liệu từ người dùng. Vì spring đã hỗ trợ validate trong interface Validator nên class của ta chỉ việc implements giao diện đó và triển khai phương thức validate() của Validator interface.
package vn.ds.store.validators;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import vn.ds.store.domains.Product;

public class ProductValidator implements Validator {

 @Override
 public boolean supports(Class clazz) {

  return Product.class.isAssignableFrom(clazz);
 }

 @Override
 public void validate(Object target, Errors errors) {
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name",
    "msg.required", new Object[] { "name" });
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "price",
    "msg.required", new Object[] { "price" });
  Product product = (Product) target;
  String name = product.getName();
  int max = 6;
  if (!name.isEmpty() && name.length() > max)
   errors.rejectValue("name", "msg.maxlength", new Object[] { "name",
     max }, "");
  if (!isNumber(product.getPrice().trim()))
   errors.rejectValue("price", "msg.number", new Object[] { "price",
     max }, "");
 }

 public boolean isNumber(String s) {
  for (int i = 0; i < s.length(); i++) {
   if (!Character.isDigit(s.charAt(i)))
    return false;
  }
  return true;
 }

}


Phương thức
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name",
    "msg.required", new Object[] { "name" });
sẽ kiểm tra xem trường "name" có emty hay có space không, nếu có sẽ add thêm một message vào đối tượng errors, nội dung msg sẽ được lấy theo id của msg trong resources là "msg.required" và truyền vào một parameter cho msg đó là một mảng Object có 1 phần tử là "name". Tương tự phương thức
errors.rejectValue("price", "msg.number", new Object[] { "price",
     max }, "");
cũng sẽ add thêm một msg của trường "price" vào errors theo msg là "msg.number" và set vào 2 parameter là một biến String "price" và một biến Integer max
Controller
package vn.ds.store.controllers;

import java.util.ArrayList;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import vn.ds.store.domains.Product;
import vn.ds.store.validators.ProductValidator;

@Controller
@RequestMapping("product")
public class ProductController {

 private ProductValidator validator = new ProductValidator();

 @RequestMapping(value = "create", method = RequestMethod.GET)
 public String doGet(Model model) {
  model.addAttribute("product", new Product());
  return "createProduct";
 }

 @RequestMapping(value = "save", method = RequestMethod.POST)
 public String doPost(@ModelAttribute Product product,
   Model model, BindingResult errors) {
  validator.validate(product, errors);
  if (errors.hasErrors()) {
   return "createProduct";
  }
  ArrayList lst = new ArrayList();
  Product p = new Product();
  p.setName("product 1");
  p.setPrice("100");
  lst.add(p);
  lst.add(product);
  model.addAttribute("products", lst);
  return "listProduct";
 }
}


Ở đây Phương thức GET của url "/product/create" sẽ được xử lý bởi hàm doGet(). hàm này sẽ tạo mới một đối tượng Product và add vào model. sau đó trả về một view có tên là "createProduct". view này là view jsp được định nghĩa trong file servlet-context.xml. vì ta không định nghĩa cụ thể nên spring sẽ xem id của view theo tên file jsp. Phương thức POST của url "/product/save" sẽ xử lý khi người dụng submit form trong method doPost()
Đầu tiên sẽ validate dữ liệu theo đối tượng ProductValidator. nếu có lỗi sẽ trả về view "createProduct" là trang nhập dữ liệu
Nếu không có lỗi sẽ thực hiện add dữ liệu vào model và gửi xuống view "listProduct".
JSP
file createProduct.jsp trong thư mục WEB-INF/views sẽ như sau:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>




Insert title here



 
  
create
Name
Price
 

và view hiển thị danh sách listProduct.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>   




Insert title here



 
List of products
Name Price
${p.name} ${p.price}

Source code theo link http://www.mediafire.com/download/vki3lvwpsqtpam8/Store_handling_form.rar Thanks & Rgds!!!

Thứ Tư, 25 tháng 12, 2013

Hướng dẫn sử dụng Spring Tool Suice (STS)

Spring Tool Suice (STS) là một plugin cho Eclipse hỗ trợ dev phát triển các project dựa trên Spring framework
http://spring.io/tools/sts
Bài viết này sẽ hướng dẫn cách sử dụng STS
Trên thanh công cụ của Eclipse->Help->EclipseMarketplace
Gõ vào ô tìm kiếm với từ khóa STS
Chọn STS phù hợp với phiên bản Eclipse-> Install


Sau khi eclipse hoàn tất cài đặt và khởi động lại
cần phải bỏ valid derived query để tránh các cảnh báo lỗi không cần thiết
Window->Preferences->Spring.
ở tab Project validators->Data validator và bỏ check  ở Invalid Derived Query-> OK

Create một project Spring web MVC

File->New->Spring Project->Spring MVC Project

Nhập tên package và Finish



STS sẽ tự động tạo một project mẫu với một controller Home

Nếu Project bị lỗi, là do version của spring đó không còn maven không hỗ trợ để load thư viện về.
để sửa bạn vào file pom.xml. chọn properties của spring và thay bằng version khác (có thể tham khảo tại http://mvnrepository.com/artifact/org.springframework)


Sau đó save lại, và chọn Project->clean.
bây giờ click phải chuột vào Store->Run As
và xem kết quả!

Bây giờ hãy xem lại example vừa được tạo ra
cấu trúc thư mục sẽ như thế này

Một ứng dụng spring MVC sẽ theo đúng mô hình MVC chuẩn, ta quan tâm đến các file sau: HomeController.java, web.xml, servlet-context.xml

HomeController.java
package vn.ds.store;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
 
 private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
 
 /**
  * Simply selects the home view to render by returning its name.
  */
 @RequestMapping(value = "/", method = RequestMethod.GET)
 public String home(Locale locale, Model model) {
  logger.info("Welcome home! The client locale is {}.", locale);
  
  Date date = new Date();
  DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
  
  String formattedDate = dateFormat.format(date);
  
  model.addAttribute("serverTime", formattedDate );
  
  return "home";
 }
 
}


Controller này sẽ xử lý request cho ull "/"  và trả về một view có tên là "home"

web.xml


 
 
  contextConfigLocation
  /WEB-INF/spring/root-context.xml
 
 
 
 
  org.springframework.web.context.ContextLoaderListener
 

 
 
  appServlet
  org.springframework.web.servlet.DispatcherServlet
  
   contextConfigLocation
   /WEB-INF/spring/appServlet/servlet-context.xml
  
  1
 
  
 
  appServlet
  /
 
Ứng dụng Spring sẽ được xoay quanh một servlet trung tâm là DispatcherServlet, các request khi yêu cầu đến ứng dụng đều được servlet này xử lý sau đó lựa chọn controller xử lý và trả về model and view nào phù hợp
Các request sẽ được xử lý theo url-mapping là "/"
ở đây bạn có thể sửa url để DispatcherServlet xử lý  như .html, .do, .xxx ....
Servlet-context.xml


 
 
 
 

 
 

 
 
  
  
 
 
 
 
 
 


Dòng <annotation-driven /> sẽ cho phép sử dụng các @notation, đây là một đặc điểm của spring kể từ spring 3 trở đi
các phương thức GET yêu cầu resources(image, js, css..) sẽ được khai báo bởi dòng
<resources mapping="/resources/**" location="/resources/" />
Khai báo viewResolver là jsp ở dòng
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
Có thể khai báo nhiều viewResolver khác như pdf, excel...
Cuối cùng dòng
<context:component-scan base-package="vn.ds.store" />
sẽ cho phép quét các class được khai báo là controller (bởi anotation @Controller) ở trong package được chỉ ra, ở đây là vn.ds.store
Tương tự bạn có thể tạo ra nhiều controller khác  để xử lý các nghiệp vụ khác nhau trong package đó, và tạo file jsp trong /WEB-INF/views/ để hiển thị.

Thks & Rgds!