此笔记是 B站 动力结点 老杜 老师的课程笔记
可能您学了MySQL数据库之后有疑问了,我操作数据库都是在命令行里,终端里控制的数据库,那我怎么讲数据库应用到网站上呢。这里就讲解释,数据库可以用Java程序控制,即JDBC,当然数据库也可以由python(模块包pymysql)等控制。这些编程语言本身就可以做游戏和网站,在登录或执行其他操作时就会调用数据库。
JDBC
Java DataBase Connectivity(Java语言连接数据库)
本质是:由SUN公司制定的 一套 接口(interface)(属于面向接口编程)
java.sql.*;
可以在api参考文档中看的内容
Java.sql包
接口 |
用途 |
Connection接口 |
获取连接 getConnection() |
Driver接口 |
获取连接DriverManager() |
ResultSet接口 |
执行DQL语句(select) |
Statement接口 |
获取数据库操作对象 createStatement() |
JDBC的六个步骤(背下来)
注册驱动
(作用:告诉Java程序,即将连接的是哪个品牌的数据库)
1 2 3 4 5 6 7 8 9 10
| import java.sql.Driver; import java.sql.DriverManager;
Driver driver = new com.mysql.jdbc.Driver; DriverManager.registerDriver(driver);
Class.forName("com.mysql.jdbc.Driver");
|
建立连接
(表示JVM的进程和数据库进程之间的通道打开了,属于进程间通信,使用完一定要关闭)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.sql.Connection;
String url="jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=true"; String user="root"; String password="123456"; Connection connection = DriverManager.getConnection(url,user,password);
|
获得数据库操作对象
(专门执行sql语句的对象)
1 2 3
| import java.sql.Statement;
Statement statement = connection.createStatement();
|
执行SQL语句
statement.execteUpdate()执行DML语句(insert、delete、update)
statement.excuteQuery()执行DQL语句(select)
(主要执行DQL和DML等)
1 2 3 4 5 6
| String sql = "INSERT TNTO 表名(字段名1,字段名2) VALUES(值1,值2)";
int count = statement.executeUpdate(sql);
|
处理查询集
(只有当第4步执行的是SELECT语句时,才有这第5步)
select才是查询结果
看查询结果集
释放资源
(使用完资源后一定要关闭,Java和数据库属于进程间通信,一定要关闭)
1 2 3 4 5 6 7
| if(statement!=null){ statement.close(); } if(connection!=null){ connection.close(); }
|
整体代码
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
| import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement;
public class JDBCTest1 { public static void main(String[] args) throws SQLException { Statement stmt=null; Connection conn=null; try {
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
String url = "jdbc:mysql://localhost:3306/MySql?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT"; String user ="root"; String password="111111"; conn=DriverManager.getConnection(url,user,password); System.out.println("数据库连接的对象 =" +conn); stmt= conn.createStatement(); String sql="insert into dept(deptno,dname,loc) values(50,'人事部','北京')"; int count=stmt.executeUpdate(sql); System.out.println(count==1?"保存成功":"保存失败");
}catch (SQLException e){ e.printStackTrace(); }finally { try { if (stmt!=null){ stmt.close(); } }catch (SQLException e) { e.printStackTrace(); } try { if (conn!=null){ conn.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 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
| import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ResourceBundle;
public class JDBCTest4 { public static void main(String[] args) throws SQLException{ ResourceBundle bundle=ResourceBundle.getBundle("jdbc"); String driver=bundle.getString("driver"); String url=bundle.getString("url"); String user=bundle.getString("user"); String password=bundle.getString("password"); Connection conn=null; Statement stmt=null; try { Class.forName(driver); conn=DriverManager.getConnection(url,user,password); stmt=conn.createStatement(); String sql="delete from dept where deptno=40"; int count=stmt.executeUpdate(sql); System.out.println(count==1 ?"删除成功":"删除失败"); }catch (SQLException | ClassNotFoundException e){ e.printStackTrace(); }finally { if (stmt!=null){ try { stmt.close(); }catch (SQLException e){ e.printStackTrace(); } } if (conn!=null){ try { conn.close(); }catch (SQLException e){ e.printStackTrace(); } } } } }
|
处理查询结果集
使用ResultSet的next()方法
如果该位置有数据就返回true,所以用while循环
取数据用getString()方法
注意,不论数据库中的数据类型是什么最好都以String的形式取出
但是可以用其他的类型double就用getDouble()….(需要取出结果计算的情况下)不过还是要和数据库中的类型一致
JDBC中所有下标都是从1开始。不是从0开始
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 70 71 72 73 74 75 76 77 78 79 80
| import java.sql.*; import java.util.ResourceBundle;
public class JDBCTest5 { public static void main(String[] args) { Connection conn=null; Statement stmt=null; ResultSet rs=null; try { Class.forName("com.mysql.cj.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/MySql?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT" ,"root","111111"); stmt=conn.createStatement(); String sql="select empno,ename,sal from emp"; rs=stmt.executeQuery(sql);
while (rs.next()){
String empno=rs.getString("empno"); String ename=rs.getString("ename"); String sal=rs.getString("sal"); System.out.println(empno+","+ename+","+sal); }
}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 (conn!=null){ try { conn.close(); }catch (SQLException e){ e.printStackTrace(); } } }
} }
|
模拟用户登录,发现SQL注入现象
导致SQL注入的根本原因:
用户输入的信息中含有sql语句的关键字,并且这些关
导致sql语句的原意被扭曲,进而达到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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Scanner;
public class Test07_login_ { public static void main(String[] args) { Map<String,String> userLoginInfo = initUI(); boolean loginSuccess = login(userLoginInfo); System.out.println(loginSuccess? "登录成功" : "登录失败"); }
public static Map<String,String> initUI(){ Scanner s = new Scanner(System.in); System.out.print("用户名:"); String loginName = s.nextLine(); System.out.print("密码:"); String loginPwd = s.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",loginName); userLoginInfo.put("loginPwd",loginPwd); return userLoginInfo; }
@SuppressWarnings({"all"}) public static boolean login(Map<String,String> userLoginInfo){ boolean is_longin = false; Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String passwd = "123456"; conn = DriverManager.getConnection(url,user,passwd); stmt = conn.createStatement(); String sql = "select * from t_user where loginName = '" + userLoginInfo.get("loginName") + "' and loginPwd = '" + userLoginInfo.get("loginPwd") + "'"; rs = stmt.executeQuery(sql); if(rs.next()){ is_longin =true; } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { try{ if(stmt != null){ stmt.close(); } if(conn != null){ conn.close(); } } catch (SQLException e){ e.printStackTrace(); } } return is_longin; } }
|
Statement和PreparedStatement对比
-Statement存在SQL注入问题;PreparedStatement解决了SQL注入问题
-Statement是编译一次执行一次;PreparedStatement是编译一次可执行N次;PreparedStatement执行效率较高一些
-PreparedStatement会在编译阶段做类型的安全检查
综上所述:PreparedStatement使用较多,极少数情况下使用Statement
什么情况下必须使用Statement?
业务方面要求必须支持SQL注入时。(PreparedStatement无法支持SQL注入)
Statement支持SQL注入,凡是业务方面需要进行sql语句拼接时,必须使用Statement
只需要给sql语句传值时使用:PreparedStatement
解决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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.Scanner;
public class Test08_solve_SQL_in { public static void main(String[] args) { Map<String,String> userLoginInfo = initUI(); boolean loginSuccess = login(userLoginInfo); System.out.println(loginSuccess? "登录成功" : "登录失败"); }
public static Map<String,String> initUI(){ Scanner s = new Scanner(System.in); System.out.print("用户名:"); String loginName = s.nextLine(); System.out.print("密码:"); String loginPwd = s.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",loginName); userLoginInfo.put("loginPwd",loginPwd); return userLoginInfo; }
@SuppressWarnings({"all"}) public static boolean login(Map<String,String> userLoginInfo){ boolean is_longin = false; Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null; try{ Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String passwd = "123456"; conn = DriverManager.getConnection(url,user,passwd);
String sql = "select * from t_user where loginName = ? and loginPwd = ?"; ps = conn.prepareStatement(sql);
ps.setString(1,userLoginInfo.get("loginName")); ps.setString(2,userLoginInfo.get("loginPwd"));
rs = ps.executeQuery(); if(rs.next()){ is_longin =true; } } catch(SQLException | ClassNotFoundException e){ e.printStackTrace(); } finally { try{ if(rs != null){ rs.close(); } if(ps != null){ ps.close(); } if(conn != null){ conn.close(); } } catch (SQLException e){ e.printStackTrace(); } } return is_longin; } }
|
Statement 用处
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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| import java.sql.*; import java.util.Objects; import java.util.Scanner;
public class Test09_desc_and_asc { public static void main(String[] args) { Scanner s = new Scanner(System.in); System.out.println("输入1升序,输入2降序"); String keyWord = null; keyWord = s.nextLine(); if(Objects.equals(keyWord, "1")){ keyWord = "asc"; }else if(Objects.equals(keyWord, "2")){ keyWord = "desc"; }
Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8","root","123456"); stmt = conn.createStatement();
String sql = "select name,english from student order by english " + keyWord;
rs = stmt.executeQuery(sql);
while (rs.next()){ System.out.println(rs.getString("name") + " " + rs.getString("english")); } } catch(ClassNotFoundException | SQLException e){ e.printStackTrace(); } finally { try{ if(rs != null){ rs.close(); } if(stmt != null){ stmt.close(); } if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } }
|
使用PreparedStatement完成增删改
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
| import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException;
public class Test10_c_d_u { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
String sql_u = "update t_vip2 set name = ? where id = ?"; ps = conn.prepareStatement(sql_u);
ps.setString(1,"zhangsan"); ps.setInt(2,2); ps.executeUpdate();
} catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } finally { try{ if(ps != null){ ps.close(); } if(conn != null){ conn.close(); } } catch(SQLException e){ e.printStackTrace(); } } } }
|
JDBC工具类的封装
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 70 71
| import java.sql.*;
public class DBUtils {
static { try{ Class.forName("com.mysql.jdbc.Driver"); } catch(ClassNotFoundException e){ e.printStackTrace(); } }
public static Connection getConnection(String url,String user,String passwd){ Connection conn = null; try { conn = DriverManager.getConnection(url, user, passwd); } catch (SQLException e){ e.printStackTrace(); } return conn; } public static Connection getConnection() { Connection conn = null; try { String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String passwd = "123456"; conn = DriverManager.getConnection(url, user, passwd); } catch (SQLException e){ e.printStackTrace(); } return conn; }
public static void close(Connection conn, Statement stmt, ResultSet rs){ try{ if(rs != null){ rs.close(); } if(stmt != null){ stmt.close(); } if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } public static void close(Connection conn, Statement stmt){ try{ if(stmt != null){ stmt.close(); } if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
|
事务机制
主要代码
1 2 3
| conn.setAutoCommit(false); conn.commit(); conn.rollback();
|
注意是连接对象的操作函数
整体代码
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 70 71 72 73 74
| import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.ResultSet;
public class Test12_transaction { public static void main(String[] args) throws SQLException { Connection conn = DBUtils.getConnection(); ResultSet rs; String sql = "select * from t_act where actno = ? or actno = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1,111); ps.setInt(2,222); rs = ps.executeQuery(); System.out.println("操作前查询结果:"); while(rs.next()){ System.out.println("actno: " + rs.getString("actno") +" balance: " + rs.getString("balance")); }
String sql2 = "update t_act set balance = balance + ? where actno = ? and balance >= ?"; ps = conn.prepareStatement(sql2); conn.setAutoCommit(false); ps.setDouble(1,-10000.0); ps.setInt(2,111); ps.setDouble(3,10000.0); int count = ps.executeUpdate(); ps.setDouble(1,10000.0); ps.setInt(2,222); ps.setDouble(3,-10000000.0); count += ps.executeUpdate(); if(count == 2){ conn.commit(); System.out.println("转账成功"); } else { conn.rollback(); System.out.println("转账失败"); } String sql3 = "select * from t_act where actno = ? or actno = ?"; ps = conn.prepareStatement(sql3); ps.setInt(1,111); ps.setInt(2,222); rs = ps.executeQuery(); while(rs.next()){ System.out.println("actno: " + rs.getString("actno") +" balance: " + rs.getString("balance")); } DBUtils.close(conn,ps,rs); } }
|
模糊查询
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
| import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;
public class Test13_like { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtils.getConnection(); String sql = "select * from student where name like ?"; ps = conn.prepareStatement(sql); ps.setString(1,"z%"); rs = ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString("id") + " " +rs.getString("name") + " " + rs.getString("chinese") + " " + rs.getString("english") + " " + rs.getString("math")); } } catch (SQLException e){ e.printStackTrace(); } finally { DBUtils.close(conn,ps,rs); } } }
|
悲观锁与乐观锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 行级锁(又叫悲观锁): select ename,job,sal from emp where job = 'MANAGER'; + | ename | job | sal | + | JONES | MANAGER | 3272.50 | | BLAKE | MANAGER | 3135.00 | | CLARK | MANAGER | 2695.00 | +
select ename,job,sal from emp where job = 'MANAGER' for update; 在select语句后加 for update 就是行级锁 代表在当前事务结束之前,其他事务都无法修改这一行数据 上面select语句表示:那三行数据(其实是对应emp表中的三行数据)都无法修改
乐观锁: 多线程并发 都可以对这行数据修改
|