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注入 / 事务 / 批处理 / 结果集 / 连接池
多快好省!前端后端,线上线下,名师精讲
更多了解 加: