文章目录

  • 1.JDBC是什么?
  • 2.JDBC的本质是什么?
  • 3.JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。
  • 4.JDBC编程六步(需要背会)
  • 5.案例一:
  • 6.案例二(JDBC大致的步骤框架初步版):
  • 7.案例三:注册驱动的另一种方法
  • 8.案例四:将连接到数据库的所有信息配置到配置文件中
  • 9.案例五:处理查询结果集
  • 10.使用IDEA开发JDBC代码配置驱动
  • 11.总结以上(JDBC代码最终版)
  • 12.用户登录业务介绍
  • 13.PowerDesigner工具的安装
  • 14.数据准备
  • 15.用户登录功能界面的初始化
  • 16.登录方法的实现
  • 17.SQL注入现象
  • 18.如何解决SQL注入问题
  • 19.Statement和PreparedStatement对比
  • 20.演示Statement的用途
  • 21.PreparedStatement完成增删改
  • 22.总结:JDBC最最终版写法
  • 23.JDBC的事务自动提交机制的演示
  • 24.演示JDBC事务自动提交机制会引发的问题和解决办法
  • 25.总结:JDBC最最最终版写法
  • 26.JDBC工具类的封装
  • 27.JDBC实现模糊查询+JDBC工具类DBUtil的使用
  • 28.总结:JDBC最最最最终版写法
  • 29.悲观锁和乐观锁的概念
  • 30.演示行级锁(悲观锁)机制

1.JDBC是什么?

Java DataBase Connectivity(Java语言连接数据库)

2.JDBC的本质是什么?

JDBC是SUN公司制定的一套接口(interface)java.sql.*; (这个软件包下有很多接口)接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。为什么要面向接口编程?解耦合:降低程序的耦合度,提高程序的扩展力。解耦合:让程序更加灵活(面向接口编程就是面向抽象编程)灵活:比如下面喂养的方法:如果写Dog d 只能喂养狗,写Cat c 只能喂养猫而写Animal a 既能喂养狗也能喂养猫,更加灵活(解耦合)多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)建议:Animal a = new Cat();  //父类型引用指向子类型对象Animal a = new Dog();// 喂养的方法public void feed(Animal a){ // 面向父类型(抽象)编程。}不建议:Dog d = new Dog();Cat c = new Cat();思考:为什么SUN制定一套JDBC接口呢?因为每一个数据库的底层实现原理都不一样。Oracle数据库有自己的原理。MySQL数据库也有自己的原理。MS SqlServer数据库也有自己的原理。....每一个数据库产品都有自己独特的实现原理。JDBC的本质到底是什么?一套接口。(通过JDBC来操纵不同的数据库)

模拟角色





3.JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。

classpath=.;D:\course\06-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar以上的配置是针对于文本编辑器的方式开发,使用IDEA工具的时候,不需要配置以上的环境变量。
IDEA有自己的配置方式。

4.JDBC编程六步(需要背会)

第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)第三步:获取数据库操作对象(专门执行sql语句的对象)第四步:执行SQL语句(DQL DML....)第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)

5.案例一:

JDBCTest01
JDBC编程六步
JDBC完成insert
/*JDBC编程六步
*/
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;public class JDBCTest01{public static void main(String[] args){Connection conn = null;Statement stmt = null;try{//1.注册驱动//使用java.sql.DriverManager(驱动管理器)中的方法:(static void) registerDiver(Driver driver) 传入一个驱动//但是jdk中的Driver是java.sql.Driver,是一个接口,无法new对象,所以需要它的实现类//它的实现类在mysql驱动中:com.mysql.cj.jdbc.Driver() com mysql cj jdbc 是包名。mysql8版本以上要加cj//registerDriver(...)这个方法存在异常 throw SQLException 受检异常,再往上抛不合适,需要try...catch...//需要导入包:java.sql.Driver;java.sql.DriverManager;java.sql.SQLException;Driver driver = new com.mysql.cj.jdbc.Driver();//多态,父类型的引用指向子类型的对象 java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();//oracle驱动中:Driver driver = new oracle.jdbc.driver.OracleDriver();DriverManager.registerDriver(driver);//2.获取连接//使用java.sql.DriverManager类中的方法:(static Connection) getConnection(String url, String user, String password) 输入url,数据库的用户名和密码;返回一个java.sql.Connection对象(是一个接口)//该方法同样存在受检异常SQLException;需要导入Connection包/*url:统一资源定位符(网络中某个资源的绝对路径)https://www.baidu.com/ ---> 这就是urlURL包括哪几部分?协议IP地址PORT(端口号)资源名http://182.61.200.7:80/index.htmlhttp://        通信协议182.61.200.7   服务器的IP地址80             服务器上软件的端口index.html     是服务器上某个资源名要访问某个服务器上的某个软件:需要知道该服务器的IP地址和该软件在该服务器上的端口号jdbc:mysql://127.0.0.1:3306/bjpowernodejdbc:mysql://  协议127.0.0.1      IP地址(要连接的服务器/电脑的IP地址)3306           mysql数据库端口号bjpowernode    具体的数据库实例名说明:localhost和127.0.0.1都是本机的IP地址jdbc:mysql://localhost:3306/bjpowernode 和上面相同什么是通信协议,有什么用?通信协议是通信之前就提前定好的数据传送格式数据包具体怎么传送数据,格式提前定好的。oracle的url:jdbc:oracle:thin:@localhost(IP地址):端口号:数据库名其他的百度搜*///mysql的url:jdbc:mysql://ip:port/bjpowernode(ip地址 port端口号 bjpowernode数据库名)String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";String user = "root";//数据库用户名String password = "333";//自己设置的数据库密码conn = DriverManager.getConnection(url,user,password);//这里是mysql数据库连接对象(conn) = com.mysql.cj.jdbc.ConnectionImpl@31304f14System.out.println("数据库连接对象 = " + conn);//3.获取数据库操作对象//第二步已经拿到了Connection对象,可以使用Connection的方法:(Statement) createStatement() 获取数据库的操作对象//该方法存在SQLException异常 并且 需要Statement导包//Statement专门执行sql语句的。stmt = conn.createStatement();//4.执行sql//例如以下语句//JDBC的sql语句结尾不需要加分号String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";//使用Statement中的方法:(int) executeUpdate(String sql)//该方法专门执行DML语句(insert delete update 对于 表中的数据 进行 增删改)//返回值是“影响数据库中的记录条数” ---> 插入2条返回2/删除3条返回3/更新5条返回5//即 影响了数据库中表的几条记录int count = stmt.executeUpdate(sql);System.out.println(count == 1 ? "保存成功" : "保存失败");//5.处理查询结果集}catch(SQLException e){e.printStackTrace();}finally{//6.释放资源//为了保证资源一定释放,在finally语句块中关闭资源 并且 要遵循 从小到大 依次关闭//后开先关//分别对其try..catch//因此 要把 Connection conn = null; 和 Statement stmt = null;写在try..catch外面try{if(stmt != null){stmt.close();}}catch(SQLException e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(SQLException e){e.printStackTrace();}}}
}

6.案例二(JDBC大致的步骤框架初步版):

JDBCTest02
大致的步骤 框架:导包
import java.sql.*;public class Test{public static void main(String[] args){Connection conn = null; 连接对象Statement stmt = null;  数据库操作对象try{1.注册驱动DriverManager(驱动管理器)类的registerDriver(Driver driver)方法多态(父类型的引用指向子类型的对象):mysql的驱动中的Driver类是对java.sql中的Driver接口的实现java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();2.获取连接通过DriverManager类的getConnection(url,user,password)方法获得连接对象Connection connmysql的url是 jdbc:mysql://localhost:3306/bjpowernodejdbc:mysql://IP地址:端口号/数据库名3.获取数据库操作对象通过Connection类的createStatement()方法获得数据库的操作对象 Statement stmtStatement stmt = conn.createStatement();4.执行sql语句首先写出sql语句: String sql = "...";然后通过Statement类的executeUpdate(String sql)方法执行sql语句返回int:影响了数据库中的几条数据(插入2条返回2/删除3条返回3/更新5条返回5)}catch(SQLException e){e.printStackTrace();}finally{6.释放资源遵循 后开先闭 规则先创建Connection conn,后创建Statement stmt先关闭 stmt,后关闭 conntry{if(stmt != null){stmt.close();}}catch(SQLException e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(SQLException e){e.printStackTrace();}}}
}
JDBC完成delete
/*JDBC完成delete
*/
import java.sql.*;public class JDBCTest02{public static void main(String[] args){Connection conn = null;Statement stmt = null;try{//1.注册驱动DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sql语句String sql = "delete from dept where deptno = 40";int count = stmt.executeUpdate(sql);System.out.println(count == 1 ? "删除成功" : "删除失败");}catch(SQLException e){e.printStackTrace();}finally{//6.释放资源try{if(stmt != null){stmt.close();}}catch(SQLException e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(SQLException e){e.printStackTrace();}}}
}

JDBC完成update将案例二的第四步改为:
String sql = "update dept set dname = '销售部' , loc = '芝加哥' where deptno = 30";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "更新成功" : "更新失败");

7.案例三:注册驱动的另一种方法

/*注册驱动的另一种方式(常用的)
*/
import java.sql.*;public class JDBCTest03{public static void main(String[] args){try{//1.注册驱动//第一种写法://DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());//第二种写法//查看com.mysql.cj.jdbc.Driver类的源代码发现:该类中有一个静态代码块//静态代码块中存在第一种注册驱动写法的语句//所以不需要写第一种方法,只让该静态代码块执行就行了//如何让一个类的静态代码块执行?让这个类加载就行了//如何加载这个类?使用反射机制(与反射机制相关的java.lang.Class类的forName(String className)方法)//Class.forName(类名)这个方法的执行会导致类加载,类加载时静态代码块执行Class.forName("com.mysql.cj.jdbc.Driver");//为什么使用第二种方法?因为参数是一个字符串,而字符串可以写到xxx.properties配置文件中//注意:该方法存在ClassNotFoundException异常//2.获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//com.mysql.cj.jdbc.ConnectionImpl@a1153bcSystem.out.println(conn);}catch(SQLException e){e.printStackTrace();}catch(ClassNotFoundException e){e.printStackTrace();}}
}

8.案例四:将连接到数据库的所有信息配置到配置文件中

//将连接到数据库的所有信息 配置到 配置文件中
/*为什么这么做?实际开发中不建议把连接数据库的信息写死到java程序中以后修改,只需要修改属性配置文件xxx.properties中的内容就好了
*/import java.sql.*;
import java.util.ResourceBundle;public class JDBCTest04{public static void main(String[] args){//使用资源绑定器java.util.ResourceBundle类 绑定 属性配置文件jdbc.properties//第一步:使用ResourceBundle类的getBundle(String 属性配置文件路径(不加文件扩展名))方法获得资源绑定器//第二步:通过getString(String ...)方法从属性配置文件中获得要用的数据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{//1.注册驱动Class.forName(driver);//2.获取连接conn = DriverManager.getConnection(url,user,password);//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sql语句String sql = "update dept set dname = '销售部1' , loc = '芝加哥1' where deptno = 30";int count = stmt.executeUpdate(sql);System.out.println(count == 1 ? "更新成功" : "更新失败");}catch(Exception e){ //SQLException异常和ClassNotFoundException异常e.printStackTrace();}finally{//6.释放资源try{if(stmt != null){stmt.close();}}catch(SQLException e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(SQLException e){e.printStackTrace();}}}
}

9.案例五:处理查询结果集

JDBC完成查
//处理查询结果集
//此案例先不使用资源绑定器
import java.sql.*;public class JDBCTest05{public static void main(String[] args){Connection conn = null;Statement stmt = null;ResultSet rs = null; //结果集对象 释放资源时先释放rstry{//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sqlString sql = "select empno,ename,sal from emp";//专门执行DQL语句(select)的方法:Statement类的executeQuery(String sql)方法 返回ResultSet结果集对象rs = stmt.executeQuery(sql);//5.处理查询结果集(看下图)/*首先使用ResultSet类的next()方法,判断下一行有没有数据。有数据返回true,没有返回falseboolean falg1 = rs.next();然后如果光标指向有数据,则取数据使用ResultSet类的getString(int index)方法,返回String类型:不管数据库中的数据是什么类型,都以String形式取出index从1开始,JDBC中所有下标从1开始if(flag1 = true){String empno = rs.getString(1); index = 1 取出第一列的数据String ename = rs.getString(2); index = 2 取出第二列的数据String sal = rs.getString(3);   index = 3 取出第三列的数据System.out.println(empno + "," + ename + "," + sal);}*/while(rs.next() == true){/*String empno = rs.getString(1);String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);*///也可以-->getString(String 字段名/列名称) 这样更好//列名称不是表中的列名称,而是查询结果集的列名称(即字段名被修改,要输入修改后的字段名)String empno = rs.getString("empno");String ename = rs.getString("ename");String sal = rs.getString("sal");System.out.println(empno + "," + ename + "," + sal);/*注意:除了可以以String类型取出,也可以以特定的类型取出int empno = rs.getInt("empno");double sal = rs.getDouble("sal");根据该数据的实际类型这样更方便计算*/}}catch(Exception e){//SQLException异常和ClassNotFoundException异常e.printStackTrace();}finally{try{if(rs != null){rs.close();}}catch(Exception e){e.printStackTrace();}try{if(stmt != null){stmt.close();}}catch(Exception e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(Exception e){e.printStackTrace();}}}
}

10.使用IDEA开发JDBC代码配置驱动

对模块module右键,点击open module settings,再点击libraries(库)
再点击“+”号,点java,找到mysql的驱动jar包(我的是mysql-connector-java-8.0.28.jar)所在位置
点击应用,点OK注意:每创建一个新的模块,都要配置一次驱动(导入jar包)

11.总结以上(JDBC代码最终版)

第一种:不使用 将数据库的信息配置到属性配置文件中
JDBC关于DML语句(insert delete update)import java.sql.*;public class Test{public static void main(String[] args){Connection conn = null;  //连接对象Statement stmt = null;   //数据库操作对象try{//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sql语句 DML语句(insert delete update)//String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";//String sql = "delete from dept where deptno = 40";String sql = "update dept set dname = '销售部' , loc = '芝加哥' where deptno = 30";int count = stmt.executeUpdate(sql);System.out.println(count == 1 ? "更新成功" : "更新失败");}catch(Exception e){e.printStackTrace();}finally{//6.释放资源try{if(stmt != null){stmt.close();}}catch(Exception e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(Exception e){e.printStackTrace();}}}
}
JDBC关于DQL语句(select) 处理查询结果集import java.sql.*;public class Test{public static void main(String[] args){Connection conn = null;  //连接对象Statement stmt = null;   //数据库操作对象ResultSet rs = null; //结果集对象 释放资源时先释放rstry{//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sql语句 String sql = "select empno,ename,sal from emp";//专门执行DQL语句(select)的方法:Statement类的executeQuery(String sql)方法 返回ResultSet结果集对象rs = stmt.executeQuery(sql);//5.处理查询结果集while(rs.next() == true){/*String empno = rs.getString(1); index = 1 取出第一列的数据String ename = rs.getString(2); index = 2 取出第二列的数据String sal = rs.getString(3);   index = 3 取出第三列的数据System.out.println(empno + "," + ename + "," + sal);*///也可以-->getString(String 字段名/列名称) 这样更好//列名称不是表中的列名称,而是查询结果集的列名称(即字段名被修改,要输入修改后的字段名)String empno = rs.getString("empno");String ename = rs.getString("ename");String sal = rs.getString("sal");System.out.println(empno + "," + ename + "," + sal);/*注意:除了可以以String类型取出,也可以以特定的类型取出int empno = rs.getInt("empno");double sal = rs.getDouble("sal");根据该数据的实际类型这样更方便计算*/}}catch(Exception e){e.printStackTrace();}finally{//6.释放资源try{if(rs != null){rs.close();}}catch(Exception e){e.printStackTrace();}try{if(stmt != null){stmt.close();}}catch(Exception e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(Exception e){e.printStackTrace();}}}
}
第二种:使用 将数据库的信息配置到属性配置文件中 (常用这种)
import java.sql.*;
import java.util.ResourceBundle;public class JDBCTest04{public static void main(String[] args){//使用资源绑定器java.util.ResourceBundle类 绑定 属性配置文件jdbc.properties//第一步:使用ResourceBundle类的getBundle(String 属性配置文件路径(不加文件扩展名))方法获得资源绑定器//第二步:通过getString(String ...)方法从属性配置文件中获得要用的数据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{//1.注册驱动Class.forName(driver);//2.获取连接conn = DriverManager.getConnection(url,user,password);//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sql语句String sql = "update dept set dname = '销售部1' , loc = '芝加哥1' where deptno = 30";int count = stmt.executeUpdate(sql);System.out.println(count == 1 ? "更新成功" : "更新失败");}catch(Exception e){ //SQLException异常和ClassNotFoundException异常e.printStackTrace();}finally{//6.释放资源try{if(stmt != null){stmt.close();}}catch(SQLException e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(SQLException e){e.printStackTrace();}}}
}
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode
user=root
password=333
总结:
1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");2.获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root",333);3.获取数据库操作对象Statement stmt = conn.getStatement();4.执行sql第一种:DML语句(insert delete update)String sql = "DML语句";int count = stmt.executeUpdate(sql); //影响数据库数据条数第二种:DQL语句(select)String sql ="DQL语句";ResultSet rs = stmt.executeQuery(sql);5.处理查询结果集(有第四步的第二种才有第五步)while(rs.next() = true){第一种:String empno = rs.getString(1); //参数为index 从1开始 获取第一列的数据String ename = rs.getString(2);String sal = rs.getString(3);//偏向使用第二、三种第二种:String empno = rs.getString("empno"); //参数为字段名String ename = rs.getString("ename");String sal = rs.getString("sal");第三种: String empno = rs.getString("empno");int empno = rs.getInt("empno");double sal = rs.getDouble("sal");  }6.释放资源(后开先闭原则)try{}catch(Exception e){e.printStackTrace();}finally{//是第四步的第二种再写这个rstry{if(rs != null){rs.close();}}catch(Exception e){e.printStackTrace();}try{if(stmt != null){stmt.close();}}catch(Exception e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(Exception e){e.printStackTrace();}}注意:推荐将连接到数据库的所有信息 配置到 配置文件中实际开发中不建议把连接数据库的信息写死到java程序中
以后修改,只需要修改属性配置文件xxx.properties中的内容就好了
//使用资源绑定器java.util.ResourceBundle类 绑定 属性配置文件jdbc.properties
//第一步:使用ResourceBundle类的getBundle(String 属性配置文件路径(不加文件扩展名))方法获得资源绑定器
//第二步:通过getString(String ...)方法从属性配置文件中获得要用的数据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");属性配置文件:jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode
user=root
password=333

12.用户登录业务介绍

实现功能:1.需求:模拟用户登录功能的实现2.业务描述:程序运行的时候,提供一个用户输入的入口,可以让用户输入用户名和密码;用户输入用户名和密码之后,提交信息,java程序收集到用户信息;java程序连接数据库验证用户名和密码是否合法;合法:显示登录成功不合法:显示登录失败3.数据的准备:在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner使用PD工具进行数据库表的设计

13.PowerDesigner工具的安装

打开PowerDesigner
选择create module
点击module types
点击physical data module 物理数据模型(建表)
选择DBMS mysql5.0 (新版本无影响)
model name:user-login
点ok
中间是画图区:按住ctrl键滑动滚轮,每一个小格子里都可以放很多张表name
code:才是真正的用在建表语句的名字使用这个工具对表进行设计user-login.sql脚本文件如果太大,用记事本或者navicat打不开
可以使用resource+脚本文件路径名(拉进去) 在DOS命令窗口中打开resource+脚本文件路径名(拉进去)--->这一步出了问题
总是:ERROR: Failed to open file xx, error: 2
然后发现原因:文件夹的名字不要用中文!

14.数据准备


15.用户登录功能界面的初始化

public class JDBCTest06 {public static void main(String[] args) {//初始化一个界面Map<String,String> userLoginInfo = initUI();}/*** 初始化用户界面* @return 用户输入的用户名和密码等登录信息*/private static Map<String, String> initUI() {Scanner s = new Scanner(System.in);System.out.print("用户名:");String loginName = s.nextLine();System.out.println("密码:");String loginPwd = s.nextLine();Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("loginName",loginName);userLoginInfo.put("loginPwd",loginPwd);return userLoginInfo;}
}

16.登录方法的实现

package com.bjpowernode.jdbc;import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;/*
实现功能:1.需求:模拟用户登录功能的实现2.业务描述:程序运行的时候,提供一个用户输入的入口,可以让用户输入用户名和密码;用户输入用户名和密码之后,提交信息,java程序收集到用户信息;java程序连接数据库验证用户名和密码是否合法;合法:显示登录成功不合法:显示登录失败3.数据的准备:在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner使用PD工具进行数据库表的设计*/
public class JDBCTest06 {public static void main(String[] args) {//初始化一个界面Map<String,String> userLoginInfo = initUI();//验证用户名和密码boolean loginSuccess = login(userLoginInfo);System.out.println(loginSuccess ? "登录成功" : "登录失败");}/*** 用户登录* @param userLoginInfo 用户登录信息* @return false表示失败,true表示成功*/private static boolean login(Map<String, String> userLoginInfo) {//打标记boolean loginSuccess = false;//单独定义变量String loginName = userLoginInfo.get("loginName");String loginPwd = userLoginInfo.get("loginPwd");//JDBC代码Connection conn = null;Statement stmt = null;ResultSet rs =null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取数据库操作对象stmt = conn.createStatement();//4.执行sql//String sql = "select * from t_user where loginName = 'xxxx' and loginPwd = 'yyyy'";String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";rs = stmt.executeQuery(sql);//5.处理查询结构集(这里不需要使用while)if (rs.next() == true){//登录成功loginSuccess = true;}} catch (Exception e) {e.printStackTrace();}finally {//6.释放资源try {if (rs != null){rs.close();}} catch (SQLException e) {e.printStackTrace();}try {if (stmt != null){stmt.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null){conn.close();}} catch (SQLException e) {e.printStackTrace();}}return loginSuccess;}/*** 初始化用户界面* @return 用户输入的用户名和密码等登录信息*/private 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;}
}

17.SQL注入现象

4.当前程序存在的问题:                 用户名:fdsa                 密码:fdsa' or '1'='1       登录成功                     这种现象称为SQL注入(安全隐患)(黑客经常使用)
5.导致SQL注入的根本原因:              用户输入的信息中含有sql语句的关键字,并且这些关导致sql语句的原意被扭曲,进而达到sql注入

18.如何解决SQL注入问题

package com.bjpowernode.jdbc;import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;/*1.解决SQL注入问题:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,就不起作用。要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatementPreparedStatement接口继承了java.sql.StatementPreparedStatement是属于预编译的数据库操作对象PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”2.测试结果:用户名:fdsa密码:fdsa' or '1'='1登录失败3.解决SQL注入的关键是什么?用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用。*/
public class JDBCTest07 {public static void main(String[] args) {//初始化一个界面Map<String,String> userLoginInfo = initUI();//验证用户名和密码boolean loginSuccess = login(userLoginInfo);System.out.println(loginSuccess ? "登录成功" : "登录失败");}/*** 用户登录* @param userLoginInfo 用户登录信息* @return false表示失败,true表示成功*/private static boolean login(Map<String, String> userLoginInfo) {//打标记boolean loginSuccess = false;//单独定义变量String loginName = userLoginInfo.get("loginName");String loginPwd = userLoginInfo.get("loginPwd");//JDBC代码Connection conn = null;PreparedStatement ps = null;  //使用PreparedStatementResultSet rs =null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取预编译的数据库操作对象//sql语句提前,sql语句的框子,其中一个?表示一个占位符,一个?将来接收一个“值”;注意:占位符不能使用单引号括起来String sql = "select * from t_user where loginName = ? and loginPwd = ?";//使用Connection的prepareStatement(String sql)方法//程序执行到此处,会发送SQL语句框子给DBMS,然后DBMS进行SQL语句的预先编译ps = conn.prepareStatement(sql);//给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中的所有下标从1开始)ps.setString(1,loginName); //如果传值是int 那么setInt(index,...)ps.setString(2,loginPwd);//4.执行sqlrs = ps.executeQuery();//不需要再传sql语句了//5.处理查询结构集(这里不需要使用while)if (rs.next() == true){//登录成功loginSuccess = true;}} catch (Exception e) {e.printStackTrace();}finally {//6.释放资源try {if (rs != null){rs.close();}} catch (SQLException e) {e.printStackTrace();}try {if (ps != null){ps.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null){conn.close();}} catch (SQLException e) {e.printStackTrace();}}return loginSuccess;}/*** 初始化用户界面* @return 用户输入的用户名和密码等登录信息*/private 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;}
}

19.Statement和PreparedStatement对比

-Statement存在SQL注入问题;PreparedStatement解决了SQL注入问题
-Statement是编译一次执行一次;PreparedStatement是编译一次可执行N次;PreparedStatement执行效率较高一些
-PreparedStatement会在编译阶段做类型的安全检查综上所述:PreparedStatement使用较多,极少数情况下使用Statement什么情况下必须使用Statement?业务方面要求必须支持SQL注入时。(PreparedStatement无法支持SQL注入)Statement支持SQL注入,凡是业务方面需要进行sql语句拼接时,必须使用Statement    只需要给sql语句传值时使用:PreparedStatement

20.演示Statement的用途

如果使用PreparedStatement,使用ps.setString(...)给占位符?传值(字符串)时,编译时会自动给传的值加上单引号
比如:String sql = "select * from t_user where loginName = ? and loginPwd = ?";如果给?传的是一个值,字符串jack和字符串333,那么会自动加上单引号,即:ps.setString(1,"jack");ps.setString(2,"333");String sql = "select * from t_user where loginName = 'jack' and loginPwd = '333'";这样是对的注意:使用ps.setInt(1,100);--->会把第一个问号换成100,不加单引号,因为不是字符串但是如果传的是desc,想要降序,也会给desc加上单引号,这样就不对了,只能使用Statement
比如:String sql = "select ename from emp order by ename ?";
ps.setString(1,"desc");
String sql = "select ename from emp order by ename 'desc'"; //错误
package com.bjpowernode.jdbc;import java.sql.*;
import java.util.Scanner;/*演示Statement用途:业务方面要求必须支持SQL注入时。(PreparedStatement无法支持SQL注入)Statement支持SQL注入,凡是业务方面需要进行sql语句拼接时,必须使用StatementString sql = "select ename from emp order by ename " + keyWords;+ keyWords 就是sql语句拼接*/
public class JDBCTest08 {public static void main(String[] args) {//用户在控制台输入desc是降序,输入asc是升序Scanner s = new Scanner(System.in);System.out.println("输入desc或asc,desc表示降序,asc表示升序");System.out.print("请输入:");String keyWords = s.nextLine();//sqlConnection conn = null;Statement stmt = null;ResultSet rs =null;try {Class.forName("com.mysql.cj.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");/*          错误:String sql = "select ename from emp order by ename ?";ps = conn.prepareStatement(sql);ps.setString(1,keyWords);rs =ps.executeQuery();*/stmt = conn.createStatement();String sql = "select ename from emp order by ename " + keyWords;rs = stmt.executeQuery(sql);while (rs.next() == true){System.out.println(rs.getString("ename"));}} 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();}}}}
}

21.PreparedStatement完成增删改

package com.bjpowernode.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;//PreparedStatement完成增删改:insert delete update
public class JDBCTest09 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取预处理数据库操作对象
/*          String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";ps = conn.prepareStatement(sql);ps.setInt(1,60);ps.setString(2,"销售部");ps.setString(3,"上海");*//*          String sql = "update dept set dname = ?,loc = ? where deptno = ?";ps = conn.prepareStatement(sql);ps.setString(1,"研发一部");ps.setString(2,"北京");ps.setInt(3,60);*/String sql = "delete from dept where deptno = ?";ps = conn.prepareStatement(sql);ps.setInt(1,60);//4.执行sqlint count = ps.executeUpdate();System.out.println(count);} catch (Exception e) {e.printStackTrace();}finally {//6.释放资源try {if (ps != null){ps.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null){conn.close();}} catch (SQLException e) {e.printStackTrace();}}}
}

22.总结:JDBC最最终版写法

在以前总结的基础上,再加上:大部分情况下使用PreparedStatement要根据情况判断是使用PreparedStatement还是Statement凡是业务方面需要进行sql语句拼接时,必须使用Statement

23.JDBC的事务自动提交机制的演示

package com.bjpowernode.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;/*JDBC事务机制:1.JDBC中的事务是自动提交的,什么是自动提交?只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为但在实际的业务中,通常都是N条DML语句共同联合才能完成,必须保证这些DML语句在同一个事务当中同时成功或同时失败2.以下程序先来验证一下JDBC的事务是自动提交机制测试结果:JDBC只要执行任意一条DML语句,则自动提交一次*/
public class JDBCTest10 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3.获取预处理数据库操作对象String sql = "update dept set dname = ? where deptno = ?";ps = conn.prepareStatement(sql);//第一次给占位符传值ps.setString(1,"x部门");ps.setInt(2,30);int count = ps.executeUpdate();//执行第一条update语句System.out.println(count);//重新给占位符传值ps.setString(1,"y部门");ps.setInt(2,20);count = ps.executeUpdate();//执行第二条update语句System.out.println(count);} catch (Exception e) {e.printStackTrace();}finally {//6.释放资源try {if (ps != null){ps.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null){conn.close();}} catch (SQLException e) {e.printStackTrace();}}}
}

24.演示JDBC事务自动提交机制会引发的问题和解决办法

package com.bjpowernode.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;/*演示JDBC事务自动提交机制会引发的问题:账户转账业务sql脚本:drop table if exists t_act;create table t_act(actno int,balance double(7,2) //注意:7表示有效数字的个数,2表示小数位的个数);insert into t_act(actno,balance) values(111,20000);insert into t_act(actno,balance) values(222,0);commit;select * from t_act;重点三行代码:将自动提交改为手动提交conn.setAutoCommit(false);conn.commit();conn.rollback();*/
public class JDBCTest11 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//将自动提交机制改为手动提交 Connection的setAutoCommit(false/true)方法conn.setAutoCommit(false);//开启事务//3.获取预处理数据库操作对象String sql = "update t_act set balance = ? where actno = ?";ps = conn.prepareStatement(sql);//111账户给222账户转10000元//给?传值ps.setDouble(1,10000);ps.setInt(2,111);int count = ps.executeUpdate();//这里设置一个空指针异常//正常来说,上下两个sql语句(一个事务)都执行之后,转账才会成功,//但是因为JDBC的自动提交机制,只提交了上一个sql语句,导致111账户钱少了,但是222账户钱没变//所以要将jdbc的自动提交机制改为手动提交String s = null;s.toString();//给?传值ps.setDouble(1,10000);ps.setInt(2,222);count += ps.executeUpdate();System.out.println(count == 2 ? "转账成功" : "转账失败");//程序能够执行到这里说明以上程序没有异常,事务结束,手动提交数据//Connection的commit()方法conn.commit();//提交事务} catch (Exception e) {//为了保证数据的安全性,遇到异常手动回滚if(conn != null){try {conn.rollback();//回滚事务} catch (SQLException e1) {e1.printStackTrace();}}e.printStackTrace();}finally {//6.释放资源try {if (ps != null){ps.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null){conn.close();}} catch (SQLException e) {e.printStackTrace();}}}
}

25.总结:JDBC最最最终版写法

在以上的基础上:还要考虑将JDBC的自动提交机制改为手动提交机制

26.JDBC工具类的封装

package com.bjpowernode.jdbc.utils;import java.sql.*;//JDBC工具类,简化JDBC编程
public class DBUtil {/*** 工具类中的构造方法一般都是私有的* 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用*/private DBUtil(){}//静态代码块在类加载时执行,并且只执行一次static {try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 获取数据连接对象* @return 连接对象* @throws SQLException*/public static Connection getConnection() throws SQLException {//有可能调用多次连接对象,但注册驱动只需要一次,所以将注册驱动写在静态代码块中//Class.forName("com.mysql.cj.jdbc.Driver");//Class.forName(类名)这个方法的执行会导致类加载,类加载时静态代码块执行//2.获取连接//把异常扔出去,不要try..catch,因为自己写代码时会写try..catch//Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode");return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");}/*** 关闭资源* @param conn 连接对象* @param ps 数据库操作对象 用Statement而不用PreparedStatement 因为PreparedStatement继承Statement* @param rs 结果集*/public static void close(Connection conn, Statement ps, ResultSet rs){if(rs != null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(ps != null){try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if(ps != null){try {ps.close();} catch (SQLException e) {e.printStackTrace();}}}
}

27.JDBC实现模糊查询+JDBC工具类DBUtil的使用

package com.bjpowernode.jdbc.utils;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** 这个程序有两个任务:*  第一:测试DBUtil*  第二:模糊查询怎么写?*/
public class JDBCTest12 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {//获取连接conn = DBUtil.getConnection();//获取预编译的数据库连接对象String sql = "select ename from emp where ename like ?";ps =conn.prepareStatement(sql);ps.setString(1,"_A%");//执行sqlrs = ps.executeQuery();//处理查询结果集while (rs.next() == true){System.out.println(rs.getString("ename"));}} catch (Exception e) {e.printStackTrace();}finally {//释放资源DBUtil.close(conn,ps,rs);}}
}

28.总结:JDBC最最最最终版写法

在以前的基础上+JDBC封装的工具类DBUtil优先使用PreparedStatement再考虑使用Statement(涉及sql语句的拼接时)涉及事务(要多个sql语句同时成功/失败)时:将JDBC的自动提交机制改为手动提交机制和事务相关的语句只有:DML语句(insert delete update)不是很清楚到底什么时候使用手动提交机制,但是有24中的案例的情况必须使用手动提交

29.悲观锁和乐观锁的概念

行级锁(又叫悲观锁):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表中的三行数据)都无法修改乐观锁:多线程并发 都可以对这行数据修改

30.演示行级锁(悲观锁)机制

package com.bjpowernode.jdbc;import com.bjpowernode.jdbc.utils.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;//演示行级锁(悲观锁)机制
//这个程序开启一个事务,这个事务专门进行查询,并且使用行级锁(悲观锁),锁住相关记录(数据)
public class JDBCTest13 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = DBUtil.getConnection();//开启事务(关闭JDBC自动提交机制)conn.setAutoCommit(false);String sql = "select ename,job,sal from emp where job = ? for update";ps = conn.prepareStatement(sql);ps.setString(1,"MANAGER");rs = ps.executeQuery();while (rs .next()== true){System.out.println(rs.getString("ename")+","+rs.getString("job")+","+rs.getDouble("sal"));}//提交事务(事务结束)conn.commit();} catch (Exception e) {//回滚事务(事务结束)if(conn != null){try {conn.rollback();} catch (SQLException e1) {e1.printStackTrace();}}e.printStackTrace();}finally {DBUtil.close(conn,ps,rs);}}
}
package com.bjpowernode.jdbc;import com.bjpowernode.jdbc.utils.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;//演示行级锁(悲观锁)机制
//这个程序负责修改被锁定的记录
public class JDBCTest14 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {conn = DBUtil.getConnection();//开启事务(关闭JDBC自动提交机制)conn.setAutoCommit(false);String sql = "update emp set sal = sal * 1.1 where job = ?";ps = conn.prepareStatement(sql);ps.setString(1,"MANAGER");int count = ps.executeUpdate();System.out.println(count);//提交事务(事务结束)conn.commit();} catch (Exception e) {//回滚事务(事务结束)if(conn != null){try {conn.rollback();} catch (SQLException e1) {e1.printStackTrace();}}e.printStackTrace();}finally {DBUtil.close(conn,ps,null);}}
}
通过断点控制test13事务不提交,然后运行test14,发现test14无法输出结果

注:该文章部分转载自动力节点

JDBC学习笔记-B站动力节点相关推荐

  1. JavaScript学习笔记-B站动力节点

    文章目录 1.1.什么是JavaScript,有什么用? 1.2.HTML,CSS,JavaSript三者关系 2.在HTML中怎么嵌入JavaScript代码? 3.JS的标识符和关键字 4.关于J ...

  2. MySQL学习笔记-B站动力节点

    文章目录 MySQL Day1 1.概述 2.sql.DB.DBMS分别是什么?他们之间的关系? 3.表 4.sql语句分类 5.导入数据 6.sql脚本 7.删除数据库命令 8.查看表结构 9.查看 ...

  3. html学习笔记-B站动力节点

    文章目录 1.系统结构介绍 2.软件环境准备 3.什么是HTML?怎么开发HTML?怎么运行HTML? 4.HTML是谁制定的? 5.我的第一个HTML 6.HTML的基本标签 7.HTML的实体符号 ...

  4. Dubbo教程学习笔记——B站动力节点

    文章目录 001-前言 002-初始Dubbo 003-Dubbo框架 3.1 概述 3.2 dubbo支持的协议 3.3 dubbo的使用--直连方式 (1)服务的提供者provider (2)服务 ...

  5. JDBC学习笔记-动力节点

    JDBC (B站动力节点杜老师) 1.JDBC是什么 2.jdbc的本质是什么? 多态: ​ Animal a = new Cat ( ) -->面向抽象编程 父类型的引用 指向 子类型的对象 ...

  6. (B站动力节点老杜MySQL教程)MySQL课堂笔记-day01.txt

    文章目录 文件来源/资料下载: MySQL课堂笔记-day01.txt 1.sql.DB.DBMS分别是什么,他们之间的关系? 2.什么是表? 3.学习MySQL主要还是学习通用的SQL语句,那么SQ ...

  7. JDBC学习笔记(1)---B站尚硅谷宋红康

    JDBC学习笔记(1)-B站尚硅谷宋红康 JDBC学习笔记(2)-B站尚硅谷宋红康 文章目录 软件架构方式介绍 JavaWeb技术概览 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的 ...

  8. (B站动力节点老杜MySQL教程)MySQL课堂笔记-day03.txt

    文章目录 文件来源/资料下载: MySQL课堂笔记-day03.txt 1.约束 1.1.唯一性约束(unique) 1.2.主键约束 1.3.外键约束 2.存储引擎?(整个内容属于了解内容) 2.1 ...

  9. (B站动力节点老杜MySQL教程)MySQL课堂笔记-day02.txt

    文章目录 文件来源/资料下载: MySQL课堂笔记-day02.txt 1.关于查询结果集的去重? 2.连接查询 2.1.什么是连接查询? 2.2.连接查询的分类? 2.3.在表的连接查询方面有一种现 ...

最新文章

  1. vue-cli eslint 规则
  2. vulnhub_内网渗透测试的记录——网络安全
  3. 【存储知识学习】第十章- NAS和SAN《大话存储》阅读笔记
  4. python画星空的程序_用python画星空源代码是什么?
  5. Programming Computer Vision with Python (学习笔记十二) 1
  6. Stanford公开课《编译原理》学习笔记(1~4课)
  7. JAVA-java内存分配
  8. webpack4导入全局sass文件
  9. python模块下载1002python模块下载_【Python】Python的urllib模、urllib2模块的网络下载文件...
  10. centos 6 apt.sw.be 错误 无法yum安装软件解决方案
  11. python列表初始化长度_在Python中预先初始化列表内容和长度的实现
  12. python生成树状图_用 python 将思维导图转换成树形列表
  13. 关于大学生睡眠时间的调查
  14. (啤酒,红酒,白酒,料酒)豆瓣(剁椒)鲫鱼做法记录
  15. H3C交换机配置教程及H3C S5810系列交换机光模块搭配
  16. 论劳动生产力进步的原因,兼论劳动产品在不同阶级人民之间自然分配顺序(读后感)
  17. android8卡顿,看完就明白为什么安卓手机卡顿,这个技巧教你解决卡顿
  18. EAX、EBX、ECX、EDX
  19. GameofMir引擎架设传奇服务器【1:架设服务端】
  20. 【转】最实用的IT类网站及工具大集合

热门文章

  1. 强烈建议 | 转行Python最好看一下这篇文章
  2. [日语二级词汇]日语二级必会汉字总结3
  3. 界面扩大缩小操作按钮_数控机床基本操作,超容易,一次看懂两次学会
  4. 海尔云谷创新中心A座能耗监测系统的应用
  5. php英文插件教程,迅睿CMS 优速:百度翻译插件教程
  6. 设计模式之禅《一》 大旗不挥,谁敢冲锋 ——6大设计原则
  7. 功能篇:从 Windows 到 macOS 新手上手指南
  8. 横店影视城:山间腹地崛起文化产业 小镇织就光影梦
  9. HCNR201工作电压点测量
  10. 搜狗专用超级单泛站群霸屏版-搜狗蜘蛛池收录