import java.sql.*;
演示:java.sql中定义的接口等
但要连接上数据库,还需要相应的驱动。我们使用(由Oracle提供的)MySQL JDBC驱动。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
直接使用DriverManager的静态方法getConnection()获取。
可以直接一个连接字符串搞定
String connStr = "jdbc:mysql://localhost:3306/17bang?user=root&useSSL=false&verifyServerCertificate=false"; Connection conn = DriverManager.getConnection(connStr);
注意从workbench复制之后,还要可能需要添加:
还可以user,password分别传入:
不建议使用properties,太丑,^_^
Statement insert = conn.createStatement();然后调用execute ()方法,传入SQL语句:
insert.execute("INSERT Student VALUES(5, 'bo')");
如果要知道影响了多少行,可以使用executeUpdate()方法
int affectedRowsCount = insert.executeUpdate("UPDATE Student SET name= concat('yz-', name);");
注意executeUpdate()和execute()的区别仅限于返回值不同,他们理论上都可以执行任何SQL语句:
复习:SQL注入
所以我们应该使用PreparedStatement(Statement的子类),且其SQL语句中的参数用问号(?)表示
PreparedStatement pDel = conn.prepareStatement( "DELETE FROM Student WHERE id = ?;");然后利用setXXX()方法设置参数值:
//第一个参数的值为整数2(int) pDel.setInt(1, 2); // 注意下标从1开始
还可以有多个参数,多种类型:
PreparedStatement pUpdate = conn.prepareStatement( "UPDATE Student SET name= concat(?, name) WHERE id = ?;"); pUpdate.setString(1, "vip-"); pUpdate.setInt (2, 5);
set global general_log=on;

String input = new Scanner(System.in).nextLine();输入:fg' OR 1=1; #,对比:
String sql = "SELECT COUNT(*) FROM Student WHERE name= '" + input + "'"; ResultSet rs = conn.createStatement().executeQuery(sql);
String sql = "SELECT COUNT(*) FROM Student WHERE name= ?"; PreparedStatement statement = conn.prepareCall(sql); statement.setString(1, input);
SELECT COUNT(*) FROM Student WHERE name= 'fg\' OR 1=1;#'
比较特殊的有:
pUpdate.setNull(3, Types.INTEGER);
pUpdate.setDate(1, Date.valueOf("2021-09-25"));
pUpdate.setTime(2, Time.valueOf(LocalTime.now()));
//两个getTime()能看懂么? new Date(Calendar.getInstance().getTime().getTime()) //两个Date重名了 new Date(new java.util.Date().getTime())
PS:其实哪怕是没有参数,用prepareStatement也是OK的。
ResultSet students = conn.prepareStatement("SELECT * FROM Student").executeQuery();
然后,调用其next()方法,获取所有结果:
while (students.next()) {
System.out.println(
"id=" + students.getInt(1) + " "+
"name=" + students.getString("name"));
}
演示:关闭连接后报异常
推荐优先使用try (resource)来自动释放
try(Connection conn = DriverManager.getConnection(connStr)){
@想一想@:为什么?因为总是关闭,类似于conn.close();写在finally里。
另外,Statement等也是实现了AutoCloseable的。但是,关闭连接会自动关闭由其创建的所有资源。
//也可以是Statement
PreparedStatement pUpdate = conn.prepareStatement(
"UPDATE Student SET name = CONCAT('p-', name) WHERE id=?");
for (int i = 0; i < ids.length; i++) {
pUpdate.setInt(1, i);
pUpdate.addBatch(); //往里面加
}
//一次性的执行
pUpdate.executeBatch();
打开general_log演示:直到executeBatch()执行,数据库没有被要求执行。conn.setAutoCommit(false);
try {
//多条SQL语句
conn.commit();
} catch (Exception e) {
conn.rollback();
} finally {
//千万不要忘记这个!
conn.setAutoCommit(true);
}
结合general log演示
-- 检查数据库17bang中有没有表student,用@result表示结果
call sys.table_exists('17bang', 'student', @result);
JDBC为存储过程调用准备了特别的API:
CallableStatement proc = conn.prepareCall("call sys.table_exists(?, ?, ?)");
存储过程的参数,如果是:
proc.setString(1, "17bang"); proc.setString(2, "student");
//参数下标只管位置,不区分参数是输入还是输出 proc.registerOutParameter(3, Types.VARCHAR);最后通过getXXX()方法获得
System.out.println(proc.getString(3));
不要忘了调用:
proc.execute();
据说是最好用的一个,^_^,
maven地址:https://mvnrepository.com/artifact/com.zaxxer/(因为众所周知的原因,很难打开)
首先要配置连接池:
HikariConfig config = new HikariConfig();
//以下和配置连接一样的
config.setJdbcUrl("jdbc:mysql://localhost:3306/17bang");
config.setUsername("root");
config.setPassword("");
//以下是池所独有的
config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒
config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10
根据配置获取一个池实例()
DataSource pool = new HikariDataSource(config);
然后从池中拿连接:
try (Connection conn = ds.getConnection()) { // 在此获取连接
@想一想@:是不是我们自己都可以做?
注意事项:
见:持久化:ADO&JDBC:driver / 连接 / SQL注入 / 事务 / 批处理 / 结果集 / 连接池
多快好省!前端后端,线上线下,名师精讲
更多了解 加: