1. MySQL补充笔记
1 2 3 4 5 6
| show databases; # 显示所有数据库 select database(); # 显示当前使用的数据库 show tables; # 显示当前数据库中的所有表 desc 表名; # 显示表的信息 rename table 原表名 to 新表名; # 修改表名 select * from t_user limit [行数偏移量,] 行数; # 行数偏移量从 0 开始,用于分页查询
|
2. JDBC笔记
2.1. 查询数据库全部内容 代码示例
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
| public static void selectAll() { Connection con = null; Statement stmt = null; ResultSet rs = null;
try { Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/userInfo?useUnicode=true&characterEncoding=utf-8"; String user = "root"; String password = "12345678"; con = DriverManager.getConnection(url, user, password);
stmt = con.createStatement(); rs = stmt.executeQuery("select * from t_user");
while(rs.next()) System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getString("pwd")); } catch (Exception e) { e.printStackTrace(); } finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
|
2.2. 登录检验 代码示例 (可能会造成 sql 注入)
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
| public static boolean checkUserInfo(String uname, String pwd) {
Connection con = null; Statement stmt = null; ResultSet rs = null;
try { Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/userInfo";
con = DriverManager.getConnection(url, "root", "12345678"); stmt = con.createStatement(); String sql = "select * from t_user where uname = '" + uname + "' and pwd = '" + pwd + "'"; rs = stmt.executeQuery(sql);
return rs.next();
} catch (Exception e) { e.printStackTrace(); } finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }
return false; }
|
2.3. 登录检验 代码示例 (使用 preparestatement 解决 SQL 注入问题)
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
| public static boolean checkUserInfo2(String name, String pwd) { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null;
try { String url = "jdbc:mysql://localhost:3306/userInfo"; con = DriverManager.getConnection(url, "root", "12345678"); String sql = "select * from t_user where uname = ? and pwd = ?"; pstmt = con.prepareStatement(sql); pstmt.setString(1, name); pstmt.setString(2, pwd); rs = pstmt.executeQuery(); return rs.next();
} catch (SQLException e) { e.printStackTrace(); } finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(pstmt != null) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } ; if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } return false; }
|
对 PreparedStatement 防止 sql 注入的简单理解
假设 Java 中的查询语句为:
1
| String sql = "select * from t_user where uname = ? and pwd = ?";
|
添加参数
1 2
| pstmt.setString(1, name); pstmt.setString(2, pwd);
|
若为正常的参数填充 name = "gukaifeng"
, pwd = "123456"
输出最终的查询语句 System.out.println(pstmt);
结果为:
1
| com.mysql.cj.jdbc.ClientPreparedStatement: select * from t_user where uname = 'gukaifeng' and pwd = '123456'
|
可以看到,原参数是没有单引号的,单引号由 PreparedStatement 自动添加。
若有 sql 注入的参数填充 nam e= "gukaifeng"
,pwd = "123456' or '1' = '1"
输出最终的查询语句 System.out.println(pstmt);
结果为:
1
| com.mysql.cj.jdbc.ClientPreparedStatement: select * from t_user where uname = 'gukaifeng' and pwd = '123456'' or ''1'' = ''1'
|
可以看到,除了由 PreparedStatement 在原字符串两边自动添加的单引号外,字符串本身的单引号都被换成了两个。
暂时没有弄懂为什么可以防止注入,我试验的结果是加了每个单引号都变为两个。
另外的试验发现,如果验证信息中本来就有单引号,虽然变成了两个,依然可以正确的查询。
网上资料有人的试验结果是给单引号加了转义,与我的结果不同。
但是究竟 PreparedStatement 如何防止注入,又可以用本来就含有单引号的查询参数正常查询,还是没能够理解,这里先记录一个尾巴。
2.4. JDBCUtils
就是把重复的代码封装一下,下面是封装后重写上面的 checkUserInfo2。
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 55 56 57 58 59
| package com.jdbc.demo;
import java.sql.*;
public class JDBCUtils {
private static final String SQLURL = "jdbc:mysql://localhost:3306/userInfo"; private static final String USER = "root"; private static final String PASSWORD = "12345678";
public static Connection getConnection() {
try { Class.forName("com.mysql.cj.jdbc.Driver"); return DriverManager.getConnection(SQLURL, USER, PASSWORD); } catch (Exception e) { e.printStackTrace(); } return null; } public static PreparedStatement getPreparedStatement(Connection con, String sql, String... p) { if(con != null) try { PreparedStatement pstmt = con.prepareStatement(sql); for(int i = 0; i < p.length; ++i) pstmt.setString(i + 1, p[i]); return pstmt; } catch (SQLException e) { e.printStackTrace(); } return null; } public static void Close(Connection con, PreparedStatement pstmt, ResultSet rs) { if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(pstmt != null) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public static boolean checkUserInfo2(String name, String pwd) { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null;
try { con = JDBCUtils.getConnection(); String sql = "select * from t_user where uname = ? and pwd = ?"; pstmt = JDBCUtils.getPreparedStatement(con, sql, name, pwd); rs = pstmt.executeQuery(); return rs.next(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.Close(con, pstmt, rs); } return false; }
|
2.4.1. 插入操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void insert(String name, String pwd) { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con = JDBCUtils.getConnection(); String sql = "insert into t_user(uname, pwd) values (?, ?)"; pstmt = JDBCUtils.getPreparedStatement(con, sql, name, pwd); if (pstmt != null) { int affected = pstmt.executeUpdate(); System.out.println(affected); } } catch(Exception e) { e.printStackTrace(); } finally { JDBCUtils.Close(con, pstmt, rs); } }
|
2.4.2. 删除操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void delete(int uid) { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con = JDBCUtils.getConnection(); String sql = "delete from t_user where uid = ?"; pstmt = con.prepareStatement(sql); pstmt.setInt(1, uid); int affected = pstmt.executeUpdate(); } catch(Exception e) { e.printStackTrace(); } finally { JDBCUtils.Close(con, pstmt, rs); } }
|
2.4.3. 修改操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void update(int uid, String newPassword) { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con = JDBCUtils.getConnection(); String sql = "update t_user set pwd = ? where uid = ?"; pstmt = con.prepareStatement(sql); pstmt.setString(1, newPassword); pstmt.setInt(2, uid); int affected = pstmt.executeUpdate(); } catch(Exception e) { e.printStackTrace(); } finally { JDBCUtils.Close(con, pstmt, rs); } }
|
2.5. 事务
1 2 3 4 5
| con.setAutoCommit(false);
con.commit();
|
2.6. 使用 C3P0 连接池
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| package com.jdbc;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;
public class C3P0DataSources { private static final String URL = "jdbc:mysql://localhost:3306/userInfo"; private static final String USERNAME = "root"; private static final String PASSWORD = "12345678"; private static ComboPooledDataSource ds;
static { try { ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.cj.jdbc.Driver"); ds.setJdbcUrl(URL); ds.setUser(USERNAME); ds.setPassword(PASSWORD);
ds.setInitialPoolSize(5); ds.setMaxPoolSize(20); } catch (PropertyVetoException e) { e.printStackTrace(); } }
public static Connection getConnection() { try { return ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } public static void ConnectionClose(Connection con) { if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void PreparedStatementClose(PreparedStatement pstmt) { if(pstmt != null) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void ResultSetClose(ResultSet rs) { if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
|
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
| public static void selectAll() { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null;
try { con = C3P0DataSources.getConnection(); if(con != null) { String sql = "select * from t_user"; pstmt = con.prepareStatement(sql); rs = pstmt.executeQuery(); while (rs.next()) { System.out.println(rs.getInt("uid") + "\t" + rs.getString("uname") + "\t" + rs.getString("pwd") + "\t" + rs.getInt("balance")); } } } catch(Exception e) { e.printStackTrace(); } finally { C3P0DataSources.ConnectionClose(con); C3P0DataSources.PreparedStatementClose(pstmt); C3P0DataSources.ResultSetClose(rs); } }
|