Java: Tổng quan về cơ sở dữ liệu quan hệ
Một cơ sở dữ liệu là một phương tiện lưu trữ thông tin theo hướng thông tin có thể được truy xuất từ nó. Hiểu một cách đơn giản nhất, một cơ sở dữ liệu quan hệ thể hiện thông tin dưới dạng các bảng bao gồm các hàng và các cột. Một bảng được tham chiếu đến như là một quan hệ theo nghĩa nó là một tập hợp các đối tượng có cùng kiểu (hàng). Dữ liệu trong bảng có thể có liên quan theo các khóa hoặc các khái niệm phổ biến, và khả năng để truy xuất dữ liệu liên quan từ một bảng là cơ sở cho cơ sở dữ liệu quan hệ. Hệ quản trị cơ sở dữ liệu (Database Management System - DBMS) sẽ xử lý cách mà dữ liệu được lưu trữ, bảo trì, và truy xuất. Trong trường hợp cơ sở dữ liệu quan hệ thì một Hệ quản trị cơ sở dữ liệu quan hệ (Relational Database Management System - RDBMS) sẽ giải quyết những vấn đề này. DBMS được sử dụng trong các bài viết liên quan tại phần JDBC của V1Study.com sẽ bao gồm cả RDBMS.
Các quy tắc toàn vẹn
Các bảng quan hệ cần tuân theo các quy tắc toàn vẹn nhất định để đảm bảo rằng dữ liệu chúng chứa là chuẩn và luôn có thể truy cập được. Trước tiên, các hàng trong bảng quan hệ phải khác nhau (distinct), nếu có những hàng giống nhau thì có nghĩa có thể có hai lựa chọn đúng cho một truy vấn lựa chọn duy nhất một bản ghi (hàng). Đối với phần lớn các DBMS thì người dùng có thể chỉ định rằng các bản ghi giống nhau là không được phép, và nếu điều đó xảy ra thì DBMS sẽ ngăn cản them vào những hàng giống nhau đó.
Quy tắc toàn vẹn thứ hai của mô hình quan hệ truyền thống là các giá trị của một cột phải không được lặp lại theo nhóm hay mảng. Khía cạnh toàn vẹn dữ liệu thứ ba liên quan đến khái niệm null. Cơ sở dữ liệu quan tâm đến các tình huống trong đó dữ liệu không thể được phép sử dụng giá trị null để chỉ ra rằng giá trị đang bị thiếu. Null không tương ứng với trống (blank) hoặc số 0 (zero). blank thì tương đương với blank khác, zero tương đương với zero khác, nhưng hai giá trị null lại không phải tương đương nhau.
Khi mà mỗi hàng trong bảng là khác nhau thì bảng có thể sử dụng một hoặc nhiều cột để nhận diện một hàng cụ thể. Lúc này, cột hay nhóm cột duy nhất đó được gọi là khóa chính (Primary Key - PK). Bất kỳ cột nào là một phần của khóa chính đều không phải là null, bởi nếu không thì PK sẽ không còn là bộ nhận diện (identifier) hoàn chỉnh. Quy tắc này được gọi là toàn vẹn thực thể.
Bảng Employees
dưới đây thể hiện một số khái niệm về cơ sở dữ liệu ở trên. Bảng này có 5 cột 6 hàng, mỗi hàng biểu diễn một nhân viên riêng.
Employee_Number |
First_name |
Last_Name |
Date_of_Birth |
Car_Number |
---|---|---|---|---|
10001 | Axel | Washington | 28-Aug-43 | 5 |
10083 | Arvid | Sharma | 24-Nov-54 | null |
10120 | Jonas | Ginsberg | 01-Jan-69 | null |
10005 | Florence | Wojokowski | 04-Jul-71 | 12 |
10099 | Sean | Washington | 21-Sep-66 | null |
10035 | Elizabeth | Yamaguchi | 24-Dec-59 | null |
Khóa chính của bảng trên là mã nhân viên (Employee_Number) bởi vì hai giá trị bất kỳ trên cột này không thể giống nhau (một số thì hiệu quả hơn một chuỗi khi tiến hành so sánh).
Câu lệnh SELECT
SQL là ngôn ngữ được thiết kế để được sử dụng với cơ sở dữ liệu quan hệ. Có một tập các câu lệnh SQL được coi là chuẩn và được sử dụng bởi tất cả các RDBMS. Ví dụ, tất cả các RDBMS đều có thể sử dụng câu lệnh SELECT
.
Một câu lệnh SELECT
còn được gọi là một truy vấn, nó được sử dụng để lấy thông tin từ một hoặc các bảng trong cơ sở dữ liệu quan hệ. Nó chỉ định một hoặc nhiều cột tiêu đề từ một hoặc nhiều bảng và một số chỉ định đặc biệt để truy vấn lựa chọn. RDBMS trả về các hàng của toàn bộ các cột đáp ứng yêu cầu truy vấn. Câu lệnh SELECT
sau đây sẽ lấy first và last name của các nhân viên:
SELECT First_Name, Last_Name
FROM Employees
WHERE Car_Number IS NOT NULL
Tập kết quả (tập hợp các hàng thỏa mãn yêu cầu không có giá trị rỗng trong cột Car_Number
) theo sau. Họ và tên được in cho mỗi hàng thỏa mãn yêu cầu vì câu lệnh SELECT
(dòng đầu tiên) chỉ định các cột First_Name
và Last_Name
. Mệnh đề FROM
(dòng thứ hai) cho bảng mà từ đó các cột sẽ được chọn.
FIRST_NAME | LAST_NAME |
---|---|
Axel | Washington |
Florence | Wojokowski |
The following code produces a result set that includes the whole table because it asks for all of the columns in the table Employees with no restrictions (no WHERE
clause). Note that SELECT *
means "SELECT all columns."
Đoạn mã sau tạo ra một tập kết quả bao gồm toàn bộ bảng vì nó yêu cầu tất cả các cột trong bảng Employees không có hạn chế (không có mệnh đề WHERE
). Lưu ý rằng SELECT *
có nghĩa là " SELECT
tất cả các cột".
SELECT *
FROM Employees
Mệnh đề WHERE
Mệnh đề WHERE
trong câu lệnh SELECT
các tiêu chí để lựa chọn dữ liệu. Ví dụ, trong đoạn mã sau đây các giá trị chỉ được chọn nếu chúng bắt đầu bằng chuỗi 'Washington' Last_Name.
SELECT First_Name, Last_Name
FROM Employees
WHERE Last_Name LIKE 'Washington%'
Từ khóa LIKE được sử dụng để so sánh chuỗi, và nó cung cấp các tính năng mà mẫu chứa các kí tự đại diện (wildcard) có thể được sử dụng. Ví dụ, trong đoạn mã trên, có một dấu phần trăm (%) ở cuối của 'Washington', cho thấy rằng bất kỳ giá trị nào có chứa chuỗi 'Washington' cộng với không có gì hoặc có nhiều ký tự phía sau sẽ đáp ứng tiêu chí lựa chọn này. Vì vậy, 'Washington' hoặc 'Washingtonian' sẽ tương thích, nhưng 'Washing' sẽ không tương thích. Các ký tự đại diện khác được sử dụng trong mệnh đề LIKE là ký tự gạch dưới (_), nó đại diện cho một ký tự bất kỳ.
Ví dụ:
WHERE Last_Name LIKE 'Ba_man'
sẽ tương thích với 'Batman', 'Barman', 'Badman', 'Balman', 'Bagman', 'Bamman', ...
Đoạn mã dưới đây có mệnh đề WHERE
sử dụng dấu (=) để so sánh các số. Câu lệnh sẽ chọn các dữ liệu liên quan đến first name và last name của nhân viên có Car_Number là 12.
SELECT First_Name, Last_Name
FROM Employees
WHERE Car_Number = 12
Đoạn mã tiếp theo lấy first name và last name của những nhân viên có mã lớn hơn 10005:
SELECT First_Name, Last_Name
FROM Employees
WHERE Employee_Number > 10005
Mệnh đề WHERE có thể có dạng khá phức tạp, với nhiều điều kiện, và trong một số DBMS thì điều kiện còn có thể lồng nhau. Câu lệnh truy vấn dưới đây chọn first name và last name của những nhân viên có mã nhỏ hơn 10100 đồng thời Car_Number phải là null (tức là những nhân viên chưa có xe ô tô).
SELECT First_Name, Last_Name
FROM Employees
WHERE Employee_Number < 10100 and Car_Number IS NULL
Join
Một tính năng đặc biệt nữa của cơ sở dữ liệu quan hệ là nó có thể để có được dữ liệu từ nhiều bảng bằng cách sử dụng kết nối bảng (JOIN). Giả sử rằng sau khi lấy tên của các nhân viên có xe, ta muốn biết thêm xe nào là của ai, bao gồm cả hãng, model và năm sản xuất. Thông tin chi tiết về xe được lưu trữ trong bảng Car như sau:
Car_Number |
Make |
Model |
Year |
---|---|---|---|
5 | Honda | Civic DX | 1996 |
12 | Toyota | Corolla | 1999 |
Để làm được điều này thì phải có một cột gọi là cột chung mà xuất hiện trong cả hai bảng để kết nối chúng với nhau. Một trong hai cột chung đó chứa khóa chính, cột còn lại chứa khóa ngoại. Trong trường hợp này, cột chung xuất hiện trong cả hai bảng là Car_Number, trong đó khóa chính nằm trên cột của bảng Cars, còn khóa ngoại nằm trên cột của bảng Empolyees. Nếu xe Honda Civic bị xóa khỏi bảng Cars, thì Car_Number=5 cũng sẽ phải được xóa khỏi bảng Empoyees để duy trì những gì được gọi là toàn vẹn tham chiếu. Nếu không, cột chứa khóa ngoại (Car_Number) trong bảng nhân viên sẽ chứa một bản ghi mà không tham chiếu đến bất cứ bản ghi nào trong bảng Cars. Khóa ngoại phải hoặc là rỗng (null) hoặc bằng một giá trị khóa chính hiện tại của bảng mà nó tham chiếu. Điều này khác với khóa chính, cụ thể là các giá trị trong cột khóa chính không được null. Có một số giá trị null trong cột Car_Number trong bảng nhân viên là do nhân viên không có ô tô.
Đoạn mã sau đây lấy thông tin first name và last name của những nhân viên có xe công ty và đồng thời lấy thông tin về hãng, model, và năm của những chiếc xe. Lưu ý rằng mệnh đề FROM liệt kê cả bảng Employees và Cars vì các dữ liệu yêu cầu chứa trong cả hai bảng. Sử dụng tên bảng và một dấu chấm (.) trước tên cột để chỉ ra bảng chứa cột đó.
SELECT Employees.First_Name, Employees.Last_Name,
Cars.Make, Cars.Model, Cars.Year
FROM Employees, Cars
WHERE Employees.Car_Number = Cars.Car_Number
Câu lệnh trên cho ta tập kết quả trông như sau:
FIRST_NAME |
LAST_NAME |
MAKE |
MODEL |
YEAR |
---|---|---|---|---|
Axel | Washington | Honda | Civic DX | 1996 |
Florence | Wojokowski | Toyota | Corolla | 1999 |
Các câu lệnh SQL phổ biến
Câu lệnh SQL được chia thành nhiều loại, gồm hai loại chính là Ngôn ngữ thao tác dữ liệu (Data Manipulation Language - DML) và Ngôn ngữ định nghĩa dữ liệu (Data Definition Language - DDL). Các câu lệnh DML có nhiệm vụ hoặc là lấy hoặc sửa đổi dữ liệu để giữ cho nó được cập nhật mới nhất. Các câu lệnh DDL dùng để tạo hoặc thay đổi bảng và các đối tượng cơ sở dữ liệu khác như các view và index.
Dưới đây là những câu lệnh DML phổ biến:
SELECT —
used to query and display data from a database. TheSELECT
statement specifies which columns to include in the result set. The vast majority of the SQL commands used in applications areSELECT
statements.INSERT —
adds new rows to a table.INSERT
is used to populate a newly created table or to add a new row (or rows) to an already-existing table.DELETE —
removes a specified row or set of rows from a tableUPDATE —
changes an existing value in a column or group of columns in a table- SELECT - dùng để truy vấn và hiển thị dữ liệu từ một cơ sở dữ liệu. Câu lệnh SELECT chỉ định các cột để hiển thị trong tập kết quả. Phần lớn các lệnh SQL được sử dụng trong các ứng dụng là các câu lệnh SELECT.
- INSERT - thêm hàng (bản ghi) mới vào một bảng.
- DELETE - loại bỏ những bản ghi được chỉ định khỏi bảng
- UPDATE - thay đổi một giá trị hiện tại trong một hoặc nhiều cột của một bảng cụ thể
Sau đây các lệnh DDL phổ biến:
CREATE TABLE —
tạo một bảng với các tên cột do người dùng cung cấp. Người dùng cũng cần phải chỉ định kiểu dữ liệu trong mỗi cột. Các kiểu dữ liệu là khác nhau với các RDBMS khác nhau, vì vậy người dùng có thể cần phải sử dụng siêu dữ liệu để thiết lập các kiểu dữ liệu được sử dụng bởi một cơ sở dữ liệu cụ thể.CREATE TABLE
thường được sử dụng ít thường xuyên hơn so với các lệnh thao tác dữ liệu vì mỗi bảng được tạo ra chỉ một lần, trong khi việc thêm hoặc xóa hàng hoặc thay đổi các giá trị thường xảy ra thường xuyên hơn.DROP TABLE — dùng để
xóa tất cả các hàng và loại bỏ định nghĩa bảng khỏi cơ sở dữ liệu. Một thực hiện API JDBC là cần thiết để hỗ trợ cho câu lệnhDROP TABLE
theo quy định của SQL92, Transitional Level. Tuy nhiên, việc hỗ trợ các tùy chọnCASCADE
vàRESTRICT
của câu lệnhDROP TABLE
chỉ là tùy chọn. Ngoài ra, hành vi củaDROP TABLE
là thực hiện-xác định xem có các view hay ràng buộc toàn vẹn được định nghĩa mà tham chiếu đến bảng đang bị xóa hay không.ALTER TABLE —
thêm hoặc loại bỏ một cột khỏi bảng. Nó cũng thêm hoặc xóa ràng buộc bảng và sửa thuộc tính cột.
Tập kết quả và Con trỏ
Những hàng mà đáp ứng các điều kiện của một truy vấn được gọi là tập kết quả. Số lượng hàng trả về trong tập kết quả có thể là không, một, hoặc nhiều. Một người dùng có thể truy cập dữ liệu trong một tập kết quả tại một thời điểm, và một con trỏ cung cấp các phương tiện để làm điều đó. Một con trỏ có thể được coi như là một con trỏ vào một tập tin có chứa các hàng của các tập kết quả, và con trỏ có khả năng theo dõi xem hàng hiện tại được truy cập hay không. Con trỏ cho phép người dùng xử lý từng hàng của tập kết quả từ trên xuống dưới và do đó có thể được sử dụng để thực hiện lặp đi lặp lại. Hầu hết các DBMS đều tạo ra một con trỏ tự động khi tập kết quả được tạo ra.
Phiên bản API trước JDBC bổ sung tính năng mới cho con trỏ của tập hợp kết quả, nó cho phép nó di chuyển cả phía trước và phía sau và cũng cho phép nó di chuyển đến một hàng quy định hoặc để một hàng có vị trí là tương đối so với các hàng khác.
Giao dịch
Khi một người dùng truy cập dữ liệu trong một cơ sở dữ liệu, thì người sử dụng khác cũng có thể truy cập cùng vào dữ liệu đó cùng một thời điểm. Nếu, ví dụ, người dùng đầu tiên đang cập nhật một số cột trong một bảng đồng thời người sử dụng thứ hai đang chọn cột từ cùng một bảng đó, thì nó có thể cho người dùng thứ hai có được dữ liệu một phần cũ và dữ liệu được cập nhật một phần. Vì lý do này, DBMS sử dụng giao dịch để duy trì dữ liệu trong trạng thái (dữ liệu thống nhất) phù hợp trong khi cho phép nhiều người dùng cùng truy cập vào một cơ sở dữ liệu cùng một lúc (dữ liệu đồng thời).
Một giao dịch là một tập hợp của một hoặc nhiều câu lệnh SQL tạo nên một đơn vị logic của công việc. Một giao dịch kết thúc với commit kết hoặc một rollback tùy thuộc vào việc có vấn đề gì với tính nhất quán dữ liệu hoặc đồng thời dữ liệu. Câu lệnh commit làm vĩnh viễn thay đổi kết quả từ các câu lệnh SQL trong giao dịch, và câu lệnh rollback sẽ undo tất cả các thay đổi kết quả từ các câu lệnh SQL trong giao dịch.
Một khóa là một cơ chế ngăn cấm hai giao dịch cùng thao tác một dữ liệu cùng một lúc. Ví dụ, một bảng khóa sẽ ngăn chặn một bảng đang bị xóa nếu có một giao dịch không bị commit trên bảng đó. Trong một số DBMS, một khóa bảng có thể khóa tất cả các hàng trong một bảng. Một khóa hàng ngăn cản hai giao dịch thay đổi cùng một hàng, hoặc nó ngăn cản một giao dịch từ việc lựa chọn một hàng, trong khi giao dịch khác vẫn đang sửa đổi nó.
Thủ tục lưu trữ (Stored Procedures)
Một thủ tục lưu trữ là một nhóm các câu lệnh SQL có thể được gọi bằng một tên nào dó. Nói cách khác, nó là mã thực thi, một chương trình con, nó thực hiện một nhiệm vụ cụ thể mà có thể được gọi theo cách tương tự như gọi một hàm hoặc phương thức. Theo truyền thống, các thủ tục lưu trữ đã được viết bằng một ngôn ngữ lập trình DBMS cụ thể. Thế hệ mới nhất của các sản phẩm cơ sở dữ liệu cho phép lưu trữ các thủ tục lưu trữ được viết bằng ngôn ngữ lập trình Java và API JDBC. Thủ tục lưu trữ được viết bằng ngôn ngữ lập trình Java bytecode di động giữa các DBMS. Khi một thủ tục lưu trữ được viết, nó có thể được sử dụng và tái sử dụng vì DBMS hỗ trợ các thủ tục được lưu trữ sẽ, như tên gọi của nó, lưu nó trong cơ sở dữ liệu.
Các mã sau đây là một ví dụ về làm thế nào để tạo ra một thủ tục lưu trữ rất đơn giản bằng cách sử dụng ngôn ngữ lập trình Java. Lưu ý rằng các thủ tục lưu trữ chỉ là một phương pháp Java tĩnh có chứa mã JDBC bình thường. Nó chấp nhận hai tham số đầu vào và sử dụng chúng để thay đổi số lượng xe của nhân viên.
Đừng lo lắng nếu bạn không hiểu các ví dụ tại thời điểm này. Đoạn mã ví dụ dưới đây được trình bày chỉ để minh họa thế nào là thủ tục lưu trữ.
import java.sql.*; public class UpdateCar { public static void UpdateCarNum(int carNo, int empNo) throws SQLException { Connection con = null; PreparedStatement pstmt = null; try { con = DriverManager.getConnection( "jdbc:default:connection"); pstmt = con.prepareStatement( "UPDATE EMPLOYEES " + "SET CAR_NUMBER = ? " + "WHERE EMPLOYEE_NUMBER = ?"); pstmt.setInt(1, carNo); pstmt.setInt(2, empNo); pstmt.executeUpdate(); } finally { if (pstmt != null) pstmt.close(); } } }
Siêu dữ liệu (Metadata)
Cơ sử dữ liệu lưu trữ dữl liệu người dùng, và nó cũng lưu trữ thông tin về các cơ sở dữ liệu riêng của mình. Hầu hết các DBMS có một tập hợp các bảng hệ thống, trong đó liệt kê các bảng trong cơ sở dữ liệu, tên cột trong mỗi bảng, khóa chính, khóa ngoài, thủ tục được lưu trữ, ... Mỗi DBMS có chức năng riêng của mình để nhận được thông tin về bố trí bảng và các tính năng cơ sở dữ liệu. JDBC cung cấp giao diện DatabaseMetaData
, mà một bộ ghi trình điều khiển phải thực hiện sao cho các phương thức của nó trả về thông tin về trình điều khiển và/hoặc DBMS mà rình điều khiển được viết. Ví dụ, một số lượng lớn các phương thức trả về hoặc không trả về trình điều khiển hỗ trợ một chức năng cụ thể. Giao diện này cung cấp cho người dùng và các công cụ một cách chuẩn hóa để có được siêu dữ liệu.