JDBC 学习笔记
JDBC 四大核心参数
- 驱动类名 MySQL 8.x:
com.mysql.cj.jdbc.Driver(必须带.cj) MySQL 5.x:com.mysql.jdbc.Driver(不需要.cj) - 数据库URL 格式:
jdbc:mysql://主机IP:端口号/数据库名?参数
- 主机IP:本地
localhost/127.0.0.1,远程填写服务器公网IP - MySQL 默认端口:
3306 - 常用参数:
useSSL=false:开发环境关闭SSL加密serverTimezone=UTC:指定时区,8.0版本必填
- 数据库用户名
- 数据库密码
// 示例配置
String driverName = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/stock?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "123456";
JDBC 标准执行步骤
- 加载驱动
Class.forName(driverName);
- 建立数据库连接
Connection conn = DriverManager.getConnection(url, username, password);
- 编写SQL语句,使用PreparedStatement(防止SQL注入)
❌ 不推荐:
Statement,存在SQL注入风险 ✅ 推荐:PreparedStatement,SQL预先编译,使用占位符?
占位符说明:
?为占位符,后续动态赋值- SQL中字符串、日期使用单引号
' ',不要自己拼接字符串
// 查询SQL示例
String sql = "select * from user where userName = ? and pwd = ? ";
// 新增SQL示例
String sql = "insert into user(userName,pwd) values(?,?)";
PreparedStatement statement = conn.prepareStatement(sql);
// 给占位符赋值,下标从1开始
String usename = "admin";
String pwd = "123456";
statement.setString(1, usename);
statement.setString(2, pwd);
- 执行SQL语句
- 增 / 删 / 改:
executeUpdate()→ 返回受影响行数 - 查询:
executeQuery()→ 返回ResultSet结果集
示例:登录校验(查询单行数据)
ResultSet rset = statement.executeQuery();
// next():指针向下移动一行,有数据返回true,无数据返回false
if (rset.next()) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
示例:查询多条数据,遍历结果集
String sql = "select * from user";
ResultSet rset = statement.executeQuery(sql);
while(rset.next()){
//方式1:根据列名获取(推荐,可读性高)
String userNo = rset.getString("userNo");
String userName = rset.getString("userName");
int pwd = rset.getInt("pwd");
//方式2:根据列下标获取(从1开始)
//String userNo = rset.getString(1);
//int pwd = rset.getInt(2);
//String userName = rset.getString(3);
System.out.println(userNo + " " + userName + " " + pwd);
}
- 关闭资源(逆序关闭)
rset.close();
statement.close();
conn.close();
常见异常以及原因
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver原因:缺少MySQL驱动包 或者 驱动类名写错(8.0漏写cj)java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)原因:数据库用户名或者密码错误MySQLSyntaxErrorException: Unknown database 'xxx'原因:URL中填写的数据库名称不存在Communications link failure原因:
- MySQL服务未启动
- IP/端口填写错误
- 防火墙拦截3306端口
- 远程服务器未开放MySQL访问权限
补充知识点
- SQL注入产生原因:Statement直接拼接字符串,SQL语句先拼接,后编译
- PreparedStatement:SQL先编译,再填充参数,从根源避免SQL注入
- 占位符下标从1开始,不是0!
完整代码
@Test
public void test6() {
ResultSet resultSet = null;
Statement statement = null;
Connection connection = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8", "root", "123456");
String sql = "select * from student";
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
Student student = new Student(id, name, age, gender);
System.out.println(student);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Utils工具类封装数据
jdbc.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/stock?useSSL=false
username=root
password=123456
JdbcUtils
/**
* JDBC工具类
* 作用:封装数据库连接获取、资源关闭的重复代码
* 从classpath下的jdbc.properties配置文件读取数据库信息
*/
public class JdbcUtils {
// 数据库驱动类名
private static String driver;
// 数据库连接地址
private static String url;
// 数据库登录账号
private static String username;
// 数据库登录密码
private static String password;
/**
* 私有构造方法
* 禁止外部new创建工具类对象,工具类全部使用静态方法
*/
private JdbcUtils() {
}
/**
* 静态代码块
* 类加载的时候只执行一次:读取配置文件、加载数据库驱动
*/
static {
try {
//1.通过当前类获得类加载器,读取resources下的配置文件
ClassLoader classLoader = JdbcUtils.class.getClassLoader();
// 2. 通过类加载器获取资源文件输入流,读取jdbc.properties
InputStream inputStream = classLoader.getResourceAsStream("jdbc.properties");
//3.创建Properties集合,专门读取properties配置文件
Properties properties = new Properties();
// 将输入流中的配置信息加载到Properties对象
properties.load(inputStream);
// 取出配置文件中的参数赋值给静态变量
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
} catch (IOException e) {
// 配置文件读取失败,包装为运行时异常,终止程序
throw new RuntimeException("读取jdbc.properties配置文件失败", e);
}
try {
// 加载数据库驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
// 驱动类找不到,抛出运行异常
throw new RuntimeException("数据库驱动类加载失败,请检查驱动类名", e);
}
}
/**
* 获取数据库连接对象
* @return Connection 数据库连接
* @throws SQLException 获取连接失败抛出SQL异常
*/
public static Connection getConections() throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
/**
* 统一关闭JDBC资源
* @param connection 数据库连接对象
* @param statement SQL执行对象
* @param resultSet 查询结果集
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
// 关闭结果集
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException("关闭ResultSet失败", e);
}
}
// 关闭Statement
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException("关闭Statement失败", e);
}
}
// 关闭数据库连接
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException("关闭Connection失败", e);
}
}
}
}
Demo
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* JDBC CRUD测试类
* 使用JdbcUtils工具类完成数据库增删改查操作
*/
public class JdbcCrudTest {
/**
* 查询测试:查询student表全部数据
* 使用Statement实现查询(存在SQL注入风险)
*/
@Test
public void test7() {
ResultSet resultSet = null;
Statement statement = null;
Connection connection = null;
try {
// 获取数据库连接
connection = JdbcUtils.getConections();
// 编写查询SQL
String sql = "select * from student";
// 创建Statement执行对象
statement = connection.createStatement();
// 执行查询,得到结果集
resultSet = statement.executeQuery(sql);
// 遍历结果集,封装为Student对象
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
Student student = new Student(id, name, age, gender);
System.out.println(student);
}
} catch (SQLException e) {
// SQL异常包装为运行时异常
throw new RuntimeException("查询学生数据失败", e);
} finally {
// 统一关闭所有资源
JdbcUtils.close(connection,statement,resultSet);
}
}
/**
* 新增测试:向student表插入一条学生数据
* 使用PreparedStatement预编译对象,防止SQL注入
*/
@Test
public void test8() {
Connection connection = null;
ResultSet resultSet = null;
PreparedStatement preparedStatement = null;
try {
// 获取数据库连接
connection = JdbcUtils.getConections();
// 带占位符的新增SQL
String sql = "insert into student(name,age,gender) values(?,?,?)";
// 创建预编译执行对象
preparedStatement = connection.prepareStatement(sql);
// 给占位符赋值
preparedStatement.setString(1,"zs");
preparedStatement.setInt(2,1);
preparedStatement.setString(3,"1");
// 执行增删改操作
preparedStatement.executeUpdate();
// 获取受影响的行数
int count = preparedStatement.getUpdateCount();
System.out.println("新增影响行数:" + count);
} catch (SQLException e) {
throw new RuntimeException("新增学生失败", e);
}finally {
// 释放资源
JdbcUtils.close(connection,preparedStatement,resultSet);
}
}
/**
* 删除测试:根据id删除学生记录
* PreparedStatement方式执行删除
*/
@Test
public void test9() {
Connection connection = null;
ResultSet resultSet = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtils.getConections();
// 根据id删除数据,?为占位符
String sql = "delete from student where id = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,7);
preparedStatement.executeUpdate();
// 获取删除影响行数
int count = preparedStatement.getUpdateCount();
System.out.println("删除影响行数:" + count);
} catch (SQLException e) {
throw new RuntimeException("删除学生失败", e);
}finally {
JdbcUtils.close(connection,preparedStatement,resultSet);
}
}
/**
* 修改测试:根据id更新学生年龄
* PreparedStatement执行更新操作
*/
@Test
public void test10() {
Connection connection = null;
ResultSet resultSet = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtils.getConections();
// 更新语句,占位符代替参数
String sql = "update student set age = ? where id = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,80);
preparedStatement.setInt(2,8);
preparedStatement.executeUpdate();
int count = preparedStatement.getUpdateCount();
System.out.println("修改影响行数:" + count);
} catch (SQLException e) {
throw new RuntimeException("更新学生信息失败", e);
}finally {
JdbcUtils.close(connection,preparedStatement,resultSet);
}
}
}
Druid池化技术优化Utils工具类
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/study?useSSL=false
username=root
password=123456
#初始化连接数量
initialSize=10
#最大连接数量
maxActive=50
#最小空闲连接
minIdle=5
初级用法
public class Mytext {
public static void main(String[] args) throws Exception {
//创建连接池对象
DruidDataSource dataSource = new DruidDataSource();
//设置连接池参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/stock?useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("123456");
//初始化连接数量
dataSource.setInitialSize(20);
//最大连接数量
dataSource.setMaxActive(100);
//最小空闲连接
dataSource.setMinIdle(5);
//获取连接
Connection connection = dataSource.getConnection();
//获取PreparedStatement
PreparedStatement preparedStatement = connection.prepareStatement("select * from user where id=?");
//设置参数
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
String uerNo = resultSet.getString("userNo");
String userName = resultSet.getString("userName");
int pwd = resultSet.getInt("pwd");
System.out.println(uerNo + "-" + userName + "-" + pwd);
}
//关闭
resultSet.close();
preparedStatement.close();
connection.close();
}
}
进阶
public class JdbcUtil {
//定义一个类属性
private static DataSource dataSource;
//没有使用连接池之前
//private static String driverName;
//private static String url;
//private static String username;
//private static String password;
static {
try {
Properties prop = new Properties();
//加载配置文件的另一种方式
//InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
//prop.load(in); 使用此方法注意moven项目properties要在resources
prop.load(new FileInputStream("jdbc.properties"));
//创建连接池(读取一个 Properties 对象,根据其中的配置信息,创建并返回一个配置好的 DruidDataSource 实例)
dataSource = DruidDataSourceFactory.createDataSource(prop);
//初始化四参数
//driverName = prop.getProperty("jdbc.driverName");
//url = prop.getProperty("jdbc.url");
//username = prop.getProperty("jdbc.username");
//password = prop.getProperty("jdbc.password");
//加载驱动
//Class.forName(driverName);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
//获取链接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
//Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
//关闭
public static void closerAll(Connection conn, PreparedStatement ps, ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
}
}
使用
public class text1 {
public static void main(String[] args)throws Exception {
//获取链接
Connection conn= JdbcUtil.getConnection();
//获取PreparedStatement
PreparedStatement statement = conn.prepareStatement("select * from user where id= ?");
//设置参数
statement.setInt(1,1);
//发送sql
ResultSet resultSet =Statement.executeQuery();
//处理结果
while(resultSet.next())
{
String userNo = resultSet.getString("userNo");
int Pwd = resultSet.getInt("Pwd");
String userName = resultSet.getString("userName");
System.out.println(userNo+" "+Pwd+" "+userName);
}
JdbcUtil.closerAll(conn,statement,resultSet);
}
}
三种加载方式
prop.load(new FileInputStream("jdbc.properties"));
InputStream in = JdbcUtil.class.getResourceAsStream("/jdbc.properties");
prop.load(in);
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
prop.load(in);
| 方式 | 代码 | 资源查找起点 | 文件存放位置要求 | 打包 Jar 后能否使用 | 路径书写注意事项 | 推荐程度 |
|---|---|---|---|---|---|---|
| 方式 1 | prop.load(new FileInputStream("jdbc.properties")); |
操作系统文件系统,【项目根目录】 | jdbc.properties 直接放在项目根文件夹(和 src 同级) |
❌ 不能!jar 包内资源无法通过 FileInputStream 读取 | 写相对磁盘路径;不要写 classpath 相关路径 | ⭐ 仅本地临时测试,禁止上线 |
| 方式 2 | JdbcUtil.class.getResourceAsStream("jdbc.properties") |
默认:JdbcUtil 所在包内路径开头加/ → classpath 根目录 |
不加/:和 JdbcUtil.java 同一个包加/:放到 resources 下(classpath 根) |
✅ 支持 jar 包读取 | 不带/= 当前包;带/代表 classpath 根 |
⭐⭐ 资源和类放在一起时使用 |
| 方式 3 | JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties") |
classpath(类路径根目录) | resources文件夹内(编译后进入 classes 根目录) |
✅ 支持 jar 包读取 | 路径开头不能写 /,直接写文件名 |
⭐⭐⭐⭐⭐【企业首选,你的 JdbcUtil 就用这个】 |
简答题背诵
- FileInputStream 操作磁盘上独立文件,和 classpath 无关,只能读取解压出来的文件,无法读取 jar 包内部资源。
xxx.class.getResourceAsStream()基于类的包路径进行查找,支持相对包路径,加斜杠切换到 classpath 根。类加载器.getResourceAsStream()统一从 classpath 根目录 搜索资源,所有 resources 下的文件编译后都在 classpath 根,写法统一,工业界标准写法。