Thứ Sáu, 23 tháng 8, 2013

Tại sao spring bean lại mặc định là singleton bean ?


Spring bean có hai scope quan trọng là singleton và prototype. Singleton bean là những bean được tạo ra vào thời điểm khởi tạo spring context. Bean này được tạo ra đúng một lần trong suốt thời gian hoạt động của application. Bất cứ khi nào có yêu cầu inject thì instance đã có của bean này sẽ được lôi ra sử dụng. Prototype bean là những bean được tạo ra bất cứ khi nào có yêu cầu inject. Khác với singleton bean, bạn sẽ có nhiều instance của prototype bean trong spring context.

Việc tạo instance của bean cũng tiêu tốn performance của application nhưng đó không phải là lý do chính. Singleton bean cũng đồng thời là stateless bean - Bean không có state ( Class không có instance field ). Những tác động của mỗi request lên bean sẽ không được bean ghi nhớ lại. Nếu stateless bean có state thì đó phải là shared state - Bất cứ request nào tác động đến bean cũng cần state này. Hầu hết web application của bạn sử dụng các stateless bean. Mô hình của bạn thường gồm các tầng action -> service -> dao -> session factory. Bạn chỉ sử dụng method mà object của mỗi tầng cung cấp, bạn chẳng làm thay đổi state nào cả. Bản thân object của mỗi tầng cũng đâu chứa state nào. Singleton cộng với stateless thì sẽ không gây ra các vấn đề về concurrency. Vì nhu cầu chung là sử dụng stateless nên mặc định spring bean là singleton. 

Những prototype bean sẽ được sử dụng cho các stateful bean. Mỗi instance của bean sẽ được tạo ra cho một request. Do bean không dùng chung nên các tác động lên bean của mỗi request được giữ lại trong state của bean. Vì là new instance bean per request nên cũng sẽ không có vấn đề về concurrency.

Tại sao struts action lại được thiết kế non thread-safe ?


Lý do đơn giản là những lập trình viên thiết kế struts mong muốn sử dụng lại action class đã được tạo ra. Không tạo mới instance. Sử dụng chung một instance cho tất cả các thread ( Thread ở đây chính là request người dùng đến action. Mỗi request sẽ được xử lý trong một thread ). Điều này khiến action class non thread-safe. Vấn đề sẽ xuất hiện khi trong action class, bạn khai báo thêm các mutable instance field và mutable static field. Bị tác động từ nhiều thread sẽ khiến các trường giá trị này thay đổi mà bản thân lập trình viên không thể tiên đoán được. Chính vì vậy, trong struts action 1, mọi dữ liệu được đẩy lên action sẽ được truyền vào qua tham số. 

Struts action 1 là non thread safe. Nhưng struts action 2 lại là thread safe. Bạn sẽ sinh ra instance của action class cho mỗi thread. Nhờ đó bạn có thể nhận dữ liệu từ request đẩy lên action qua các mutable instance field của struts action class 2.

Thứ Hai, 19 tháng 8, 2013

How to turn on/off mysql server ?


How to turn on mysql server ?

sudo service mysql start

Hoặc

sudo mysqld

How to turn off mysql server ?

sudo service mysql stop

Hoặc

sudo mysqladmin -u root -p shutdown

How to disable mysql at startup in Linux ?


Bạn có nhiều cách để disable mysql tại thời điểm startup. Trong bài viết này, mình trình bày cách đơn giản nhất mình biết.

Bạn di chuyển đến thư mục /etc/init. Trong thư mục này, bạn sẽ thấy file cấu hình của mysql có tên là mysql.conf Để disable mysql, bạn không cần sửa file cấu hình này. Đơn giản nhất, bạn chỉ cần tạo một file có tên là /etc/init/mysql.override có nội dung là manual

Reset root passwd của mysql trên Linux


Giả sử vào một ngày đẹp trời, bạn quên mất root passwd của mysql trên Linux. Bạn sẽ làm gì đây ?

Version mysql mình đang dùng: 5.5.24

Bạn có hai cách để xử lý:
Cách thứ nhất khởi động mysql bằng mysql_safe. 
Cách thứ hai, khởi động mysql bằng mysqld nhưng kèm theo tham số --skip-grant-tables.

Mình sẽ trình bày từng cách một. Với cả hai cách, bạn đều phải tắt mysql service đi:
sudo service mysql stop
hoặc một cách thô bạo hơn, bạn có thể kill mysql service
sudo kill -9 /var/run/mysqld/mysqld.pid


Khi mysql của bạn đang hoạt động, nó sẽ tự động sinh ra một file mysqld.pid chứa pid của mysql process. Bạn có thể tìm thấy pid của mysql trong file này hoặc tìm qua netstat
sudo netstat -apn | grep 3306

Trong cách thứ nhất. Bạn cần chuẩn bị một script file có nội dung như sau:


UPDATE mysql.user SET Password=PASSWORD('abc') WHERE User='root';
FLUSH PRIVILEGES;

Bạn có thể lưu file này với đuôi mở rộng bất kỳ, có thể đặt bất kỳ đâu trong file system. Mình lưu file này dưới tên mysql-init. Sau đó, bạn sẽ dùng mysqld_safe để gọi thực hiện script file này. Mysqld_safe cũng tương tự mysqld. Bản thân mysqld_safe sẽ gọi mysqld để hoàn tất công việc của nó. Điểm khác biệt ở chỗ mysqld_safe có hỗ trợ chạy script file và log error. 
sudo mysql_safe --init-file=<absolute-path-to-script-file>/mysql-init --log-error=<absolute-path-to-log-error-file>/error_mysql_log

Một file error_mysql_log.err sẽ được sinh ra khi bạn chạy câu lệnh trên.

(*) Rất có thể khi bạn thực hiện câu lệnh mysqld_safe, bạn sẽ gặp lỗi  
[ERROR] /usr/sbin/mysqld: File '<absolute-path-to-script-file>/mysql-init' not found (Errcode: 13)

Lỗi này là do mysqld không có quyền đọc mysql-init. Để điều chỉnh, bạn thay đổi trong file cấu hình: /etc/apparmor.d/usr.sbin.mysqld
sudo vi /etc/apparmor.d/usr.sbin.mysqld

Bạn bổ sung thêm dòng này vào file trên:

<absolute-path-to-script-file>/mysql-init r

Sau đó, bạn reload lại apparmor:

sudo /etc/init.d/apparmor reload

Sau khi khởi động mysql service bằng mysql_safe. Bạn thực hiện truy cập vào mysql service với mật khẩu mới cho root account.
mysql -u root -p

Trong cách thứ hai. Bạn có thể thực hiện việc reset nhanh chóng hơn.

Sử dụng tham số --skip-grant-tables để cho phép khởi động mysql service mà không cần qua các bước xác thực khi truy cập.
sudo mysqld --skip-grant-tables

Sau đó bạn truy cập vào mysql service mà không cần password. Password không cần nhập.
mysql -u root -p

Sau khi truy cập thành công, bạn sử dụng các command tương tự trong đoạn script mysql-init trong cách một để reset password của root.

Chủ Nhật, 18 tháng 8, 2013

Cơ chế đánh index của database


Bài viết dựa theo những thông tin thu nhận được từ trang:
http://use-the-index-luke.com/

SQL index là kỹ thuật tuning SQL quan trọng. Index nói chung giúp gia tăng tốc độ query database lên. Mình sẽ nêu từng đặc điểm quan trọng của index và phân tích mỗi đặc điểm.

Index là một cấu trúc riêng biệt được tạo ra và tham chiếu đến dữ liệu thực sự nằm trong table. Như vậy, sử dụng index yêu cầu cần disk space để chứa cấu trúc của nó. Index không làm thay đổi cấu trúc dữ liệu của table. Nó chỉ tham chiếu đến dữ liệu đang nằm trong table. Index rất giống phần index nằm ở những trang cuối của một cuốn sách. Bạn cần thêm giấy cho các trang index, các index đó lại trỏ đến các mục trong cuốn sách. Rất giống nhau phải không nào. 

Lý do index khiến tốc độ query tăng lên vì dữ liệu index được tổ chức sắp xếp hợp lý theo một trình tự còn dữ liệu thực sự trong table là dữ liệu không có thứ tự nên không tiện cho tìm kiếm. Việc tìm kiếm qua index cũng giống như tra số điện thoại trong danh bạ nhưng không hoàn toàn giống. Dữ liệu trong danh bạ không thể cập nhật nhanh chóng được vì không có không gian trống giữa các index. Bạn không thể dễ dàng chèn thêm/xóa bớt một index vào danh bạ được. Với danh bạ điện thoại, index chỉ có thể cập nhật trong lần xuất bản tiếp theo. Đây là điểm khác biệt với index của database. Index của database cần được cập nhật nhanh chóng khi có các sự kiện insert, update, delete trên table. Để đáp ứng yêu cầu này, index của database được tổ chức theo hai cách: doubly linked list - danh sách hai chiều và search tree - cây tìm kiếm.

Các câu lệnh linux thường sử dụng


1. Mount
sudo mount -t type device|partition dir

type là file system type của device
device là tên device đó, partition là tên phân vùng trên một device
dir là mount point

Ví dụ: mount -t ntfs /dev/sda1 /media/

2. Umount
sudo umount dir|partition|device

dir là mount point
device là tên device cần gỡ bỏ khỏi tree directory
partition là tên partition cần gỡ bỏ khỏi tree directory

Ví dụ: umount /media/

3. blkid
Xem danh sách các phân vùng đang được gắn vào máy tính cùng với UUID của từng phân vùng:
sudo blkid

Ví dụ kết quả có được:

/dev/sda1: LABEL="SOMETHING" UUID="xxxx" TYPE="ntfs" 
/dev/sda2: UUID="xxxx" TYPE="swap" 
/dev/sda3: UUID="xxxx" TYPE="ext4" 
/dev/sda4: UUID="xxxx" TYPE="ext4"

 Không phải tất cả các phân vùng được liệt kê trong danh sách trên đều đang được mount.

4. fdisk -l
Lấy thông tin về toàn bộ các disk, phân vùng của từng disk
sudo fdisk -l

5. pwd
Lấy thông tin về thư mục hiện thời
pwd

To be continue....

Thứ Sáu, 16 tháng 8, 2013

Với oracle, sử dụng hibernate generator hay trigger để tăng giá trị sequence ?



Bạn đều biết mỗi một table đều cần một trường đóng vai trò làm primary key. Giá trị của trường này luôn là duy nhất cho mỗi record. Trường này sẽ làm nhiệm vụ phân biệt mỗi record là duy nhất trong table, sẽ không thể có hai record mà có các giá trị hoàn toàn giống nhau được. Để thực hiện điều đó thì primary key thường là kiểu số và được database hỗ trợ tăng tự động. Khi thực hiện insert, bạn không cần nhập giá trị cho trường này. Database sẽ ngầm định thực hiện tăng giá trị số này lên, đảm bảo các record khác nhau sẽ có số khác nhau. Mỗi database lại có cách xử lý cột số này khác nhau. Oracle sẽ sử dụng một đối tượng sequence. Đối tượng này không có liên hệ gì đến table mà nó sẽ được sử dụng. Bạn chỉ biết đối tượng này thuộc schema nào mà thôi. Khi bạn cần tăng số tự động cho cột PK thì sử dụng câu SQL tương tự như sau:


INSERT INTO Orders_tab (Orderno, Custno)
VALUES (Order_seq.NEXTVAL, 1032);

Với Order_seq là một đối tượng sequence được tạo ra trước đó. Với ms sql hay mysql, khi khai báo table, bạn đã xác định rõ cột nào nhận giá trị tăng tự động rồi. Với ms sql, bạn khai báo cột đó là IDENTITY còn với mysql, bạn khai báo cột đó là AUTO_INCREMENT. Khác với oracle, bạn không cần khai báo những cột đã được xác định tăng giá trị tự động vào câu lệnh insert.

Xét riêng oracle, nếu bạn không muốn gọi nextval trong câu lệnh insert, bạn có thể dùng trigger để thực hiện tăng số tự động.

Khi làm việc với hibernate, một ORM rất phổ biến, để các thao tác thêm dữ liệu qua các mapping object thực hiện được mà không cần quan tâm đến số tăng tự động, bạn cần khai báo generator cho trường bạn chọn làm id của bảng. Generator sẽ xác định cơ chế tăng số tự động tương ứng với từng database. Hibernate sẽ thực hiện tăng số tự động giùm bạn như sau:

  • Bước 1: Hibernate sử dụng cơ chế generator để tạo giá trị mới. 
  • Bước 2: Hibernate gán giá trị mới vào trường chọn làm id của mapping object.
  • Bước 3: Hibernate chuyển hóa câu lệnh save thành insert với trường id nhận giá trị mới được sinh ở bước 1.
  • Bước 4: Nhờ có bước 1 và bước 2 mà bạn có thể lấy được giá trị id mới được tạo ra của object.

Trong trường hợp bạn sử dụng trigger để tăng giá trị cho cột số tự động (sử dụng database oracle), bạn không cần khai báo generator cho mapping object, bạn vẫn có thể thực hiện save object thành công. Nhưng hai cách làm đó khác gì nhau ?

  • Sử dụng trigger, câu lệnh insert của bạn sẽ không có giá trị cho cột số tự động, trigger sẽ làm việc đó. Bạn không thể lấy được ngay id vừa tạo ra qua object. Bạn cần thực hiện tiếp một query nữa để lấy ra id đó.
  • Sử dụng generator, hibernate đã lấy được giá trị mới cho id từ bước 1, nó sử dụng giá trị này cho câu lệnh insert luôn.  Bạn có thể lấy ngay id vừa tạo ra qua object. Không cần thêm bất cứ query nào cả.

So sánh thì thấy sử dụng generator rất hiệu quả. Nhưng nếu bạn lập trình sql script cho mục đích học hỏi thì dùng trigger là lựa chọn tốt.

Thứ Hai, 12 tháng 8, 2013

Tại sao nói static method không phải là khái niệm thuần túy object oriented ?



Bởi vì bạn không cần tạo ra một instance class để sử dụng static method. Thêm nữa là static method không làm việc với một số khái niệm đặc trưng của OOP. Bạn không thể định nghĩa static method trong abstract class hay interface. Bạn cũng không thể override một static method. Thực sự thì static method là procedure programming style.

Static method có phải là bad practice hay không ? Nếu bạn sử dụng đúng thì không hề. Bad practice là những cách lập trình khó hiểu, khó sử dụng, dễ gây ra các duplicate code, khó maintain, reuse, vi phạm các nguyên tắc thừa kế. Bản thân static method rất dễ khiến lập trình viên sử dụng nó bừa bãi trong chương trình. Một ví dụ về bad practice là bạn thực hiện gọi static method qua một instace class. Hoàn toàn được nhưng khó hiểu về lý do sử dụng như vậy. Sau đây là một số chỉ dẫn để bạn cân nhắc xem liệu có nên sử dụng một static method hay không

Thứ nhất, method đó có cần sử dụng các instace variable hay không ? Bạn biết đấy không thể dùng instance variable trong static method

Thứ hai, nếu một số instance method sử dụng chung một đoạn code thì bạn nên đặt đoạn code đó trong static method

Thứ ba, method đó có cần override hay không ? Bạn biết là static method không thể override

Thường thì static method được sử dụng để viết util class của bạn. Bạn có thể xem Math hoặc commons.lang, tất cả các method đều là static

Nói chung, static method có hai hạn chế là: Vi phạm các nguyên tắc OOP và có nhiều trở ngại khi test các static method bằng các test tool.

Chủ Nhật, 11 tháng 8, 2013

Spring AOP


Kinh nghiệm lập trình chỉ ra rằng, để hoàn thành một chương trình, bạn không chỉ phải giải quyết các business logic thỏa mãn được yêu cầu khách hàng mà con cần giải quyết những logic bắt buộc khác. Những logic này tuy không thể hiện các business rules, business processes của khách hàng nhưng lại luôn xuất hiện trong mọi chương trình. Vài ví dụ như: login/logout, validation, authentication và authorization, logging, trail log... Những thành phần vừa nêu giữ cho chương trình của bạn bền vững. Rắc rối ở chỗ nó xuất hiện ở nhiều nơi trong chương trình nên việc lập trình nó sẽ dẫn đến duplicate code nhiều nơi trong chương trình. Đây là một thiết kế xấu. Những code của các thành phần như vậy gọi là concern hoặc cross cutting code. Lập trình viên chúng ta luôn muốn viết code một lần mà chạy được nhiều nơi, độc lập để dễ tái sử dụng. Spring AOP sẽ giải quyết cho chúng ta các cross cutting code này. 

Mình sẽ trình bày một số khái niệm căn bản của spring AOP. Khi làm việc với Spring AOP bạn sẽ thấy các khái niệm này được sử dụng rất nhiều lần.


  • Join point: Đây là một method execution sẽ được cài aspect. Aspect sẽ được cài vào trước, sau hoặc xung quanh join point
  • Point cut: Đây là một expression cho phép Spring AOP match được aspect nào sẽ áp dụng cho join point nào.
  • Advice: là hành động thực hiện bởi aspect, chính là cross cutting code bạn muốn cài. Có ba loại advice chính: before advice, after advice và around advice. Around advice làm được tất cả mọi việc mà hai loại advice kể trên làm được nhưng theo nguyên lý thiết kế vừa đủ thì bạn chỉ nên dùng loại advice đáp ứng vừa đủ chính xác yêu cầu của bạn mà thôi.
  • Aspect: Hay còn gọi là advisor, là tập hợp của advice và point cut.
  • Target object: là object mà các method của nó sẽ được cài cắm các aspect. Target object không hề biết aspect được cài vào xung quanh nó. Kỳ diệu ở đây là nhờ đó bạn sẽ không phải chỉnh sửa target object.
  • AOP proxy: Là một object tạo bởi Spring AOP. Object này sẽ hình thành một lớp proxy chắn giữa method call và target object. Khái niệm proxy ở đây rất giống khái niệm proxy trong mạng máy tính. Vì nằm sau một proxy nên mọi target object còn được gọi là proxied object. Những advice sẽ được cài vào và gọi thực hiện trên proxy
  • Introduction: Đây là một new interface sẽ được khoác lên thêm cho target object. Spring AOP sẽ cung cấp implementation cho interface này. Tính năng này cho phép bạn bổ sung các interface mới cho target object mà bản thân object đấy lại không hề biết, nghĩa là bạn không cần chỉnh sửa code trong target object mà vẫn thêm tính năng cho nó.
  • Weaving: Là cách mà Spring AOP sẽ cài aspect vào join point. Những cross cutting code sẽ được chèn vào trước, sau hoặc xung quanh joint point. Cũng giống như các AOP framework khác, Spring AOP sẽ thực hiện weaving tại thời điểm runtime.

Để thực hiện các tính năng của mình, Spring AOP sẽ phải sử dụng aspectj nhưng là sử dụng không đầy đủ. Spring AOP support aspectj theo hai cách: @Aspectj hoặc schema-based. Kỹ thuật này chỉ đòi hỏi Spring AOP sử dụng aspectj để matching và compiling còn weaving thì Spring AOP có cơ chế riêng. Tất nhiên Spring AOP vẫn có cho phép sử dụng full-feature aspectj.

Có rất nhiều điểm spring AOP khác biệt với aspectj


  • Spring AOP sử dụng proxy để chặn method call và thực hiện advice trên đó còn aspectj thì có cơ chế khác hoàn toàn, không sử dụng đến proxy. Cũng vì dùng proxy mà Spring AOP không thể chặn được những self method call qua this vì những method call này xuất phát từ trong target object. Không có cản trở tương tự trên aspectj
  • Spring AOP không hỗ trợ đủ các designator của aspectj
  • Spring AOP hỗ trợ bean designator mà không có trong aspectj
  • Spring AOP có cơ chế weaving riêng không sử dụng cơ chế của aspectj

Bản chất của Spring AOP là proxy. Một proxy object sẽ bao lấy target object và chặn lời gọi đến method trong target object. Spring AOP có sử dụng hai loại proxy: JDK Dynamic proxy và CGLIB proxy. Loại CGLIB proxy nằm sẵn trong thư viện spring core, bạn không cần tìm kiếm thư viện cglib làm gì cả. Còn loại JDK Dynamic proxy thì nằm sẵn trong thư viện java core. Spring AOP sẽ chọn loại proxy được sử dụng tùy theo target object. Nếu target object implements interface thì JDK Dynamic proxy được dùng còn nếu ngược lại thì là CGLIB proxy. Spring AOP sẽ tự động tạo proxy lên các object được pointcut match khi bạn enable aspectj support. Tất nhiên Spring AOP cũng cho phép bạn tự định nghĩa proxy object sử dụng.

Thứ Bảy, 10 tháng 8, 2013

Làm sao browser duyệt được file JSP ?


Đây là một câu hỏi mà mình gặp khá nhiều bạn hỏi. Thực tế là không có browser nào hiểu được file JSP. Khi gặp file JSP thì browser sẽ coi đó là một file cần được download xuống vì nó không biết cách để hiển thị file đó. 

Thế tại sao dùng browser truy cập đến các file jsp vẫn lấy được nội dung về. Thực ra cái mà browser lấy về khi đó không phải là JSP mà là một trang HTML. JSP là file chứa các thành phần động, các thành phần đó cần được server biên dịch để trả về ở dạng HTML. Trường hợp mình nói ở trên khi browser không hiển thị mà tải JSP về thì có lẽ do server không dịch gì cả.

Quá trình biên dịch này diễn ra như sau:

  • Request đến trang JSP tới server 
  • Server được cài đặt một JSP container như apache tomcat chả hạn sẽ thực hiện biên dịch JSP ra byte code ở dạng các file class. Bạn có thể thấy các file class này trong thư mục work của tomcat. 
  • Các byte code sau đó được diễn dịch để trả về trang HTML (mọi thành phần động đều được diễn dịch hết thành mã html tương ứng) cho client.

apache tomcat mình nhắc đến trong bài là một web container chứ không phải apache server - đây lại là một http server thuần túy.

Cơ bản thì cả hai đều hiểu và điều tác được http request/http response nhưng khả năng điều tác của http server cao hơn một web container. Bù lại, apache tomcat lại hiểu và biên dịch được file jsp còn apache server thì không thể. Chính vì sự khác biệt này mà các kỹ sư thường tích hợp apache tomcat vào apache server để tận dụng ưu điểm của cả hai.

Tích hợp Spring với Struts 1


Spring là một framework tuyệt vời, thậm chí một phần của spring là spring MVC cũng thừa sức thực hiện mọi tính năng mà struts cung cấp. Thế thì mắc công gì mà tích hợp, xài luôn spring MVC đi cho rồi. Nhưng thực tế là chúng ta có nhiều dự án đã triển khai là struts rồi. Và sau đó, chúng ta phát hiện ra Spring thật tuyệt vời, chúng ta muốn sử dụng các tính năng quản lý tùy biến hấp dẫn mà Spring đem lại nên chúng ta mới cần tích hợp.

Ở đây mình sẽ trình bày sơ lược cách tích hợp struts 1.3 vào spring 3.2.3

Đầu tiên, bạn cần tìm cách chuyển quyền điều khiển các action từ struts sang cho spring. Bạn sẽ phải định nghĩa một ContextLoaderPlugin trong struts-config.xml


<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
       <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml"/>
</plug-in>

Dựa vào cấu hình này, action servlet sẽ tìm được vị trí của application context.

Tiếp theo, bạn khai báo type của các action trong struts-config.xml là DelegatingActionProxy

<action path="/user" type="org.springframework.web.struts.DelegatingActionProxy" name="userForm" scope="request" validate="false" parameter="method">
         <forward name="list" path="/userList.jsp"/>
         <forward name="edit" path="/userForm.jsp"/>
</action>

Cuối cùng, bạn chỉ việc khai báo các bean có id hoặc name tương ứng với path của action trong applicationContext.xml và class là trỏ đến class thực sự của action này.

<bean name="/user" scope="prototype" class="org.example.web.UserAction"/>

scope là prototype có nghĩa là bean này sẽ được tạo ra khi có yêu cầu, chấp nhận tạo bean này nhiều lần trong container.

Spring core


Spring là framework rất mạnh. Nếu bạn chưa học sử dụng nó thì bạn đang không tận dụng những lợi ích tuyệt vời mà nó đem lại. Spring có thể được cài đặt và sử dụng trong bất cứ dự án java nào: desktop application hay web application.

Spring sử dụng một nguyên lý cơ bản là dependency injection. Mỗi object của bạn thường không đứng độc lập. Trong các dự án cỡ enterprise application, mỗi object lại thường có nhiều tham chiếu đến các object khác. Những tham chiếu này gọi là dependency. Để sử dụng object, bạn cần khởi tạo object đó, chuyện này tưởng chừng dễ dàng nhưng trong một dự án lớn, số object nhiều và liên kết với nhau sẽ làm bạn khó khăn để xử lý. Spring sẽ làm giúp việc này cho bạn. Và kết quả là bạn có thể sử dụng ngay object khi chương trình chạy mà bạn không cần tốn công viết những dòng code để khởi tạo object đó.

Nguyên lý dependency injection còn gọi là IoC - Inversion of Control. Lý do gọi là như vậy vì quá trình khởi tạo các bean (Những object nằm dưới sử điều khiển của spring gọi là bean, những bean này nằm trong một container) được thực hiện khi khởi tạo spring container, trước khi application hoạt động. Điều này trái với lệ thường.

Điểm mạnh của Spring là nó chấp nhận cài đặt dependency tuân theo một interface cho một bean. Điều này giúp cho cấu hình dependency cho một object trở nên linh động hơn. Bạn có thể tùy ý cài đặt các implementation khác nhau cho bean đó.

Các infrastructure object như SessionFactory đều được cấu hình như vậy trước khi sử dụng.

Spring container còn gọi là Spring IoC container hay provider được thể hiện dưới dạng một object có tên là ApplicationContext. Mọi thông tin cấu hình cho các bean đều mặc định nằm trong file cấu hình có tên là applicationContext.xml

Khi nào rảnh, chúng ta sẽ đi sâu vào cách spring quản lý các beans.

Mẹo nhỏ khi viết web client side


Code web ở client side bao gồm html, javascript và css. Vài mẹo nhỏ sẽ giúp nâng cao hiệu năng của web.

Tip 1: Luôn khai báo css ngay trước thẻ đóng của head </head> vì browser sẽ thực hiện các lệnh html ở trong thẻ này trước tiên. Bạn luôn muốn trang web của bạn khi được render trên browser đã có đầy đủ style.

Tip 2: Luôn khai báo javascript trước thẻ đóng của body </body> trừ một số trường hợp đặc biệt thì mới đặt javascript trước thẻ </head>. Lý do là vì javascript chỉ dùng để xử lý các sự kiện trên web page. Chỉ trừ các javascript dùng xử lý các sự kiện load page... cần được load trước khi thẻ <body> được đọc. Phần còn lại đều có thể delay cho đến khi nội dung của body được load hết. Điều này sẽ giúp cho người dùng không phải mất công đợi trình duyệt load thêm một đống javascript.

Tip 3: Luôn luôn đặt javascript và css vào trong file. Hạn chế viết javascript và css trực tiếp vào JSP, HTML. Mục đích của việc làm này là tận dụng khả năng cache của browser. Nếu để ý bạn sẽ thấy bất kỳ browser nào cũng tự động cache lại js, css file. 

Tip 4: Sử dụng ảnh nén. Dạng ảnh nén là png

Tip 5: Sử dụng dạng nén cho các thư viện js và css

Tip 6: Nếu js và css của bạn được lấy xuống từ CDN - Content Delivery Network thì bạn cần sử dụng địa chỉ IP của CDN thay vì dùng địa chỉ domain. Điều này sẽ loại bỏ được quãng thời gian phân giải địa chỉ tên miền.

Tip 7: Loại bỏ tất cả die link trong ứng dụng

Tip 8: Hi vọng người dùng bật tính năng cache của trình duyệt để các thư viện js và css được tự động lưu vào cache :D

Tip 9: Thực hiện nén html trả về. Bất cứ browser nào hiện thời cũng đều hỗ trợ nén gzip cả. 

Sẽ bổ sung thêm dần...

So sánh criteria, named query và hql


Hôm nay, mình trình bày với các bạn chút ít về criteria, named query và hql. So sánh chúng giúp bạn lựa chọn được công cụ đúng đắn trong mỗi trường hợp.

Muốn biết loại query nào tốt hơn trong trường hợp nào, chúng ta thử xem cách hibernate xử lý từng loại query. 

HQL: Hibernate sử dụng antlr - một công cụ phân tích cú pháp để parse câu HQL rồi xuất ra mã SQL thông thường và thực hiện. Mỗi lần bạn sử dụng, quá trình này lại lặp lại. 

Criteria: Hibernate không parse gì cả. Nó sẽ xuất ra mã SQL và thực hiện. Mỗi lần sử dụng, quá trình này lại lặp lại.

Named query: Khác với hai loại trên, đây là loại query mà bản thân câu query được viết tách rời với code. Nó thường nằm trong thẻ <hibernate-mapping> đằng sau các khai báo mapping, trong file xml chung hoặc riêng với các khai báo mapping. Hibernate chỉ parse và xuất mã cho named query một lần khi mapping file được cấu hình vào trong session factory. Cách làm này đem lại lợi thế về hiệu năng cho named query hơn hẳn so với hai loại query trên. Nhưng bất lợi ở chỗ, mọi thay đổi đến query đều cần bạn khởi động lại application để named query parse, gen lại rồi nạp lại vào session factory.

Nói chung, criteria sẽ thích hợp cho việc build các search query. Named query thích hợp cho các câu query ít phải thay đổi và dùng nhiều lần. HQL đơn giản hơn nên vẫn sẽ thích hợp cho các query không cần có nhiều điều kiện phức tạp và không được dùng quá nhiều lần.

Tại sao lại chỉ có một SessionFactory trong application context ?


Ở đây mình nói đến SessionFactory quản lý bởi Spring framework. Nhưng câu hỏi và câu trả lời tương tự cũng có khi Hibernate quản lý SessionFactory.

Câu trả lời ngắn gọn là để đảm bảo performance của ứng dụng. 

Câu trả lời chi tiết thì như sau:

SessionFactory là đối tượng chứa các thông tin quan trọng để thực hiện tạo kết nối đến database như: mapping, data source, các connection information khác... Do đó việc tạo đối tượng này rất tốn công. Đây chính là lý do chỉ có một session factory cho một application.

Để đảm bảo session factory chỉ được tạo ra một lần, các kỹ sư thiết kế đã sử dụng singleton pattern. Session factory còn là một đối tượng sử dụng chung trong toàn bộ application nên các kỹ sư cũng thiết kế để đảm bảo instance của nó là thread-safe. Nhưng session - đối tượng bạn lấy ra từ session factory lại là non-thread-safe.

Mở rộng chút:

Có thể application của bạn cần thao tác với nhiều database, điều này dẫn đến bạn cần nhiều data source. Khi đó bạn sẽ phải cần nhiều hơn một session factory nhưng dù gì thì bạn luôn chỉ tạo mỗi session factory một lần trong suốt chương trình Với Spring, Session factory sẽ được tạo ra ngay trong quá trình khởi động spring container. Đây là tính năng pre-installed cho các bean mà nằm dưới sự quản lý của spring (Session factory cũng chỉ là một bean nằm trong spring container). Tính năng này đảm bảo chuẩn bị tất cả các điều kiện cần thiết để chương trình của bạn hoạt động.

Bất cứ lúc nào bạn cần thao tác với database, bạn đều cần một tham chiếu đến Session factory. Từ session factory sẽ rút ra được session và qua session, bạn mới có thể thao tác với database. Thiết kế tốt cần đảm bảo mọi thao tác với database chỉ nên thực hiện tại tầng DAO - Data Access Object.

Tóm lại: Bạn chỉ có một session factory cho mỗi application và có một session cho từng client cần sử dụng database.

Thứ Bảy, 3 tháng 8, 2013

Git tutorial

Hôm nay mình sẽ giới thiệu cho các bạn về cách sử dụng git. 

Git là một distributed version control system rất phổ biến trong thế giới open source. Ban đầu nó được sử dụng làm VCS cho linux kernel development. Sau này, nó ngày càng được sử dụng rỗng rãi hơn, ngay cả trong việc phát triển các sản phẩm thương mại. Mình thì cũng chưa thấy nó có gì mạnh hơn so với tortoise - một VCS cũng khá phổ biến khác. Chắc dùng nhiều mới thấy hết được.

Version control system là gì ?

Là một hệ thống quản lý một tập hợp các file cùng với lịch sử lưu trữ và thay đổi trên các file đó. File không nhất thiết là source code nhưng thường thì người ta chỉ dùng VCS cho quản lý mã nguồn của các dự án công nghệ thông tin.

Repository là gì ?

Là thùng chứa các file và history của file. Một VCS thường có nhiều người dùng. Mỗi người dùng lại có thể có nhiều repository

Distributed version control system là gì ?

Không nhất thiết bạn chỉ một central server để lưu trữ repository. Bạn có thể copy repository sang máy khác. Việc làm này gọi là clone. Ví dụ đơn giản nhất là khi bạn checkout một remote repo. Khi đó bạn đã thực hiện clone remote repo vào một local repo trên máy của bạn. Local repo sẽ luôn duy trì liên kết ngược lại remote repo. Liên kết này gọi là origin.

Git là gì ?

Là một distributed VCS. Khi quản lý phiên bản dùng git, bạn sẽ luôn có một local repo. Local repo này có thể có một remote repo. Các remote repo thường nằm trong github. Local repo hiển thị trên file system của bạn ở dạng một folder. Trong local repo luôn có một folder tên là .git chứa thông tin về local repo và cả origin của repo này (nếu nó liên kết với remote repo). Tất cả những file và folder trong local repo không thuộc .git gọi là working tree.

Tiến trình làm việc ?

Bạn tạo một file hay folder bất kỳ trong folder chứa .git folder. File hay folder này gọi là unversion. Bạn cần đánh index cho các file hay folder thì git mới quản lý chúng được. File hay folder sau khi được đánh index sẽ gọi là versioned. 

Khi đánh index, một snapshot của local repo sẽ được chụp và lưu trữ trong staging area. Hành động đánh index và tạo snapshot này gọi là add. 

Cho đến lúc này, file hay folder chưa thuộc local repo. Bạn cần thực hiện commit. Khi commit, các snapshot sẽ được di chuyển từ staging area đến local repo và một commit object được tạo ra. Commit object này chứa thông tin về hành động commit.

Nhưng cho đến lúc này, file hay folder vẫn chưa nằm trên remote repo để bạn chia sẻ với nhiều developer khác. Bạn cần thực hiện push để đẩy những file hay folder đó lên remote repo.

Khi cần cập nhật thay đổi từ remote repo bạn thực hiện pull. 

Mỗi một repo của bạn dù là local hay remote đều có một default branch gọi là master. Mỗi branch sẽ chứa một tập hợp file và lịch sử riêng độc lập với các branch còn lại. Lợi ích của việc dùng branch là bạn có thể thêm tính năng chương trình trong các branch khác hoặc thực hiện fix bug trên một branch thay vì làm hết trong master. Sau khi hoàn tất thêm tính năng và fix bug, bạn có thể merge các thay đổi trên các branch đó vào master branch. Hành động chọn một branch gọi là checkout branch đó.

Git cũng cung cấp cho bạn chức năng so sánh giữa các branch: giữa master (của local repo) với origin/master (của remote repo).

Lệnh fetch là lệnh download dữ liệu từ remote repo về local repo nhưng chưa thực hiện merge thông tin đó lên working tree. Pull = fetch + merge

Revert là hành động restore snapshot từ staging area. Bạn chỉ có thể revert thay đổi trước khi commit thay đổi đó lên local repo vì như đã nói, commit sẽ làm di chuyển các snapshot từ staging area lên local repo.

Revision là phiên bản file và folder lưu trữ trong repo

Git còn hỗ trợ xóa tất cả các unversion file hay folder.

HEAD là tham chiếu luôn trỏ đến một commit object. Khi bạn commit, HEAD sẽ trỏ đến commit object vừa tạo ra. Khi bạn chuyển branch (switch branch), HEAD sẽ trỏ đến last commit của branch mới. Bạn có thể tham chiếu đến các commit object trước đó qua HEAD~1 hoặc HEAD~2. Sử dụng HEAD bạn có thể so sánh sự khác biệt giữa hai commit liên tiếp.

git diff HEAD~1 HEAD

Checkout: Là hành động chọn một branch hoặc tạo một local repo và đồng bộ một remote repo lên local repo.

Share Github: Là hành động bạn tạo một remote repo trên github và đồng bộ local repo lên github.

Fork: Một tính năng khá hay trong github. Có thể bạn muốn đóng góp sức lực cho một project nào đó hoặc muốn dùng một project nào đó đã có để làm điểm khởi đầu cho project của bạn. Để thực hiện điều đó, bạn có thể fork project. Project được fork sẽ được clone vào repository của bạn. Bạn thậm chí có thể propose (đề xuất) thay đổi trên một file đã có trong một project nào đó.

Thứ Năm, 1 tháng 8, 2013

Scrum vs Water fall


Bài này nhằm mục đích so sánh giữa hai quy trình scrum và water fall. Bạn có thể dễ dàng đoán được cái nào ưu việt hơn nhưng mình muốn đi sâu hơn, đánh giá xem ưu nhược điểm của quy trình và tính áp dụng của quy trình có khả thi trong thực tế không.

Water fall là quy trình truyền thống. Trong đó, mọi thứ được diễn ra theo một tuần tự duy nhất xuyên suốt toàn bộ dự án. 

Về cơ bản thì water fall bao gồm: thu thập yêu cầu, phân tích yêu cầu, thiết kế, phát triển, kiểm thử, phát hành rồi duy trì. Bước thu thập yêu cầu và phân tích yêu cầu thường do một đội có chức danh BA - Business Analysist đảm nhiệm. Khâu phân tích có thể có thêm các nhân sự bên thiết kế và phát triển. Kết quả của khâu này là  một tập tài liệu và đảm bảo mọi thành viên trong đội dự án từ BA, designer, developer, tester đều nắm được. Nhưng vấn đề ở chỗ việc hiểu cũng có nhiều mức. Nhiều khi mình cứ tưởng mình hiểu khách hàng nhưng hóa ra là mình lại chẳng hiểu gì về họ. Sai sót trong sự hiểu có thể đến từ chính khách hàng. Khách hàng hầu hết là dân kinh doanh, chẳng biết gì về kỹ thuật. Ở đây chúng ta có hai nhóm khách hàng và developer, designer có cách nhìn về một vấn đề rất khác nhau. Để ngăn ngừa sự hiểu lầm này mà ra đời BA là cầu nối giữa hai nhóm. BA do đó thường là một dân chuyên làm kỹ thuật, có hiểu biết về kinh doanh. Với các dự án onsite, BA cũng thường là BrSE - Bridge System Engineer. Trắc trở nữa là khách hàng đôi khi còn không hiểu họ cần gì. Họ hay nhầm lẫn giữa cái họ cần và cái họ muốn. Cái họ muốn thường xa vời, không đem lại lợi ích trực tiếp cho kinh doanh của họ. Một BA giỏi thì cần loại bỏ suy nghĩ về ham muốn này của khách hàng. 

Kể cả BA đã làm tốt vai trò của họ thì mọi chuyện cũng không đơn giản. Vì water fall chỉ diễn ra tuần tự một lần có nghĩa là bạn không thể dành hẳn thời gian để phân tích yêu cầu khi có sự thay đổi từ khách hàng. Tệ thay, điều này diễn ra thường xuyên. Vô hình chung, trong dự án xuất hiện những khoảng thời gian không được quản lý mà ở đó developer, designer phải họp bàn để tìm hiểu yêu cầu và tìm phương án. Nó không chỉ khiến độ tập trung của developer và designer suy giảm vì không biết lúc nào thì yêu cầu lại thay đổi lần nữa mà còn khiến cho project plan trở nên vô dụng vì đánh mất vai trò quản lý của nó. Dự án do đó dễ bị sa lầy. 

Một đặc điểm nữa của water fall là vai trò trách nhiệm trong đội dự án phân chia rất rạch ròi. Dự án water fall gồm các bước tuần tự, chưa xong bước này thì bước tiếp không làm được. Bạn có thể hình dung: designer chưa thiết kế xong thì developer cũng không code, tester viết test case xong thì đợi cho developer code xong thì test. Dù dự án rất gấp nhưng đặc thù này khiến nhân lực trong đội không hoạt động 100% trong suốt dự án.

Cuối cùng, ác mộng của nhiều developer là viết tài liệu bàn giao lại xuất hiện rất nhiều vào cuối dự án. Dự án water fall đòi hỏi khá nhiều tài liệu. Mình lúc đầu cùng sùng bái tài liệu nhưng sau này mình thấy tài liệu chỉ là công cụ để trao đổi thông tin, ý tưởng và cách này không hiệu quả bằng giao tiếp trực quan giữa người với người. Có một số dự án sử dụng framework chặt chẽ thì việc viết tài liệu mô tả là thừa thãi. Khi làm theo framework, các class đã có vai trò nào, thuộc tầng nào đã rõ hết rồi. 

Bạn thấy đấy, water fall là quy trình rất cực nhọc. Có lẽ nó chỉ phù hợp cho các dự án dài hơi, thiết kế phức tạp, ổn định về yêu cầu và được thực hiện bởi một đám developer designer giàu kinh nghiệm. Scrum một quy trình phát triển phần mềm nhanh khắc phục hầu hết nhược điểm của water fall. Sức mạnh của scrum đến từ một lối tiếp cận và thực hiện dự án mới mẻ.

Sơ lược về cách scrum thực hiện:
Khách hàng viết product backlog - yêu cầu chính. Đội dự án sẽ họp để phân tích product backlog. Sau đó đội dự án sẽ họp tiếp để phân tích và chẻ nhỏ yêu cầu thành các sprint backlog - Tương tự kick off dự án trong water fall nhưng đây là kick off một sprint. Đóng băng backlog và thực hiện sprint. Trong quá trình chạy sprint, đội dự án có họp sprint hàng ngày khoảng 15 phút để mỗi người tự thông báo tiến độ. Kết thúc sprint sẽ trình bày sản phẩm chạy được phù hợp với sprint backlog. Trước khi chuyển sang sprint kế tiếp sẽ tiến hàng sơ kết sprint để rút kinh nghiệm.

Scrum là tập hợp nhiều vòng phát triển phần mềm liên tục. Mỗi vòng gọi là một sprint. Kết quả mỗi vòng là một sản phẩm chạy được, chưa phải sản phẩm hoàn thiện nhưng nó hơn hẳn một bản demo. Vì có nhiều vòng phát triển nên developer và designer có thêm cơ hội để hiểu sâu hơn yêu cầu, tối ưu hóa các giải pháp, nâng cao chất lượng sản phẩm, nghiên cứu công nghệ. 

Các bước thực hiện mỗi vòng tương tự một water fall. Nhưng khác ở chỗ, trong mỗi sprint, khách hàng không được thay đổi yêu cầu. Đây là chi tiết rất đáng giá, đội dự án giờ đây sẽ toàn tâm toàn ý với công việc hơn. Đội dự án sẽ được bảo vệ bởi scrum master khi đang hoạt động trong một sprint. Kết quả mỗi sprint lại là một sản phẩm chạy được nên giữ được nhịp độ cho đội phát triển.

Một điểm đáng lưu ý nữa là scrum không phân nhiệm rõ ràng vai trò từng cá nhân trong đội. Một cá nhân có thể có đa trách nhiệm. Anh hay cô ta có thể là developer với backlog này nhưng lại có thể là designer với backlog khác hay thậm chí là tester với một backlog khác nữa. Cải tiến này sẽ nâng cao năng suất của đội dự án, giờ đây thì chẳng có sự chờ đợi giữa các cá nhân làm các công việc chuyên biệt như trước nữa.

Đội dự án trong scrum là tập hợp các cá nhân có động lực. Họ không nhận việc mà đăng ký để thực hiện việc. Khái niệm gán việc bị loại bỏ hoàn toàn. Vai trò quản lý nắm đầu của PM cũng biến mất thay vào đó là scrum master nhưng scrum master chỉ có vai trò hỗ trợ và bảo vệ đội. Đôi khi anh hay cô ta cũng có thể là một phát triển viên trong đội.

Cuối cùng, scrum không yêu cầu tài liệu kinh khủng như water fall. Scrum khuyến khích trao đổi trực tiếp để giữ cho luồng thông tin thông suốt giữa các cá nhân trong đội.

Một số ngoại lệ: 
Nếu khách hàng thay đổi sprint backlog giữa lúc sprint đang hoạt động ?
Nếu khách hàng hủy sprint backlog ?
Tính thực tiễn trong ứng dụng scrum ?