java-web/docs/tasks/chapter06-tasks.md

221 lines
8.3 KiB
Markdown
Raw Normal View History

2024-08-08 15:18:09 +08:00
## 6. 数据库访问
### 6.1 JDBC
#### 6.1.3 实现第一个JDBC程序
**练习任务**:
1. **创建数据库表**:
- 根据PetClinic中的`Owner`实体模型创建一个表,包含`id`(整数,主键,自增)、`firstName`字符串长度255不可为空、`lastName`字符串长度255不可为空、`address`字符串长度255、`city`字符串长度255、`telephone`字符串长度255字段。
- 示例代码:
```java
private static void createOwnerTable(Connection conn) throws SQLException {
String sql = "CREATE TABLE IF NOT EXISTS owners (" +
"id INT AUTO_INCREMENT PRIMARY KEY," +
"firstName VARCHAR(255) NOT NULL," +
"lastName VARCHAR(255) NOT NULL," +
"address VARCHAR(255)," +
"city VARCHAR(255)," +
"telephone VARCHAR(255))";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.executeUpdate();
}
}
```
2. **插入业主记录**:
- 向`owners`表中插入两条记录。
- 示例代码:
```java
private static void insertOwners(Connection conn) throws SQLException {
String sql = "INSERT INTO owners (firstName, lastName, address, city, telephone) VALUES (?, ?, ?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "Alice");
pstmt.setString(2, "Johnson");
pstmt.setString(3, "123 Main St");
pstmt.setString(4, "Springfield");
pstmt.setString(5, "555-1234");
pstmt.executeUpdate();
pstmt.setString(1, "Bob");
pstmt.setString(2, "Smith");
pstmt.setString(3, "456 Elm St");
pstmt.setString(4, "Springfield");
pstmt.setString(5, "555-5678");
pstmt.executeUpdate();
}
}
```
3. **查询业主记录**:
- 查询`owners`表中的所有记录,并打印出每条记录的信息。
- 示例代码:
```java
private static void queryOwners(Connection conn) throws SQLException {
String sql = "SELECT * FROM owners";
try (PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
int id = rs.getInt("id");
String firstName = rs.getString("firstName");
String lastName = rs.getString("lastName");
String address = rs.getString("address");
String city = rs.getString("city");
String telephone = rs.getString("telephone");
System.out.println("ID: " + id + ", First Name: " + firstName + ", Last Name: " + lastName + ", Address: " + address + ", City: " + city + ", Telephone: " + telephone);
}
}
}
```
4. **更新业主记录**:
- 更新`owners`表中名字为"Alice Johnson"的业主的电话号码为"555-1111"。
- 示例代码:
```java
private static void updateOwner(Connection conn) throws SQLException {
String sql = "UPDATE owners SET telephone = ? WHERE firstName = ? AND lastName = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "555-1111");
pstmt.setString(2, "Alice");
pstmt.setString(3, "Johnson");
int rowsUpdated = pstmt.executeUpdate();
System.out.println(rowsUpdated + " row(s) updated.");
}
}
```
5. **删除业主记录**:
- 删除`owners`表中名字为"Bob Smith"的业主记录。
- 示例代码:
```java
private static void deleteOwner(Connection conn) throws SQLException {
String sql = "DELETE FROM owners WHERE firstName = ? AND lastName = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "Bob");
pstmt.setString(2, "Smith");
int rowsDeleted = pstmt.executeUpdate();
System.out.println(rowsDeleted + " row(s) deleted.");
}
}
```
6. **完整程序**:
- 将以上步骤整合到一个完整的程序中确保正确加载MySQL驱动、建立数据库连接并处理异常。
- 示例代码:
```java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JdbcCrudExample {
private static final String DB_URL = "jdbc:mysql://localhost:3306/petclinic";
private static final String USER = "root";
private static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
try {
// 加载MySQL驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取数据库连接
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 创建表
createOwnerTable(conn);
// 插入记录
insertOwners(conn);
// 查询记录
queryOwners(conn);
// 更新记录
updateOwner(conn);
// 删除记录
deleteOwner(conn);
// 再次查询记录
queryOwners(conn);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// 其他方法...
}
```
### 6.3 SQL注入的预防措施
**练习任务**:
1. **使用PreparedStatement防止SQL注入**:
- 修改上述的`queryOwners`方法,使用`PreparedStatement`来执行查询。
- 示例代码:
```java
private static void queryOwners(Connection conn) throws SQLException {
String sql = "SELECT * FROM owners WHERE firstName = ? AND lastName = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "Alice");
pstmt.setString(2, "Johnson");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String firstName = rs.getString("firstName");
String lastName = rs.getString("lastName");
String address = rs.getString("address");
String city = rs.getString("city");
String telephone = rs.getString("telephone");
System.out.println("ID: " + id + ", First Name: " + firstName + ", Last Name: " + lastName + ", Address: " + address + ", City: " + city + ", Telephone: " + telephone);
}
}
}
```
### 6.2 数据库连接池
#### 6.2.4 使用HikariCP实现数据库连接池
**练习任务**:
1. **配置HikariCP连接池**:
- 使用HikariCP配置一个连接池并使用它来获取数据库连接。
- 示例代码:
```java
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class HikariCpExample {
public static void main(String[] args) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/petclinic");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(10);
config.setConnectionTimeout(30000);
HikariDataSource ds = new HikariDataSource(config);
try (Connection conn = ds.getConnection()) {
// 使用连接执行数据库操作
System.out.println("Database connection established.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
```