Lập trình với Spring JDBC
Ngày nay, JPA và các ORMs luôn là lựa chọn mặc định của lập trình viên khi lập trình ứng dụng Spring Framework. Tuy nhiên, JDBC vẫn luôn là công cụ mạnh mẽ để truy xuất dữ liệu quan hệ. Nếu bạn không cần phải dùng đến một ORM, bạn cần truy xuất nhanh chóng đến CSDL, bạn ưu tiên tốc độ hơn là sự ...
Ngày nay, JPA và các ORMs luôn là lựa chọn mặc định của lập trình viên khi lập trình ứng dụng Spring Framework. Tuy nhiên, JDBC vẫn luôn là công cụ mạnh mẽ để truy xuất dữ liệu quan hệ. Nếu bạn không cần phải dùng đến một ORM, bạn cần truy xuất nhanh chóng đến CSDL, bạn ưu tiên tốc độ hơn là sự tiện dụng, bạn có thể sẽ chọn JDBC thay cho JPA/Hibernate.
Tuy nhiên, vấn đề của JDBC là nó khiến cho bạn phải viết quá nhiều code thừa, cách sử dụng JDBC cũng hao hao cách sử dụng ADO.NET khi bạn lập trình ứng dụng .NET Framework. Để tận dụng thế mạnh của ADO.NET nhưng không phải viết code lập đi lập lại, lập trình viên .NET có sự lựa chọn là sử dụng các Micro ORM như PetaPoco, Dapper.. Còn với JDBC, Spring đã cung cấp cho chúng ta một sự lựa chọn không tồi là JDBCTemplate.
Tạo dự án
Trong ví dụ này, chúng ta sẽ sử dụng Maven để tạo một ứng dụng đơn giản để truy xuất vào CSDL MySQL bằng cách sử dụng Spring JdbcTemplate. Dưới đây là tập tin pom.xml với các phụ thuộc:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.2.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> </dependency> </dependencies>
Ở tập tin pom.xml, chúng ta có một phụ thuộc là spring-orm, thư viên này cung cấp khả năng sử dụng Spring JdbcTemplate.
**Cấu trúc ứng dụng **
Cấu trúc của ứng dụng được thiết lập như dưới đây:
Theo bức hình ở trên, chúng ta sẽ tạo một số lớp:
Contact: Đây là một lớp model**
ContactDAO và ContactDAOImpl: mã lệnh để truye xuất bảng contact trong CSDL MySQL
DIConfiguration: mã lệnh để cấu hình datasource bean giúp cho ContactDAO có thể thiết lập kết nối đến CSDL.
App là lớp chứa phương thức main để chạy chương trình và chứa mã lệnh demo cho việc sử dụng các lớp nói trên.
1. Lớp Contact
package com.tumivn.hijdbctemplate.model public class Contact { private int id; private String name; private String email; private String address; private String telephone; public Contact() { } public Contact(String name, String email, String address, String telephone) { this.setName(name); this.setEmail(email); this.setAddress(address); this.setTelephone(telephone); } 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 getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } }
Lớp Contact chỉ là một lớp POJO (Plain Old Java Object), khi bạn lấy dữ liệu từ MySQL lên, bạn sẽ lưu trong các đối tượng của lớp này hoặc là bạn sẽ lưu dữ liệu mới được tạo ra từ các đối tượng lớp Contact và lưu xuống CSDL.
** ContactDAO interface**
Bây giờ chúng ta sẽ tạo ContactDAO interface để định nghĩa các hành phi có thể thực hiện để tương tác với bảng contact:
package com.tumivn.hijdbctemplate.dao; import com.tumivn.hijdbctemplate.model.Contact; import org.springframework.stereotype.Component; import java.util.List; @Component public interface ContactDAO { public void saveOrUpdate(Contact contact); public void delete(int contactId); public Contact get(int contactId); public List<Contact> list(); }
**Lớp ContactDAOImpl **
Chúng ta sẽ lập trình một lớp hiện thực hóa interface ContactDAO có tên là ContactDAOImpl. Có một điều bạn cần lưu ý là ContactDAOImpl có một phụ thuộc là lớp javax.sql.DataSource, chúng ta sẽ inject phụ thuộc này thông qua constructor.
package com.tumivn.hijdbctemplate.dao; import com.tumivn.hijdbctemplate.model.Contact; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; public class ContactDAOImpl implements ContactDAO { private JdbcTemplate jdbcTemplate; public ContactDAOImpl(DataSource dataSource){ jdbcTemplate = new JdbcTemplate(dataSource); } public void saveOrUpdate(Contact contact) { if (contact.getId() > 0) { String sql = "UPDATE contact SET name=?, email=?, address=?, " + "telephone=? WHERE contact_id=?"; jdbcTemplate.update(sql, contact.getName(), contact.getEmail(), contact.getAddress(), contact.getTelephone(), contact.getId()); } else { String sql = "INSERT INTO contact (name, email, address, telephone)" + " VALUES (?, ?, ?, ?)"; jdbcTemplate.update(sql, contact.getName(), contact.getEmail(), contact.getAddress(), contact.getTelephone()); } } public void delete(int contactId) { String sql = "DELETE FROM contact WHERE contact_id=?"; jdbcTemplate.update(sql, contactId); } public Contact get(int contactId) { String sql = "SELECT * FROM contact WHERE contact_id=" + contactId; return jdbcTemplate.query(sql, new ResultSetExtractor<Contact>() { @Override public Contact extractData(ResultSet rs) throws SQLException, DataAccessException { if (rs.next()) { Contact contact = new Contact(); contact.setId(rs.getInt("contact_id")); contact.setName(rs.getString("name")); contact.setEmail(rs.getString("email")); contact.setAddress(rs.getString("address")); contact.setTelephone(rs.getString("telephone")); return contact; } return null; } }); } public List<Contact> list() { String sql = "SELECT * FROM contact"; List<Contact> contacts = jdbcTemplate.query(sql, new RowMapper<Contact>() { @Override public Contact mapRow(ResultSet rs, int rowNum) throws SQLException { Contact contact = new Contact(); contact.setId(rs.getInt("contact_id")); contact.setName(rs.getString("name")); contact.setEmail(rs.getString("email")); contact.setAddress(rs.getString("address")); contact.setTelephone(rs.getString("telephone")); return contact; } }); return contacts; } }
** Lớp DIConfiguration**
Lớp DIConfiguration là lớp lưu giữ cấu hình để khởi tạo các Spring beans cho ứng dụng. Trong lớp này, chúng sẽ định nghĩa các bean dataSource và contactDAO. Trong các ứng dụng thực tế, chúng ta nên có một tập tin .properties để lưu các thông tin cấu hình thay vì lưu các thông tin để kết nối đến MySQL ngay bên trong lớp DIConfiguration như hiện tại.
package com.tumivn.hijdbctemplate.configuration; import com.tumivn.hijdbctemplate.dao.ContactDAO; import com.tumivn.hijdbctemplate.dao.ContactDAOImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.sql.DataSource; @Configuration @ComponentScan(value={"com.tumivn.hijdbctemplate"}) public class DIConfiguration { @Bean //@Scope("singleton") public DataSource getDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring_jdbc_ex"); dataSource.setUsername("root"); //dataSource.setPassword("P@ssw0rd"); return dataSource; } @Bean public ContactDAO getContactDAO() { return new ContactDAOImpl(getDataSource()); } }
** The App class**
package com.tumivn.hijdbctemplate; import com.tumivn.hijdbctemplate.configuration.DIConfiguration; import com.tumivn.hijdbctemplate.dao.ContactDAO; import com.tumivn.hijdbctemplate.model.Contact; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.stereotype.Component; import java.util.Iterator; import java.util.List; /** * Hello world! * */ @Component public class App { @Autowired private ContactDAO contactDAO; public void testContactDAO(){ contactDAO.saveOrUpdate(new Contact("Tumi Le", "tumivn@gmail.com", "","")); List<Contact> contacts = contactDAO.list(); Iterator<Contact> i = contacts.iterator(); while(i.hasNext()){ Contact contact = i.next(); System.out.println("Id: " + contact.getId() + "Contact name: " + contact.getName() + " ; Email: " + contact.getEmail()); } } public static void main( String[] args ) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DIConfiguration.class); App app = context.getBean(App.class); app.testContactDAO(); } }
Lớp App có một phụ thuộc là ContactDAO. Phương thức testContactDAO() sẽ thêm một record vào bảng contact trong CSDL và sau đó lấy ra danh sách các contacts và in ra màn hình.
Ở phương thức main, chúng ta sẽ dùng lớp AnnotationConfigApplicationContext để khởi tạo cấu hình cho ứng dụng và tạo ra một thực thể của lớp App để sử dụng.
Chạy ứng dụng
Chúc mừng, bạn đã hoàn thành một ứng dụng sử dụng Spring JdbcTemplate. Bạn đừng quá lo lắng với danh sách contact dài ngoằng từ kết quả ở trên. Danh sách dài đó là kết quả của việc chạy lại chương trình nhiều lần, khi thôi lập trình một tính năng khác (sẽ xuất hiện trong bài tới) mà thôi.