java学习笔记

JDBC 基础

之前一直有简单的了解,今天依靠着文档希望可以系统深入的学习一下

其实内心觉得学的还是有点慢了,希望后面可以加快进度

依旧是参考

1
https://www.javasec.org/javase/JDBC/

很详细很全面的一篇文章,可以学到很多东西

JDBC(Java Database Connectivity)是Java提供对数据库进行连接、操作的标准API。Java自身并不会去实现对数据库的连接、查询、更新等操作而是通过抽象出数据库操作的API接口(JDBC),不同的数据库提供商必须实现JDBC定义的接口从而也就实现了对数据库的一系列操作

JDBC Connection

Java通过java.sql.DriverManager来管理所有数据库的驱动注册,所以如果想要建立数据库连接需要先在java.sql.DriverManager中注册对应的驱动类,然后调用getConnection方法才能连接上数据库。

JDBC定义了一个叫java.sql.Driver的接口类负责实现对数据库的连接,所有的数据库驱动包都必须实现这个接口才能够完成数据库的连接操作。java.sql.DriverManager.getConnection(xx)其实就是间接的调用了java.sql.Driver类的connect方法实现数据库连接的。数据库连接成功后会返回一个叫做java.sql.Connection的数据库连接对象,一切对数据库的查询操作都将依赖于这个Connection对象。

JDBC连接数据库的一般步骤:

1、注册驱动,Class.forName(“数据库驱动的类名”)。
2、获取连接,DriverManager.getConnection(xxx)。

简单实操一下
首先docker启动一下mysql

但是执行代码是出现了问题

原来是没下载依赖

这里学一下如何把项目构建成maven项目

首先下载了maven,并改一下settings.xml文件
在.zshrc里面export了一下 这里注意java版本

然后重构一下项目,其实这里发现我电脑里的jdk版本有点混乱了出现了很多问题,最后还是新建的比较好。

在pom.xml里面添加一下依赖

成功连接

DataSource

在真实的java项目中通常不会使用原生的JDBC的DriverManager去连接数据库,而是使用数据源(javax.sql.DataSource)来代替DriverManager,一般情况下在Web服务启动时候会先预定义好数据源,有了数据源程序就不再需要编写任何与数据库连接相关的代码了,直接引用DataSource对象既可以获取数据库连接。

常见的数据源有:DBCP、C3P0、Druid、Mybatis DataSource,他们都实现于javax.sql.DataSource接口。

还是有一些抽象的东西之前不是很了解,我还是去查了一下

这里为什么会使用数据源是因为DriverManager是JDBC的早期设计,用于获取数据库连接,每次调用DriverManager.getConnection()时,它会尝试建立一个新的数据库连接,直到连接成功。这种方式在高并发的应用中性能较差,因为频繁创建和销毁数据库连接非常耗时和资源。

以DBCP为例:DBCP 是 Apache 提供的一个连接池实现,它通过实现 javax.sql.DataSource 接口,管理数据库连接池。

它支持配置连接池的最大连接数、最小连接数、最大空闲时间等。
这里本地简单的配置了一下,不得不承认maven真的很好用,可以很轻松的加载一些配置

Spring MVC 数据源

Spring MVC 是一个基于模型-视图-控制器(MVC)架构的 Web 框架。它可以通过配置数据源来实现与数据库的交互。Spring MVC 支持通过配置数据源来选择第三方数据库连接池(如 Druid),以便与数据库进行连接和操作。

  1. 基于 XML 配置的数据源

在传统的 Spring MVC 中,我们通过 XML 配置来创建和初始化数据源。Spring 使用 标签定义 Java Bean,在配置文件中配置数据源及其属性(如数据库 URL、用户名和密码等)。下面是一个使用 Druid 数据源配置的示例

配置步骤:
在 XML 配置文件中定义 DataSource Bean:

1
2
3
4
5
6
7
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<!-- 可以根据需要添加更多配置 -->
</bean>

id=”dataSource” 是 Bean 的唯一标识符,Spring 会根据该标识符注入 Bean。

class=”com.alibaba.druid.pool.DruidDataSource” 表示使用 Druid 数据源连接池。

init-method=”init” 和 destroy-method=”close” 指定了初始化和销毁方法,这对连接池很重要。

配置数据库连接参数(如 URL、用户名和密码): 在 jdbc.properties 文件中,您可以定义数据库的连接信息(例如,URL、用户名、密码等)。使用 property-placeholder 机制来加载外部配置文件。

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useOldAliasMetadataBehavior=true&useSSL=false
jdbc.username=root
jdbc.password=root

在 Spring 配置文件中加载外部的 properties 文件:

1
2
<context:property-placeholder location="classpath:/config/jdbc.properties"/>
通过 <context:property-placeholder> 标签,Spring 会自动加载 jdbc.properties 文件中的配置信息。${jdbc.username} 和 ${jdbc.password} 等占位符会被解析为实际的配置值。

配置 JdbcTemplate 来使用数据源: 使用 JdbcTemplate 是 Spring 提供的一个 JDBC 数据访问工具,它可以简化与数据库的交互。您可以在配置文件中将 JdbcTemplate Bean 与数据源 Bean 进行关联。

1
2
3
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false" lazy-init="false">
<property name="dataSource" ref="dataSource"/>
</bean>

感觉要熬穿了没有时间去实践了,希望后面有时间可以实操一下(

这些资料可以更清晰的明白这几个东西的关系

SpringBoot配置数据源:

在SpringBoot中只需要在application.properties或application.yml中定义spring.datasource.xxx即可完成DataSource配置。

1
2
3
4
5
6

spring.datasource.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useOldAliasMetadataBehavior=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

JDBC SQL注入

文章只讨论了基础的sql注入,和其他数据库的注入方式感觉没什么区别,就不多看了

SQL注入防御

主要看看有没有特殊的方式来防御sql注入

1、转义用户请求的参数值中的’(单引号)、”(双引号)。

2、限制用户传入的数据类型,如预期传入的是数字,那么使用:Integer.parseInt()/Long.parseLong等转换成整型。

3、使用PreparedStatement对象提供的SQL语句预编译。

最好的办法还是使用参数预编译

PreparedStatement SQL预编译查询

首先在tomcat部署一下环境,连接数据库后自己创建一个testdb,在myapps/myapp写一下jsp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<%@ page import="java.sql.*" %>
<%@ page import="java.io.StringWriter" %>
<%@ page import="java.io.PrintWriter" %>
<%
String jdbcUrl = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
String dbUser = "root"; // 改成你的用户名
String dbPass = "123456"; // 改成你的密码

Connection conn = null;
Statement stmt = null;
PreparedStatement pstt = null;
ResultSet rs = null;

String id = request.getParameter("id");

try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
stmt = conn.createStatement();

// 创建表和插入数据(只执行一次)
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS sys_user (id INT PRIMARY KEY, username VARCHAR(100), email VARCHAR(100))");
stmt.executeUpdate("INSERT IGNORE INTO sys_user (id, username, email) VALUES (1, 'Alice', 'alice@example.com'), (2, 'Bob', 'bob@example.com'), (3, 'Charlie', 'charlie@example.com')");

String sql = "SELECT id, username, email FROM sys_user WHERE id = ?";
pstt = conn.prepareStatement(sql);
pstt.setObject(1, id != null ? id : "1"); // 默认查询 ID=1
rs = pstt.executeQuery();

out.println("<h3>查询结果:</h3>");
out.println("<table border='1'>");
out.println("<tr><th>ID</th><th>用户名</th><th>邮箱</th></tr>");

while (rs.next()) {
out.println("<tr>");
out.println("<td>" + rs.getInt("id") + "</td>");
out.println("<td>" + rs.getString("username") + "</td>");
out.println("<td>" + rs.getString("email") + "</td>");
out.println("</tr>");
}
out.println("</table>");
}catch (Exception e) {
out.println("<pre>");
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
out.println(sw.toString());
out.println("</pre>");
}finally {
try { if (rs != null) rs.close(); } catch (Exception e) {}
try { if (pstt != null) pstt.close(); } catch (Exception e) {}
try { if (stmt != null) stmt.close(); } catch (Exception e) {}
try { if (conn != null) conn.close(); } catch (Exception e) {}
}
%>

关键在于

1
String sql = "SELECT id, username, email FROM sys_user WHERE id = ?";

Java代码改为?(问号)占位的方式即可实现SQL预编译查询。

需要特别注意的是并不是使用PreparedStatement来执行SQL语句就没有注入漏洞,而是将用户传入部分使用?(问号)占位符表示并使用PreparedStatement预编译SQL语句才能够防止注入!

还有之前对tomcat的部署也不是很熟悉,不知道为什么会使用jsp文件

其实是因为:JSP 本质是 Servlet,但 Tomcat 会自动将 JSP 编译成 Servlet,然后运行。

简单部署了一下

JDBC预编译

1
2
3
JDBC预编译查询分为客户端预编译和服务器端预编译,对应的URL配置项是:useServerPrepStmts,
当useServerPrepStmts为false时使用客户端(驱动包内完成SQL转义)预编译,
useServerPrepStmts为true时使用数据库服务器端预编译。