JDBC入门:Java连接数据库全指南
JDBC简介
JDBC(Java Database Connectivity)是 Java 语言中用来规范客户端程序如何访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。它允许开发者使用纯 Java 代码与各种关系型数据库进行交互。
JDBC连接数据库的基本流程
- 加载数据库驱动:
DriverManager.registerDriver(new Driver()); // 或者 Class.forName("com.mysql.jdbc.Driver");
- 建立数据库连接:
conn = DriverManager.getConnection("jdbc:mysql:///jdbcdemo", "root", "root");
- 创建Statement对象:
stmt = conn.createStatement();
- 执行SQL语句:
String sql = "select * from t_user"; rs = stmt.executeQuery(sql);
- 处理结果集:
while(rs.next()){int id = rs.getInt("id");String username = rs.getString("username");String password = rs.getString("password");String email = rs.getString("email");System.out.println(id+"-"+username+"-"+password+"-"+email); }
- 释放资源(按创建顺序的逆序关闭):
rs.close(); stmt.close(); conn.close();
JDBC工具类封装
基础工具类封装
这是最简单的工具类封装,包含了加载驱动、获取连接、创建 Statement 和关闭资源的基本方法。
public class JdbcUtils3 {public static void loadDrive(){try {Class.forName("com.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConntion(){loadDrive();Connection connection = null;try {connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");} catch (SQLException e) {e.printStackTrace();}return connection;}public static Statement creatstmt() throws SQLException {Connection conntion = getConntion();Statement statement = conntion.createStatement();return statement;}public static void close(ResultSet rs, Statement stmt, Connection conn){try {rs.close();stmt.close();conn.close();}catch (Exception e){e.printStackTrace();}}
}
使用属性文件配置的工具类
public class JdbcUtils {private static final String driverclass;private static final String url;private static final String username;private static final String password;static{Properties pro = new Properties();InputStream inputStream = JdbcUtils.class.getResourceAsStream("/db.properties");try {pro.load(inputStream);} catch (IOException e) {e.printStackTrace();}driverclass = pro.getProperty("driverclass");url = pro.getProperty("url");username = pro.getProperty("username");password = pro.getProperty("password");}// 其他方法...
}
对应的db.properties文件内容:
driverclass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcdemo
username=root
password=root
使用连接池的工具类
在实际应用中,使用连接池可以显著提高性能。这里使用了 Druid 连接池:
public class JdbcUtils2 {private static DataSource DATA_SOURCE;static{Properties pro = new Properties();InputStream inputStream = JdbcUtils2.class.getResourceAsStream("/druid.properties");try {pro.load(inputStream);DATA_SOURCE = DruidDataSourceFactory.createDataSource(pro);} catch (Exception e) {e.printStackTrace();}}public static Connection getConnection(){Connection conn = null;try {conn = DATA_SOURCE.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;}// 其他方法...
}
对应的druid.properties文件需要配置连接池相关参数。
SQL注入问题与规范
SQL注入问题演示
注意:此时是正确的用户名和密码是aaa,“123”
public String login(String username,String password){// ...String sql = "select * from t_user where username = '"+username+"' and password = '"+password+"'";stmt = conn.createStatement();rs = stmt.executeQuery(sql);// ...
}
当传入username = “aaa ‘or’ 1=1”和任意密码时,SQL语句会变成:
select * from t_user where username = 'aaa' or '1=1' and password = '12309809'
由于‘1=1’恒为真,且在sql语句中“and”的优先级比“or”更高,所以攻击者可以绕过密码验证直接登录成功。
使用preparedStatement防范SQL注入
public String login2(String username,String password){// ...String sql = "select * from t_user where username = ? and password = ?";stmt = conn.prepareStatement(sql);stmt.setString(1,username);stmt.setString(2,password);rs = stmt.executeQuery();// ...
}
PreparedStatement 通过预编译 SQL 语句和使用参数化查询,有效防止了 SQL 注入:
- SQL语句先被编译,语法结构固定
- 参数值通过set方法设置,会被当做普通值处理,不会解释为SQL语法
- 即使传入恶意参数,数据库也会将其视为普通字符串
最佳实践建议
- 始终使用PreparedStatement而不是Statement,防止SQL注入
- 使用连接池管理数据库连接,提高性能
- 将配置信息外置到属性文件中,便于维护
- 规范资源释放,确保数据库连接等资源被正确关闭
- 处理异常,提供有意义的错误信息
- 考虑使用ORM框架(如Mybatis、Hibernate)简化JDBC操作
总结
JDBC 是 Java 操作数据库的基础,掌握其核心流程、工具类封装和 SQL 注入防范是每个 Java 开发者的必备技能。通过合理封装工具类和使用连接池,可以显著提高代码质量和应用性能。而 PreparedStatement 的使用则是防范 SQL 注入的关键措施。
在实际项目中,虽然我们通常会使用更高级的持久层框架,但理解 JDBC 底层原理对于排查问题和优化性能仍然非常重要。