文章标题:
Java Web:深入钻研与实践提升(第2部分)
文章内容:
Java Web从入门到精通:全方位探索与实战(一)-CSDN博客
目录
四、Java Web开发里的数据库操作:以MySQL为例
4.1 MySQL数据库基础操作
MySQL作为一款应用广泛的开源关系型数据库管理系统,在Java Web开发中承担着关键的数据存储与管理职责。掌握MySQL的基础操作是开展Java Web数据库开发的根基。
数据库在MySQL中是数据存储与组织的核心载体,好似一个大型仓库用来存放各类数据。创建数据库时,运用CREATE DATABASE语句,语法为CREATE DATABASE [IF NOT EXISTS] database_name;。其中,IF NOT EXISTS是可选参数,能避免在数据库已存在时出现错误提示。例如,创建一个名为testdb的数据库,代码如下:
CREATE DATABASE IF NOT EXISTS testdb;
若要切换当前操作的数据库,使用USE语句,像USE testdb;,这就如同进入仓库的特定区域开展操作。查看所有数据库,可执行SHOW DATABASES;,它会列出系统中所有的数据库,便于我们知晓数据库的整体状况。而当某个数据库不再需要时,可使用DROP DATABASE语句删除,比如DROP DATABASE testdb;,不过此操作得谨慎,一旦执行,数据库及其所有数据会被永久删去。
表是数据库中数据存储的具体构造,类似仓库里的一个个货架,每个货架存放特定类型的数据。创建表时,需定义表名以及各列的名称、数据类型和约束条件。例如,创建一个名为users的表,用于存储用户信息,包含id(用户ID,整数类型,自动递增且为主键)、username(用户名,可变长度字符串,最大长度为50)、email(邮箱,可变长度字符串,最大长度为100)和password(密码,可变长度字符串,最大长度为50),代码如下:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
email VARCHAR(100),
password VARCHAR(50)
);
查看数据库中的所有表,执行SHOW TABLES;,它会展示当前数据库中所有的表名。若要查看某个表的结构,使用DESCRIBE语句,比如DESCRIBE users;,它会详细列出表中各列的信息,包括列名、数据类型、是否允许为空等,帮助我们了解表的设计。当表不再需要时,使用DROP TABLE语句删除,例如DROP TABLE users;,同样,此操作会删除表及其所有数据,需慎重使用。
在数据库中插入数据是常见操作,向users表中插入一条用户数据,包含用户名john_doe、邮箱john@example.com和密码password123,代码如下:
INSERT INTO users (username, email, password) VALUES
('john_doe', 'john@example.com', 'password123');
若要插入多条数据,可在VALUES关键字后用逗号分隔多个值列表,如:
INSERT INTO users (username, email, password) VALUES
('jane_smith', 'jane@example.com', 'password456'),
('tom_wilson', 'tom@example.com', 'password789');
从数据库中查询数据是获取信息的关键操作。查询users表中所有用户的信息,使用SELECT语句,代码如下:
SELECT * FROM users;
这里的*表示选择所有列。若只查询部分列,如只查询username和email列,代码为:
SELECT username, email FROM users;
若要根据条件查询,如查询用户名为john_doe的用户信息,使用WHERE子句,代码如下:
SELECT * FROM users WHERE username = 'john_doe';
还可以对查询结果进行排序,如按username升序排序,代码为:
SELECT * FROM users ORDER BY username ASC;
ASC表示升序,DESC表示降序。
更新数据库中的数据用于修改现有记录。将users表中用户名为john_doe的用户邮箱更新为new_john@example.com,代码如下:
UPDATE users SET email = 'new_john@example.com' WHERE username = 'john_doe';
删除数据库中的数据用于移除不再需要的记录。删除users表中用户名为tom_wilson的用户记录,代码如下:
DELETE FROM users WHERE username = 'tom_wilson';
4.2 JDBC技术深度剖析
JDBC(Java Database Connectivity)是Java语言中用于连接和操作数据库的重要API,它为Java开发者提供了一种统一的途径来与各类不同的数据库开展交互,让Java应用程序能够便捷地访问和管理数据库中的数据,如同一座连接Java程序与数据库的桥梁。
JDBC的核心接口和常用类构成了其强大功能的基础。DriverManager类是JDBC的管理层,负责管理数据库驱动程序的加载以及建立数据库连接。它犹如一个交通枢纽管理员,协调着Java程序与不同数据库之间的连接。例如,在加载MySQL数据库驱动时,使用Class.forName("com.mysql.cj.jdbc.Driver");语句,告知DriverManager要使用的数据库驱动类。
Connection接口代表与数据库的连接,它是与数据库交互的基础。通过DriverManager.getConnection(url, username, password)方法获取连接对象,其中url为数据库连接字符串,username和password分别为数据库的用户名和密码。例如:
String url = "jdbc:mysql://localhost:3306/testdb";
String username = "root";
String password = "password";
Connection connection = DriverManager.getConnection(url, username, password);
Statement接口用于执行SQL语句,它可以直接执行静态SQL语句。通过Connection对象的createStatement()方法创建Statement对象,比如Statement statement = connection.createStatement();。然后使用statement.executeQuery(sql)方法执行查询语句,返回ResultSet结果集;使用statement.executeUpdate(sql)方法执行插入、更新、删除等语句,返回受影响的行数。
PreparedStatement接口继承自Statement接口,它主要用于执行预编译的SQL语句。预编译的SQL语句能够提高执行效率,并且能有效防范SQL注入攻击。通过Connection对象的prepareStatement(sql)方法创建PreparedStatement对象,其中sql为带有参数占位符(?)的SQL语句。例如,插入用户数据的预编译SQL语句为:
String sql = "INSERT INTO users (username, email, password) VALUES (?,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "new_user");
preparedStatement.setString(2, "new_user@example.com");
preparedStatement.setString(3, "new_password");
int rowsAffected = preparedStatement.executeUpdate();
ResultSet接口用于存储查询结果集,它提供了一系列方法来遍历和获取结果集中的数据。通过Statement或PreparedStatement执行查询语句后返回ResultSet对象,然后使用while (resultSet.next())循环遍历结果集,通过resultSet.getString("column_name")等方法获取指定列的值。例如:
String sql = "SELECT * FROM users";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String email = resultSet.getString("email");
System.out.println("ID: " + id + ", Username: " + username + ", Email: " + email);
}
接下来,通过一个完整的代码示例来展示如何使用JDBC连接MySQL数据库并执行查询操作:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb";
String username = "root";
String password = "password";
try {
// 加载驱动程序
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接
Connection connection = DriverManager.getConnection(url, username, password);
// 创建Statement对象
Statement statement = connection.createStatement();
// 执行查询语句
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
// 处理结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String usernameFromDb = resultSet.getString("username");
String email = resultSet.getString("email");
System.out.println("ID: " + id + ", Username: " + usernameFromDb + ", Email: " + email);
}
// 关闭资源
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在上述代码中,首先加载MySQL驱动程序,然后通过DriverManager获取与数据库的连接。接着创建Statement对象,执行查询语句并获取结果集。最后,通过循环遍历结果集,输出查询到的用户信息。操作完成后,依次关闭ResultSet、Statement和Connection对象,以释放资源。
在使用JDBC过程中,可能会碰到一些常见问题。例如,驱动程序未找到异常ClassNotFoundException,这通常是因为没有正确添加数据库驱动包或者驱动类名写错。解决办法是确保数据库驱动包已正确添加到项目的类路径中,并检查驱动类名是否正确。
还有连接数据库失败的问题,可能是由于连接字符串错误、用户名或密码错误、数据库服务器未启动等原因导致。此时需要仔细检查连接字符串、用户名和密码,确保数据库服务器处于运行状态。
SQL注入攻击也是一个需要关注的问题,比如用户输入的数据被恶意拼接在SQL语句中,可能导致数据泄露或数据被篡改。使用PreparedStatement代替Statement能够有效防止SQL注入攻击,因为PreparedStatement会对参数进行预处理,避免了直接将用户输入的数据拼接到SQL语句中。
4.3 数据库连接池的应用
在Java Web开发中,频繁地创建和销毁数据库连接会带来明显的性能开销,因为建立数据库连接涉及网络通信、数据库认证等复杂操作,耗费时间和资源。数据库连接池技术应运而生,它通过预先创建一定数量的数据库连接,并将这些连接存储在连接池中,当应用程序需要与数据库进行交互时,可以直接从连接池中获取一个可用的连接,而无需每次都重新建立连接。使用完毕后,连接会被归还到连接池中,以供后续使用。这种方式大大减少了连接的建立和销毁的开销,提升了系统的性能和响应速度,就像一个连接的“仓库”,随时为应用程序提供可用的连接。
常见的数据库连接池有HikariCP、C3P0、DBCP等。HikariCP以其高性能和低延迟闻名,具有快速的连接获取速度、低资源消耗和高并发性能等特点,适用于高并发、高性能需求的应用程序。C3P0是一款老牌的Java数据库连接池,具有较高的稳定性和可靠性,支持自动回收连接、测试连接的有效性等功能,还提供了多种配置选项,用户可根据具体需求进行灵活配置。DBCP是Apache Commons项目的一部分,具有简单易用、配置灵活等特点,支持连接池的基本功能,如连接回收、连接测试等,同时还提供了一些高级功能,如连接的统计信息、自动重连等。
接下来,以C3P0为例,展示如何配置和使用数据库连接池。首先,需要在项目中添加C3P0的依赖。如果使用Maven项目,在pom.xml文件中添加以下依赖:
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
然后,在src目录下创建c3p0-config.xml配置文件,进行连接池的配置,示例代码如下:
<c3p0-config>
<default-config>
<!-- 数据库驱动名 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<!-- 数据库的url -->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">password</property>
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default:3 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">5</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">10</property>
<!-- 连接关闭时默认将所有未提交的操作回滚。Default: false -->
<property name="autoCommitOnClose">false</property>
<!-- 每60秒检查所有连接池中的空闲连接。Default:0 -->
<property name="idleConnectionTestPeriod">60</property>
<!-- 最大空闲时间,指定的时间内未使用则连接被丢弃。若为0则永不丢弃。Default:0 -->
<property name="maxIdleTime">300</property>
</default-config>
</c3p0-config>
在上述配置文件中,设置了数据库驱动类、连接URL、用户名、密码等基本信息,还配置了连接池的一些属性,如初始连接数、最小连接数、最大连接数、获取连接失败后的重试策略等。
接下来,通过代码获取连接池中的连接并执行数据库操作,示例代码如下:
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class C3P0Example {
public static void main(String[] args) {
// 创建C3P0数据源
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
// 配置数据源属性(也可通过c3p0-config.xml配置)
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testdb");
dataSource.setUser("root");
dataSource.setPassword("password");
// 配置连接池属性(也可通过c3p0-config.xml配置)
dataSource.setMinPoolSize(5);
dataSource.setMaxPoolSize(10);
dataSource.setCheckoutTimeout(3000);
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 从连接池获取连接
connection = dataSource.getConnection();
// 执行查询
String sql = "SELECT * FROM users";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
// 处理查询结果
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String email = resultSet.getString("email");
System.out.println("ID: " + id + ", Username: " + username + ", Email: " + email);
}
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
// 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
if (connection != null) {
try {
connection.close(); // 将连接放回连接池
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
} catch (PropertyVetoException ex) {
ex.printStackTrace();
} finally {
// 关闭数据源(通常在应用程序关闭时执行)
dataSource.close();
}
}
}
五、Java Web中的会话技术:Cookie与Session
5.1 Cookie详解
Cookie是一种客户端会话管理技术,它就像服务器发给客户端浏览器的一张小纸条,用于在客户端存储少量数据。当用户访问服务器时,服务器可以将一些信息以Cookie的形式发送给浏览器,浏览器会把这些Cookie存储在本地。下次用户再次访问服务器时,浏览器会自动将这些Cookie发送给服务器,服务器可依据这些Cookie来识别用户的身份或获取相关的用户信息。
Cookie的主要作用包含:
-
会话状态管理:比如用户登录信息的记录,当用户登录成功后,服务器能将用户的登录状态以Cookie的形式发送给浏览器,下次用户访问时,服务器可通过Cookie判断用户是否已登录,从而决定是否需要用户再次登录。
-
个性化设置:存储用户的个性化设置,像用户在网站上设置的语言偏好、主题风格等,服务器可根据Cookie中的设置为用户提供个性化的服务。
-
购物车功能:在电商网站中,Cookie可用于存储用户购物车中的商品信息,方便用户在不同页面之间切换时,购物车中的商品信息不会丢失。
在Java中,操作Cookie主要使用javax.servlet.http.Cookie类。常用的属性和方法如下: