Tài liệu tự học: JDBC Kết nối trong dự án Java Maven với SQLite Mục lục: 1. Giới thiệu về JDBC và SQLite 2. Hướng dẫn cài đặt và thiết lập Maven 3. Cài đặt thư viện JDBC 4. Kết nối với cơ sở dữ liệu SQLite 5. Thực hiện các truy vấn SQL và lấy kết quả 6. Parse query và tạo ArrayList object ## 1. Giới thiệu về JDBC và SQLite JDBC (Java Database Connectivity) là một API trong Java cho phép các ứng dụng Java kết nối và tương tác với cơ sở dữ liệu. SQLite là một hệ quản trị cơ sở dữ liệu quan hệ nhẹ và phổ biến được sử dụng trong các ứng dụng nhỏ và nhẹ. ## 2. Hướng dẫn cài đặt và thiết lập Maven Maven là một công cụ quản lý dự án phổ biến trong lập trình Java. Để tạo dự án Maven, làm theo các bước sau: Bước 1: Cài đặt Maven từ trang chủ của Apache Maven (https://maven.apache.org/). Bước 2: Thiết lập biến môi trường MAVEN_HOME và thêm %MAVEN_HOME%\bin vào biến môi trường PATH. Bước 3: Kiểm tra cài đặt bằng cách chạy lệnh `mvn -version` trong dòng lệnh. ## 3. Cài đặt thư viện JDBC Để sử dụng JDBC để kết nối với cơ sở dữ liệu SQLite, chúng ta cần thêm thư viện JDBC vào dự án Maven. Thêm đoạn mã sau vào tệp pom.xml của dự án: ```xml <dependencies> <dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> <version>3.34.0</version> </dependency> </dependencies> ``` Sau khi thêm phần dependency, Maven sẽ tải xuống và cài đặt thư viện JDBC cho dự án của bạn. ## 4. Kết nối với cơ sở dữ liệu SQLite Sau khi đã cài đặt thư viện JDBC, chúng ta có thể kết nối với cơ sở dữ liệu SQLite bằng cách sử dụng đoạn mã sau: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class SQLiteConnection { public static void main(String[] args) { Connection connection = null; try { // Kết nối với cơ sở dữ liệu SQLite connection = DriverManager.getConnection("jdbc:sqlite:path/to/database.db"); System.out.println("Kết nối thành công!"); // Thực hiện các truy vấn và tương tác với cơ sở dữ liệu ở đây } catch (SQLException e) { System.err.println(e.getMessage()); } finally { try { if (connection != null) connection.close(); } catch (SQLException e) { System.err.println(e); } } } } ``` Trong đoạn mã trên, "path/to/database.db" là đường dẫn tới tệp SQLite database. ## 5. Thực hiện các truy vấn SQL và lấy kết quả Để thực hiện các truy vấn SQL và lấy kết quả từ cơ sở dữ liệu SQLite, chúng ta có thể sử dụng đối tượng `Statement` hoặc `PreparedStatement`. Dưới đây là một ví dụ minh họa: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class SQLiteQuery { public static void main(String[] args) { Connection connection = null; try { connection = DriverManager.getConnection("jdbc:sqlite:path/to/database.db"); System.out.println("Kết nối thành công!"); Statement statement = connection.createStatement(); String query = "SELECT * FROM table_name"; ResultSet resultSet = statement.executeQuery(query); while (resultSet.next()) { // Xử lý kết quả từ ResultSet ở đây } } catch (SQLException e) { System.err.println(e.getMessage()); } finally { try { if (connection != null) connection.close(); } catch (SQLException e) { System.err.println(e); } } } } ``` Trong ví dụ trên, `SELECT * FROM table_name` là một truy vấn SQL đơn giản để lấy tất cả các hàng từ bảng `table_name`. Bạn có thể thay đổi truy vấn theo nhu cầu của mình. ## 6. Parse query và tạo ArrayList object Để parse query và tạo một ArrayList object từ kết quả truy vấn, bạn cần định nghĩa một lớp đại diện cho đối tượng trong bảng. Dưới đây là một ví dụ minh họa: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class SQLiteQuery { public static void main(String[] args) { Connection connection = null; try { connection = DriverManager.getConnection("jdbc:sqlite:path/to/database.db"); System.out.println("Kết nối thành công!"); Statement statement = connection.createStatement(); String query = "SELECT * FROM table_name"; ResultSet resultSet = statement.executeQuery(query); List<YourObject> resultList = new ArrayList<>(); while (resultSet.next()) { YourObject obj = new YourObject(); obj.setId(resultSet.getInt("id")); obj.setName(resultSet.getString("name")); // Đặt các trường khác vào đối tượng obj resultList.add(obj); } // Sử dụng resultList ở đây } catch (SQLException e) { System.err.println(e.getMessage()); } finally { try { if (connection != null) connection.close(); } catch (SQLException e) { System.err.println(e); } } } } ``` Trong ví dụ trên, `YourObject` là lớp đại diện cho đối tượng trong bảng. Bạn cần định nghĩa các trường và phương thức getter/setter tương ứng cho các trường đó. Trong vòng lặp, ta tạo một đối tượng `YourObject`, đọc các giá trị từ `ResultSet` và đặt vào đối tượng, sau đó thêm đối tượng vào `resultList`. Sau khi kết thúc vòng lặp, bạn có thể sử dụng `resultList` để làm việc với dữ liệu trả về từ cơ sở dữ liệu. ## 6. Thực hiện truy vấn Execute Để thực hiện một truy vấn SQL bằng JDBC mà không cần kết quả trả về, chẳng hạn như INSERT, UPDATE hoặc DELETE, bạn có thể sử dụng phương thức `executeUpdate()` của đối tượng `Statement` hoặc `PreparedStatement`. Dưới đây là một ví dụ minh họa: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class SQLiteUpdate { public static void main(String[] args) { Connection connection = null; try { connection = DriverManager.getConnection("jdbc:sqlite:path/to/database.db"); System.out.println("Kết nối thành công!"); Statement statement = connection.createStatement(); String query = "INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2')"; int rowsAffected = statement.executeUpdate(query); System.out.println("Số hàng bị ảnh hưởng: " + rowsAffected); } catch (SQLException e) { System.err.println(e.getMessage()); } finally { try { if (connection != null) connection.close(); } catch (SQLException e) { System.err.println(e); } } } } ``` Trong ví dụ trên, `INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2')` là một truy vấn INSERT SQL đơn giản để chèn dữ liệu vào bảng `table_name`. Bạn có thể thay đổi truy vấn theo nhu cầu của mình. Phương thức `executeUpdate()` trả về số hàng bị ảnh hưởng bởi truy vấn. Trong ví dụ trên, chúng ta sử dụng biến `rowsAffected` để lưu giá trị này và in ra số hàng bị ảnh hưởng. Lưu ý rằng khi sử dụng truy vấn INSERT, UPDATE hoặc DELETE, bạn nên sử dụng `PreparedStatement` thay vì `Statement` để tránh các vấn đề bảo mật và các vấn đề liên quan đến SQL Injection. Đó là cách thực hiện một truy vấn SQL mà không cần kết quả trả về trong JDBC. Bạn có thể áp dụng tương tự cho các truy vấn SQL khác mà không cần lấy dữ liệu trả về từ cơ sở dữ liệu. ## 7. Thực hiện truy vấn với Store Procedure Để sử dụng JDBC để gọi một stored procedure và truyền tham số vào procedure, bạn có thể sử dụng đối tượng `CallableStatement` trong JDBC. Dưới đây là một ví dụ minh họa: ```java import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class CallStoredProcedure { public static void main(String[] args) { Connection connection = null; CallableStatement callableStatement = null; try { connection = DriverManager.getConnection("jdbc:sqlite:path/to/database.db"); System.out.println("Kết nối thành công!"); // Tạo một CallableStatement để gọi stored procedure callableStatement = connection.prepareCall("{call procedure_name(?, ?)}"); // Đặt giá trị các tham số vào stored procedure callableStatement.setString(1, "value1"); callableStatement.setInt(2, 123); // Thực thi stored procedure callableStatement.execute(); System.out.println("Stored procedure đã được gọi thành công!"); } catch (SQLException e) { System.err.println(e.getMessage()); } finally { try { if (callableStatement != null) callableStatement.close(); if (connection != null) connection.close(); } catch (SQLException e) { System.err.println(e); } } } } ``` Trong ví dụ trên, `procedure_name` là tên của stored procedure mà bạn muốn gọi. Bạn nên thay thế `procedure_name` bằng tên thực tế của stored procedure trong cơ sở dữ liệu của bạn. `callableStatement = connection.prepareCall("{call procedure_name(?, ?)}");` dùng để tạo một đối tượng `CallableStatement` để gọi stored procedure. Trong truy vấn này, chúng ta sử dụng `?` để đại diện cho các tham số của stored procedure. `callableStatement.setString(1, "value1");` và `callableStatement.setInt(2, 123);` được sử dụng để đặt giá trị cho các tham số của stored procedure. Trong ví dụ này, chúng ta đặt giá trị chuỗi "value1" cho tham số đầu tiên và giá trị số 123 cho tham số thứ hai. Bạn nên thay đổi các giá trị này tùy thuộc vào yêu cầu của stored procedure của bạn. Cuối cùng, `callableStatement.execute();` được sử dụng để thực thi stored procedure. Bạn cũng có thể sử dụng các phương thức khác như `executeQuery()` hoặc `executeUpdate()` tùy thuộc vào kiểu kết quả mà stored procedure trả về. ## EXTRA: Viết hàm dùng chung để tương tác với lớp trừu tượng Generic Nếu bạn muốn tự động lặp qua các cột của ResultSet và gán giá trị cho các trường có tên tương đương trong lớp đại diện `T`, bạn có thể sử dụng đối tượng `ResultSetMetaData` để thu thập thông tin về các cột và sử dụng reflection để gán giá trị. Dưới đây là một cách giải quyết: ```java import java.lang.reflect.Field; import java.sql.*; import java.util.ArrayList; public class DBHelper { private static final String DB_URL = "jdbc:sqlite:path/to/database.db"; public static <T> ArrayList<T> executeQuery(String query, Class<T> clazz) { Connection connection = null; Statement statement = null; ResultSet resultSet = null; ArrayList<T> results = new ArrayList<>(); try { connection = DriverManager.getConnection(DB_URL); statement = connection.createStatement(); resultSet = statement.executeQuery(query); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); while (resultSet.next()) { T result = clazz.getDeclaredConstructor().newInstance(); for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnName(i); Field field = clazz.getDeclaredField(columnName); field.setAccessible(true); field.set(result, resultSet.getObject(i)); } results.add(result); } } catch (SQLException | ReflectiveOperationException e) { System.err.println(e.getMessage()); } finally { try { if (resultSet != null) resultSet.close(); if (statement != null) statement.close(); if (connection != null) connection.close(); } catch (SQLException e) { System.err.println(e); } } return results; } } ``` Trong ví dụ trên, phương thức `executeQuery` nhận một truy vấn SQL và một đối tượng Class `clazz`, đại diện cho lớp mô hình (model) bạn muốn truy vấn trả về. Trong phần thân của phương thức `executeQuery`, chúng ta thu thập thông tin về cột bằng cách sử dụng `ResultSetMetaData`. Chúng ta lặp qua từng hàng của `ResultSet` và tạo một đối tượng mới của `T` sử dụng `clazz.getDeclaredConstructor().newInstance()`. Sau đó, chúng ta lặp qua từng cột của hàng hiện tại và lấy tên cột sử dụng `metaData.getColumnName(i)`. Chúng ta sử dụng `clazz.getDeclaredField(columnName)` để lấy trường tương ứng trong lớp `T`. Sau đó, chúng ta gán giá trị của cột sử dụng `field.set(result, resultSet.getObject(i))`. Cuối cùng, chúng ta thêm đối tượng `result` vào danh sách kết quả. Để sử dụng phương thức `executeQuery`, bạn có thể truyền truy vấn SQL và lớp mô hình (model) như sau: ```java public class User { private int id; private String name; // ... các trường khác và phương thức getter/setter public static void main(String[] args) { String query = "SELECT id, name FROM users"; ArrayList<User> users = DBHelper.executeQuery(query, User.class); // Sử dụng danh sách người dùng đã truy vấn for (User user : users) { System.out.println(user.getId() + " - " + user.getName()); } } } ``` Trong ví dụ trên, chúng ta sử dụng lớp `User` làm mô hình (model) và truyền `User.class` vào phương thức `executeQuery`. Lưu ý rằng cách này giả định rằng tên cột trong ResultSet tương ứng với tên của các trường trong lớp mô hình (model). Nếu có sự khác biệt trong tên, bạn có thể sử dụng các chú thích hoặc quy ước tên để ánh xạ đúng giữa cột và trường. ## 9: Hibernate Hibernate là một framework ORM (Object-Relational Mapping) trong Java, được sử dụng để ánh xạ các đối tượng Java vào cơ sở dữ liệu quan hệ. Nó cung cấp một cách tiện lợi và mạnh mẽ để làm việc với cơ sở dữ liệu mà không cần viết trực tiếp các truy vấn SQL. Một số lợi ích chính khi sử dụng Hibernate là: 1. Giảm thiểu mã lặp: Hibernate giúp giảm thiểu mã lặp bằng cách tự động xử lý các phần tử của lớp đối tượng và ánh xạ chúng vào cơ sở dữ liệu. Bạn không cần phải viết các truy vấn SQL chi tiết và quản lý kết nối cơ sở dữ liệu một cách rõ ràng. 2. Tính di động của cơ sở dữ liệu: Hibernate cho phép bạn thay đổi cơ sở dữ liệu mà không cần sửa đổi mã nguồn của ứng dụng. Bạn có thể chuyển đổi giữa các cơ sở dữ liệu quan hệ như MySQL, Oracle, PostgreSQL một cách dễ dàng. 3. Tối ưu hiệu suất: Hibernate cung cấp các cơ chế tối ưu hiệu suất như lazy loading (tải lười) và caching (bộ đệm). Lazy loading cho phép tải dữ liệu từ cơ sở dữ liệu theo yêu cầu, giúp giảm thiểu lượng dữ liệu không cần thiết được tải vào bộ nhớ. Caching giúp lưu trữ các dữ liệu truy vấn phổ biến trong bộ nhớ để truy cập nhanh hơn và giảm tải cho cơ sở dữ liệu. 4. Đảm bảo tính nhất quán dữ liệu: Hibernate hỗ trợ các giao dịch (transactions) để đảm bảo tính nhất quán dữ liệu trong ứng dụng. Bạn có thể sử dụng các phương thức như beginTransaction(), commit() và rollback() để quản lý các thay đổi dữ liệu. 5. Tích hợp dễ dàng: Hibernate tích hợp tốt với các framework và công nghệ phổ biến khác trong cộng đồng Java như Spring, Java EE và JPA (Java Persistence API). Điều này giúp bạn xây dựng ứng dụng linh hoạt và dễ dàng mở rộng. 6. Tính bảo mật: Hibernate cung cấp các công cụ và cơ chế bảo mật để bảo vệ dữ liệu trong cơ sở dữ liệu. Bạn có thể áp dụng các quyền truy cập và ràng buộc dữ liệu thông qua các annotation và cấu hình. Tổng quan, Hibernate giúp giảm thiểu công việc lặp lại, tăng tính di động, cải thiện hiệu suất và đảm bảo tính nhất quán dữ liệu. Nó là một công cụ quan trọng trong phát triển ứng dụng Java liên quan đến cơ sở dữ liệu. Để tạo một dự án Java sử dụng Maven và tích hợp Hibernate với cơ sở dữ liệu SQLite, bạn có thể làm theo các bước sau: Bước 1: Tạo dự án Maven 1. Mở trình quản lý dự án hoặc dòng lệnh, đảm bảo rằng Maven đã được cài đặt trên hệ thống của bạn. 2. Tạo một thư mục mới cho dự án của bạn và di chuyển vào thư mục đó. 3. Khởi tạo dự án Maven bằng lệnh sau: ``` mvn archetype:generate -DgroupId=com.example -DartifactId=myproject -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false ``` Trong lệnh trên, thay thế `com.example` bằng groupId của bạn và `myproject` bằng artifactId của dự án. Bước 2: Thêm dependency Hibernate vào dự án Maven 1. Mở tệp `pom.xml` trong dự án của bạn. 2. Thêm dependency Hibernate vào phần `<dependencies>` như sau: ```xml <dependencies> <!-- Các dependency khác --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.6.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.2.0.Final</version> </dependency> <dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> <version>3.34.0</version> </dependency> </dependencies> ``` Trong ví dụ trên, chúng ta đã thêm dependency Hibernate Core, Hibernate EntityManager và SQLite JDBC. Bước 3: Cấu hình Hibernate và SQLite 1. Tạo một tệp cấu hình Hibernate (ví dụ: `hibernate.cfg.xml`) trong thư mục `src/main/resources` của dự án. 2. Đặt thông tin kết nối SQLite trong tệp cấu hình như sau: ```xml <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.SQLiteDialect</property> <property name="hibernate.connection.driver_class">org.sqlite.JDBC</property> <property name="hibernate.connection.url">jdbc:sqlite:/path/to/database.db</property> <property name="hibernate.hbm2ddl.auto">update</property> <!-- Các cấu hình khác --> </session-factory> </hibernate-configuration> ``` Chú ý thay thế `/path/to/database.db` bằng đường dẫn tới tệp cơ sở dữ liệu SQLite của bạn. Bước 4: Định nghĩa lớp mô hình (model) 1. Tạo một thư mục `src/main/java` trong dự án của bạn (nếu nó chưa tồn tại). 2. Trong thư mục `src/main/java`, tạo các package và lớp Java để đại diện cho mô hình dữ liệu của bạn. Ví dụ, tạo package `com.example.model` và lớp `User` trong package đó. ```java package com.example.model; import javax.persistence.*; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(name = "name") private String name; // Các trường và phương thức getter/setter khác } ``` Bước 5: Sử dụng Hibernate để thực hiện ORM 1. Tạo một lớp Java (ví dụ: `Main.java`) trong thư mục `src/main/java` để chạy chương trình. 2. Trong lướp `Main.java`, bạn có thể sử dụng Hibernate API để thực hiện các hoạt động ORM. Dưới đây là một ví dụ đơn giản: ```java package com.example; import com.example.model.User; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class Main { public static void main(String[] args) { // Khởi tạo SessionFactory từ cấu hình Configuration configuration = new Configuration().configure("hibernate.cfg.xml"); SessionFactory sessionFactory = configuration.buildSessionFactory(); // Tạo phiên làm việc Session session = sessionFactory.openSession(); session.beginTransaction(); // Thực hiện các hoạt động ORM User user = new User(); user.setName("John Doe"); session.save(user); // Đóng phiên làm việc session.getTransaction().commit(); session.close(); sessionFactory.close(); } } ``` Trong ví dụ trên, chúng ta đã khởi tạo `SessionFactory` từ cấu hình Hibernate được định nghĩa trong tệp `hibernate.cfg.xml`. Sau đó, chúng ta mở một phiên làm việc (`Session`), bắt đầu một giao dịch, thêm một đối tượng `User` vào cơ sở dữ liệu thông qua `session.save()`, hoàn tất giao dịch và đóng phiên làm việc và `SessionFactory`.