JDBC

2021.5.21
依然跟着动力节点杜老师学!!!

1、什么是JDBC?

Java DataBase Connectivity
在java语言中编写sql语句,对mysql数据库中的数据进行CRUD操作。

2、JDBC相关的类库在哪里?

java.sql.*;

3、JDBC本质上是一堆什么呢?

JDBC实际上是SUN公司制定好的一套接口,纯interface
java.sql.*;
这个包下都是JDBC的接口,SUN公司制定的!
JDBC是体现“接口作用”的非常经典的例子。
JDBC降低了耦合度,提高了扩展力。
对于java程序员来说,不需要关心数据库是哪个品牌。只要面向JDBC接口编程就行!JDBC整个程序的结构当中有三波人????第一波:SUN公司,负责制定JDBC接口。这些接口已经写好了,在java.sql.*;第二波:java.sql.*下面的所有接口都要有实现类,这些实现类是数据库厂家编写的。我们连接的是mysql数据库,mysql数据库厂家的实现类在哪里呢?mysql-connector-java-5.1.23-bin.jarjar包中很多.class字节码文件,这是mysql数据库厂家写的接口实现!注意:如果连接的是oracle数据库,你需要从网上下载oracle的jar包。mysql-connector-java-5.1.23-bin.jar 这个jar包有一个专业的术语,大家记住就行:mysql的驱动。如果是oracle的jar,被称为oracle的驱动。第三波:我们java程序员,面向JDBC接口写代码就行!
模拟JDBC本质
// JDBC实际上是SUN公司制定好的一套接口,纯interface
public interface JDBC{// 获取数据库连接的一个方法void getConnection();
}// mysql数据库厂家对SUN公司制定的JDBC接口进行了实现
// 这个代码只能由mysql厂家来编写,因为只有mysql厂家才知道mysql的秘密
public class MySQL implements JDBC{//获取连接public void getConnection(){// 这个大括号中的代码是mysql厂家写的System.out.println("通过mysql数据库的小秘密,连接了mysql数据库");}
}// oracle数据库厂家对SUN公司制定的JDBC接口进行了实现
// 这个代码只能由oracle厂家来编写,因为只有oracle厂家才知道oracle的秘密
public class Oracle implements JDBC{//获取连接public void getConnection(){// 这个大括号中的代码是oracle厂家写的System.out.println("通过oracle数据库的小秘密,连接了oracle数据库");}
}// 这就是我们,我们面向JDBC接口调用方法连接数据库
// 我们不需要关心底层是什么数据库
// 因为JDBC接口的出现让程序降低了耦合度
// 我们java程序员写一套JDBC程序,可以连接任何一套数据库public class JavaProgrammer{public static void main(String[] args){// 面向JDBC接口写代码JDBC jdbc = new Oracle();//JDBC jdbc = new MySQL();// 连接数据库// 调用方法的时候,面向JDBC接口调用,JDBC接口中有什么方法,就调用什么方法jdbc.getConnection();// ...以下几百行代码都是面向JDBC接口调用的,不需要修改// ....}
}

4、JDBC开发之前的准备工作?

mysql的驱动jar包,需要配置到classpath当中吗?mysql-connector-java-5.1.23-bin.jar里是字节码,是class文件。Java虚拟机的类加载器会去加载class文件,类加载器怎么能够找到这些class文件呢?classpath没有配置的情况下,默认从当前路径下加载class。classpath如果配置死了,例如:classpath=D:\abc,则表示固定只从d:\abc目录下找classclasspath=.;D:\course\04-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar. 代表什么?当前路径。以上的classpath什么意思?类加载器从当前路径下加载class,如果当前路径下没找到,则去D:\course\04-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar找class文件。jar包需要解压吗?不需要解压,java虚拟机的类加载器有这个能力找到class文件。

5、JDBC编程六步

1.注册驱动通知java程序我们即将要连接的是哪个品牌的数据库2.获取数据库连接java进程和mysql进程,两个进程之间的通道开启了java进程可能在北京,mysql进程可能在上海3.获取数据库操作对象这个对象很重要,用这个对象执行SQL的4.执行SQL语句执行CRUD操作5.处理查询结果集如果第四步是select语句,才有这个第五步6.释放资源关闭所有的资源(因为JDBC毕竟是进程之间的通信,会占用很多资源,需要关闭)
/*JDBC编程六步1.注册驱动2.获取数据库连接3.获取数据库操作对象4.执行SQL语句5.处理查询结果集6.释放资源
*/
import java.sql.DriverManager;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Connection; // 连接接口interface
import java.sql.Statement;public class JDBCTest01{public static void main(String[] args){Connection conn = null;Statement stmt = null;try{//1.注册驱动// com.mysql.jdbc.Driver是mysql数据库厂家写的,实现了java.sql.Driver接口// 如果是oracle数据库的,类名就不一样了,oracle.jdbc.driver.OracleDriver//com.mysql.jdbc.Driver() 实现 java.sql.Driver接口Driver driver = new com.mysql.jdbc.Driver(); // mysql的// Driver driver = new oracle.jdbc.driver.OracleDriver(); // oracle的DriverManager.registerDriver(driver);//2.获取数据库连接/*什么是URL、统一资源定位符任何一个URL都包括协议+IP地址+端口号port+资源名http://192.168.100.2:8888/abc协议是一个提前规定好的数据传输格式,通信协议有很多:http、https...在传送数据之前,提前商量好数据传送的格式这样对方接受到数据之后,就会按照这个格式去解析,拿到有价值的数据IP地址网络当中定位某台计算机的PORT端口号定位这台计算机上某个服务的资源名这个服务下的某个资源jdbc:mysql://          这是java程序和mysql数据库通信的协议localhost             这是本机IP地址,本机IP地址还可以写成:127.0.0.13306                    mysql数据库端口号bjpowernode              mysql数据库的名称jdbc:mysql://192.168.111.123:3306/abc如果是oracle数据库的话:oracle:jdbc:thin:@localhost:1521:bjpowernodeoracle:jdbc:thin:@    这是oracle和java的通信协议localhost             本机IP地址1521                  oracleĬ默认端口bjpowernode              oracle中数据库的名字localhost和127.0.0.1都是本机IP地址。死记硬背*/String url = "jdbc:mysql://localhost:3306/bjpowernode";String user = "root";String password = "123456";conn = DriverManager.getConnection(url,user,password);//输出连接对象的内存地址// com.mysql.jdbc.JDBC4Connection@2aaf7cc2// com.mysql.jdbc.JDBC4Connection类实现了ava.sql.Connection接口// 实际上我们后续的开发不需要关心底层具体是哪个对象,因为面向接口编程//System.out.println(conn);// 3.获取数据库操作对象stmt = conn.createStatement();//System.out.println(stmt);// 通过一个连接对象Connection是可以创建多个Statement对象的//Statement stmt2 = conn.createStatement();//System.out.println(stmt2);// 4.执行SQL语句// insert delete update/*String insertSql = "insert into dept(deptno,dname,loc) values(50, '销售部', '北京')";// Statement 接口中的 executeUpdate方法专门来执行DML语句的// 该方法的返回值表示:影响了数据库表中的总记录条数!int count = stmt.executeUpdate(insertSql);System.out.println(count); // 1*//*String updateSql = "update dept set dname = '人事部', loc = '天津' where deptno = 50";int count = stmt.executeUpdate(updateSql);System.out.println(count);  // 1*/String deleteSql = "delete from dept where deptno = 50";int count = stmt.executeUpdate(deleteSql);System.out.println(count);}catch(SQLException e){e.printStackTrace();} finally{// 6.释放资源,先释放Statement,再释放Connection// 分别进行try..catch处理// 放在finally中关闭if(stmt != null){try{stmt.close();}catch(SQLException e){e.printStackTrace();}}if(conn != null){try{conn.close();}catch(SQLException e){e.printStackTrace();}}}}
}
/*处理查询结果集提醒一下:JDBC中所有的下标都是从1开始的
*/
import java.sql.*;public class JDBCTest02{public static void main(String[] args){Connection conn = null;Statement stmt = null;ResultSet rs = null;try{// 1.注册驱动DriverManager.registerDriver(new com.mysql.jdbc.Driver());// 2.获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");// 3.获取数据库操作对象stmt = conn.createStatement();// 4.执行SQL语句// JDBC当中的SQL语句不需要以";"结尾String sql = "select empno,ename as hehe,sal from emp order by sal desc";// 执行查询语句是这个方法,executeQuery// ResultSet就是查询结果集对象,查询的结果都在这个对象当中rs = stmt.executeQuery(sql);// 5.处理查询结果集// 目前没什么好处理的,直接遍历输出/*ResultSet对象中有以下数据:+-------+--------+---------+| empno | ename  | sal     |+-------+--------+---------+|  7839 | KING   | 5500.00 ||  7788 | SCOTT  | 3300.00 ||  7902 | FORD   | 3300.00 ||  7566 | JONES  | 3272.50 ||  7698 | BLAKE  | 3135.00 ||  7782 | CLARK  | 2695.00 ||  7499 | ALLEN  | 1760.00 ||  7844 | TURNER | 1650.00 ||  7934 | MILLER | 1430.00 ||  7654 | MARTIN | 1375.00 ||  7521 | WARD   | 1375.00 ||  7876 | ADAMS  | 1210.00 ||  7900 | JAMES  | 1045.00 ||  7369 | SMITH  |  880.00 |+-------+--------+---------+调用ResultSet接口中相应的方法遍历查询结果集*//*boolean has = rs.next(); //光标向前移动一位if(has){// 条件成立表示光标指向的行有记录// 取当前行的第一个值String empno = rs.getString(1); // 注意:getString()这个方法不管底层数据库表中是什么类型,统一都以String形式返回// 取当前行的第二个值String ename = rs.getString(2);// 取当前行的第三个值String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);}has = rs.next();if(has){String empno = rs.getString(1);String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);}*/while(rs.next()){// 根据下标来取值的,并且都是以String类型取出来的!/*String empno = rs.getString(1);String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);*/// 根据下标来取值,以特定类型取出!!!/*int empno = rs.getInt(1);String ename = rs.getString(2);double sal = rs.getDouble(3);System.out.println(empno + "," + ename + "," + (sal + 100));*/// 根据查询结果的列名可以取吗?// 以后这种方式是常用的,健壮。int empno = rs.getInt("empno"); // empno并不是字段的名称,是查询结果的列名!!!//String ename = rs.getString("ename");String ename = rs.getString("hehe"); //是查询结果的列名,不是表格中的字段名double sal = rs.getDouble("sal");System.out.println(empno + "," + ename + "," + (sal + 100));}}catch(SQLException e){e.printStackTrace();} finally{// 6.释放资源//先关闭ResultSet,再关闭Statement,最后关闭Connectionif(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();}}}}
}

next()遍历结果集的原理

6、注册驱动的第二种方式

package com.bjpowernode.jdbc;import java.sql.*;/*
注册驱动的第二种方式:类加载注册mysql的厂家写的类:
这个类中有静态代码块,要让这个静态代码块执行,需要让类加载
class com.mysql.jdbc.Driver {static {try {java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException("Can't register driver!");}}
}*/
public class JDBCTest04 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {// 1、注册驱动// oracle数据库: Class.forName("oracle.jdbc.driver.OracleDriver");Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","123456");// 3、获取数据库操作对象stmt = conn.createStatement();// 4、执行SQL语句String sql = "select a.ename as '员工',b.ename as '领导' from emp a left join emp b on a.mgr = b.empno";rs = stmt.executeQuery(sql);// 5、处理查询结果集while(rs.next()){String ename = rs.getString("员工");String lname = rs.getString("领导");System.out.println(ename + "," + lname);}} catch (Exception e) {e.printStackTrace();} finally {// 6、释放资源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();}}}}
}

7、连接数据库的信息统一写到属性配置文件中。

(db.properties 属性配置文件)#######mysql connectivity configuration###############
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode
user=root
password=123456#######oracle connectivity configuration###############
#driver=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@localhost:1521:orcl
#user=scott
#password=tigerpackage com.bjpowernode.jdbc;import java.sql.*;
import java.util.ResourceBundle;/*
思想:将连接数据库的可变化的4条信息写到配置文件中。以后想连接其他数据库的时候,可以直接修改配置文件,不用修改java程序。四个信息是什么?driverurluserpassword*/
public class JDBCTest05 {public static void main(String[] args) {//资源绑定器ResourceBundle bundle = ResourceBundle.getBundle("resources/db");// 通过属性配置文件拿到信息String driver = bundle.getString("driver");String url = bundle.getString("url");String user = bundle.getString("user");String password = bundle.getString("password");System.out.println(driver);System.out.println(url);System.out.println(user);System.out.println(password);Connection conn = null;Statement stmt = null;ResultSet rs = null;try {// 1、注册驱动Class.forName(driver);// 2、获取连接conn = DriverManager.getConnection(url,user,password);// 3、获取数据库操作对象stmt = conn.createStatement();// 4、执行SQL语句String sql = "select a.ename as '员工',b.ename as '领导' from emp a left join emp b on a.mgr = b.empno";rs = stmt.executeQuery(sql);// 5、处理查询结果集while(rs.next()){String ename = rs.getString("员工");String lname = rs.getString("领导");System.out.println(ename + "," + lname);}} catch (Exception e) {e.printStackTrace();} finally {// 6、释放资源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();}}}}
}

8、实现一个登录功能。

第一步:提供一个输入的界面,可以让用户输入用户名和密码。第二步:底层数据库当中需要有一张用户表,用户表中存储了用户信息。
drop table if exists t_user;
create table t_user(id int primary key auto_increment,login_name varchar(255) unique,login_pwd varchar(255),real_name varchar(255)
);
insert into t_user(login_name, login_pwd, real_name) values('admin','123','管理员');
insert into t_user(login_name, login_pwd, real_name) values('zhangsan','123','张三');
select * from t_user;
第三步:当java程序接收到用户名和密码的时候,连接数据库验证用户名和密码。
验证通过,表示登录成功,验证失败,表示登录失败。
登录发生sql注入
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;/*
模拟用户登录用户名:fdsa
密码:fdsa' or '1'='1
select * from t_user where login_name = 'fdsa' and login_pwd = 'fdsa' or '1'='1'
登录成功以上随意输入一个用户名和密码,登录成功了,这种现象被称为SQL注入现象!导致SQL注入的根本原因是什么?怎么解决?导致SQL注入的根本原因是:用户不是一般的用户,用户是懂得程序的,输入的用户名信息以及密码信息中含有SQL语句的关键字,这个SQL语句的关键字和底层的SQL语句进行“字符串拼接”,导致原SQL语句的含义被扭曲了。最最最最最最主要的原因是:用户提供的信息参与了SQL语句的编译。主要因素是:这个程序是先进行的字符串的拼接,然后再进行SQL语句的编译,正好被注入。*/
public class Mysql {public static void main(String[] args) {// 初始化一个界面,可以让用户输入用户名和密码Map<String,String> userLoginInfo = initUI();// 连接数据库验证用户名和密码是否正确boolean ok = checkNameAndPwd(userLoginInfo.get("loginName"), userLoginInfo.get("loginPwd"));System.out.println(ok ? "登录成功" : "登录失败");}/*** 验证用户名和密码* @param loginName 登录名* @param loginPwd 登录密码* @return true表示登录成功,false表示登录失败*/private static boolean checkNameAndPwd(String loginName, String loginPwd) {boolean ok = false; //默认是登录失败的!Connection conn = null;Statement stmt = null;ResultSet rs = null;try {//1、注册驱动Class.forName("com.mysql.jdbc.Driver");//2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");//3、获取数据库操作对象stmt = conn.createStatement();//4、执行SQLString sql = "select * from t_user where login_name = '" + loginName + "' and login_pwd = '" + loginPwd + "'";System.out.println(sql);// 程序执行到此处,才会将以上的sql语句发送到DBMS上。DBMS进行sql语句的编译。rs = stmt.executeQuery(sql);//5、处理查询结果集// 如果以上sql语句中用户名和密码是正确的,那么结果集最多也就查询出一条记录,所以以下不需要while循环,if就够了!if(rs.next()){ok = true;}} catch (Exception e) {e.printStackTrace();} finally {//6、释放资源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();}}}return ok;}/*** 初始化界面,并且接收用户的输入。* @return 存储登录名和登录密码的Map集合。*/private static Map<String,String> initUI() {System.out.println("欢迎使用该系统,请输入用户名和密码进行身份认证.");Scanner s = new Scanner(System.in);System.out.print("用户名:");String loginName = s.nextLine(); //接收用户输入,一次接收一行!System.out.print("密码:");String loginPwd = s.nextLine();//将用户名和密码放到Map集合中。Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("loginName",loginName);userLoginInfo.put("loginPwd",loginPwd);// 返回Mapreturn userLoginInfo;}
}
解决sql注入
package com.bjpowernode.jdbc;import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;/*** 怎么避免SQL注入?*  SQL注入的根本原因是:先进行了字符串的拼接,然后再进行的编译。**  java.sql.Statement接口的特点:先进行字符串的拼接,然后再进行sql语句的编译。*     优点:使用Statement可以进行sql语句的拼接。*     缺点:因为拼接的存在,导致可能给不法分子机会。导致SQL注入。**  java.sql.PreparedStatement接口的特点:先进行SQL语句的编译,然后再进行sql语句的传值。*     优点:避免SQL注入。*     缺点:没有办法进行sql语句的拼接,只能给sql语句传值。**     PreparedStatement预编译的数据库操作对象。*/
public class JDBCTest07 {public static void main(String[] args) {// 初始化一个界面,可以让用户输入用户名和密码Map<String,String> userLoginInfo = initUI();// 连接数据库验证用户名和密码是否正确boolean ok = checkNameAndPwd(userLoginInfo.get("loginName"), userLoginInfo.get("loginPwd"));System.out.println(ok ? "登录成功" : "登录失败");}/*** 验证用户名和密码* @param loginName 登录名* @param loginPwd 登录密码* @return true表示登录成功,false表示登录失败*/private static boolean checkNameAndPwd(String loginName, String loginPwd) {boolean ok = false; //默认是登录失败的!Connection conn = null;PreparedStatement stmt = null;ResultSet rs = null;try {//1、注册驱动Class.forName("com.mysql.jdbc.Driver");//2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");//3、获取预编译的数据库操作对象// 注意:一个问号?是一个占位符,一个占位符只能接收一个“值/数据”// 占位符 ? 两边不能有单引号。 '?' 这样是错误的。String sql = "select * from t_user where login_name = ? and login_pwd = ?";stmt = conn.prepareStatement(sql); // 此时会发送sql给DBMS,进行sql语句的编译// 给占位符?传值// JDBC中所有下标都是从1开始。// 怎么解决SQL注入的:即使用户信息中有sql关键字,但是不参加编译就没事。// 如果需要的类型是int,调用setInt方法,传入的是一个数值stmt.setString(1, loginName); // 1代表第1个问号 ?stmt.setString(2, loginPwd); // 2代表第2个问号 ?//4、执行SQLrs = stmt.executeQuery(); //这个方法不需要将sql语句传递进去。不能是这样:rs = stmt.executeQuery(sql);//5、处理查询结果集if(rs.next()){ok = true;}} catch (Exception e) {e.printStackTrace();} finally {//6、释放资源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();}}}return ok;}/*** 初始化界面,并且接收用户的输入。* @return 存储登录名和登录密码的Map集合。*/private static Map<String,String> initUI() {System.out.println("欢迎使用该系统,请输入用户名和密码进行身份认证.");Scanner s = new Scanner(System.in);System.out.print("用户名:");String loginName = s.nextLine(); //接收用户输入,一次接收一行!System.out.print("密码:");String loginPwd = s.nextLine();//将用户名和密码放到Map集合中。Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("loginName",loginName);userLoginInfo.put("loginPwd",loginPwd);// 返回Mapreturn userLoginInfo;}
}

9、Statement主要是进行sql语句的拼接。PreparedStatement可以避免sql注入,主要进行传值。

如果需要降序,用PreparedStatement可以吗
package com.bjpowernode.jdbc;import java.sql.*;
import java.util.Scanner;/*** 需求:用户在控制台上输入desc则降序,输入asc则升序* 思考一下:这个应该选择Statement还是PreparedStatement呢?*  选Statement,因为PreparedStatement只能传值,不能进行sql语句的拼接。*  这里传入的是'desc'一个字符串*  sql语句变成了:select ename,sal from emp order by sal ’desc‘  报错*/
public class JDBCTest08 {public static void main(String[] args) {Scanner s = new Scanner(System.in);System.out.print("请输入desc或asc【desc表示降序,asc表示升序】:");// 用户输入的String orderKey = s.next(); // desc// 先使用PreparedStatementConnection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1、注册驱动Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");// 3、获取预编译的数据库操作对象String sql = "select ename,sal from emp order by sal ?";ps = conn.prepareStatement(sql);// 给?传值ps.setString(1, orderKey);// 4、执行sql语句rs = ps.executeQuery();while(rs.next()){String ename = rs.getString("ename");String sal = rs.getString("sal");System.out.println(ename + "," + sal);}} catch (Exception e) {e.printStackTrace();} finally {if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (ps != null) {try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
降序使用Statement
package com.bjpowernode.jdbc;import java.sql.*;
import java.util.Scanner;
/*
使用Statement。*/
public class JDBCTest09 {public static void main(String[] args) {Scanner s = new Scanner(System.in);System.out.print("请输入desc或asc【desc表示降序,asc表示升序】:");// 用户输入的String orderKey = s.next(); // desc// 先使用PreparedStatementConnection conn = null;Statement stmt = null;ResultSet rs = null;try {// 1、注册驱动Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");// 3、获取预编译的数据库操作对象stmt = conn.createStatement();// 4、执行sql语句String sql = "select ename,sal from emp order by sal " + orderKey;rs = stmt.executeQuery(sql);// 5、处理查询结果集while(rs.next()){String ename = rs.getString("ename");String sal = rs.getString("sal");System.out.println(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();}}}}
}
使用PreparedStatement进行增删改
package com.bjpowernode.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;/*
使用PreparedStatement完成增删改。*/
public class JDBCTest10 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {// 1、注册驱动Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");// 3、获取预编译的数据库操作对象/*String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";ps = conn.prepareStatement(sql);// 给?传值ps.setInt(1, 50);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, 50);*/String sql = "delete from dept where deptno = ?";ps = conn.prepareStatement(sql);ps.setInt(1, 50);// 4、执行SQL语句int count = ps.executeUpdate(); // 这个方法不需要sql参数System.out.println(count);} catch (Exception e) {e.printStackTrace();} finally {// 6、释放资源if (ps != null) {try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
使用PreparedStatement做模糊查询
package com.bjpowernode.jdbc;import java.sql.*;/*
使用PreparedStatement做模糊查询*/
public class JDBCTest11 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1、注册驱动Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");// 3、获取预编译的数据库操作对象(找出名字中含有O的)/*String sql = "select ename from emp where ename like '%?%'"; //这样写错误的! ?不能写在''里面ps = conn.prepareStatement(sql);ps.setString(1, "O");*/// 重点是占位符该怎么写!!!String sql = "select ename from emp where ename like ?"; //这样是对的!ps = conn.prepareStatement(sql);ps.setString(1, "%O%");// 4、执行SQL语句rs = ps.executeQuery();while(rs.next()){System.out.println(rs.getString("ename"));}} catch (Exception e) {e.printStackTrace();} finally {// 6、释放资源if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (ps != null) {try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}

10、JDBC的事务问题?

conn.setAutoCommit(false);
conn.commit();
conn.rollback();
package com.bjpowernode.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;/*
JDBC默认情况下对事务是怎么处理的?模拟一下银行账户转账操作,A账户20000,B账户0。A账户向B账户转账10000元。从A账户减去10000,向B账户加上10000.必须同时成功,或者同时失败!转账是需要:执行两条update语句的。JDBC默认情况下支持自动提交:什么叫做自动提交呢?只要执行一条DML语句就自动提交一次。(所以这个程序在关闭自动提交机制之前,在出现异常或者睡眠之前就会提交,导致A账户总额变成10000, 而后面给B账户的转账无法执行)在实际开发中必须将JDBC的自动提交机制关闭掉,改成手动提交。
当一个完整的事务结束之后,再提交。conn.setAutoCommit(false); 关闭自动提交机制。conn.commit(); 手动提交conn.rollback(); 手动回滚*/
public class JDBCTest12 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {// 1、注册驱动Class.forName("com.mysql.jdbc.Driver");// 2、获取连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");// 开启事务:将自动提交机制关闭掉。conn.setAutoCommit(false);// 3、获取预编译的数据库操作对象String sql = "update t_act set balance = ? where actno = ?";ps = conn.prepareStatement(sql);//给?传值ps.setDouble(1, 10000);ps.setString(2, "A");int count = ps.executeUpdate(); //更新成功之后表示更新1条,返回1//Thread.sleep(1000 * 20);// 模拟异常!!!!String s = null;s.toString();// 给?传值ps.setDouble(1, 10000);ps.setString(2, "B");count += ps.executeUpdate(); //再次更新1条再返回1System.out.println(count == 2 ? "转账成功" : "转账失败!");// 代码能够执行到此处,说明上面的代码没有出现任何异常,表示都成功了,手动提交。// 手动提交,事务结束。conn.commit();} catch (Exception e) {// 出现异常的话,为了保险起见,这里要回滚!try {if (conn != null) {conn.rollback();}} catch (SQLException ex) {ex.printStackTrace();}e.printStackTrace();} finally {// 6、释放资源if (ps != null) {try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}

11.为了便于以后的开发,封装一个JDBC工具类

package com.bjpowernode.jdbc.utils;import java.sql.*;
import java.util.ResourceBundle;/*** 数据库工具类,便于JDBC的代码编写。*/
public class DBUtil {// 工具类中的构造方法一般都是私有化的,为什么?// 构造方法私有化是为了防止new对象,为什么要防止new对象?// 因为工具类中的方法都是静态的,不需要new对象,直接使用“类名.”的方式调用。// Suppresses default constructor, ensuring non-instantiability.private DBUtil(){}// 类加载时绑定属性资源文件,静态变量private static ResourceBundle bundle = ResourceBundle.getBundle("resources/db");// 注册驱动static {try {Class.forName(bundle.getString("driver"));} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 获取数据库连接对象* @return 新的连接对象* @throws SQLException*/public static Connection getConnection() throws SQLException {String url = bundle.getString("url");String user = bundle.getString("user");String password = bundle.getString("password");Connection conn = DriverManager.getConnection(url,user,password);return conn;}/*** 释放资源* @param conn 连接对象* @param stmt 数据库操作对象* @param rs 查询结果集*/public static void close(Connection conn, Statement stmt, ResultSet rs){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();}}}}
测试工具类
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;/*
测试DBUtil工具类。*/
public class JDBCTest13 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 获取连接conn = DBUtil.getConnection();// 获取预编译的数据库操作对象String sql = "select ename,sal from emp where ename like ?";ps = conn.prepareStatement(sql);// 给?传值ps.setString(1, "A%");// 执行sqlrs = ps.executeQuery();// 处理结果集while(rs.next()){System.out.println(rs.getString("ename") + "," + rs.getDouble("sal"));}} catch (SQLException e) {e.printStackTrace();} finally {// 释放资源DBUtil.close(conn, ps, rs);// 如果没有结果集对象,调用这个方法的时候第三个参数传null。//DBUtil.close(conn, ps, null);}}
}

关于DQL语句的悲观锁?

悲观锁和乐观锁在后面解释
对于一个DQL语句来说,末尾是可以添加这样一个关键之的:for updateselect ename,sal from emp where job = 'MANAGER' for update
这句话的含义是:在本次事务的执行过程中,job = ‘MANAGER’的记录被查询,这些记录在我查询的过程中,任何人,任何事务都不能对这些记录进行修改操作,直到我当前事务结束。这种机制被称为:行级锁机制(又称为悲观锁)在mysql中:当使用select...where...for update...时, mysql进行row lock 还是table lock取决于是否能使用索引(例如主键,unique约束的字段),能则为行锁,否则为表锁未查询到数据则无锁。而使用‘<>’,'like'等操作时,索引会失效,自然进行的是table lock
所以慎用 for update
整个表锁住的时候会导致性能降低,谨慎使用
使用for update时,最好是锁主键值,或者右unique约束的字段,锁别的字段可能会导致整个表锁住乐观锁(提了一下)
是多个线程可以对当前记录进行修改,但是有个版本号

模拟悲观锁

两个程序进行模拟,锁Manager是进行的表锁,锁empno是行锁

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;/*
在当前事务中对job='MANAGER'的记录进行查询并锁定,使用行级锁机制。*/
public class JDBCTest14 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 获取连接conn = DBUtil.getConnection();// 开启事务conn.setAutoCommit(false);// 执行String sql = "select ename,sal from emp where empno = ? for update";ps = conn.prepareStatement(sql);ps.setInt(1, 7369);rs = ps.executeQuery();while(rs.next()){System.out.println(rs.getString("ename") + "," + rs.getDouble("sal"));}//睡眠Thread.sleep(1000 * 20);// 提交事务conn.commit();} catch (SQLException e) {// 回滚事务if(conn != null){try {conn.rollback();} catch (SQLException ex) {ex.printStackTrace();}}e.printStackTrace();} catch (InterruptedException e) {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;/*
在这个事务当中对job='MANAGER'的记录进行update操作。*/
public class JDBCTest15 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {conn = DBUtil.getConnection();String sql = "update emp set sal = sal * 10 where empno = ?";ps = conn.prepareStatement(sql);ps.setInt(1, 7698);int count = ps.executeUpdate();System.out.println("更新了"+count+"条");} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(conn, ps, null);}}
}

JDBC(本质,配置环境变量,JDBC编程六步,类加载注册,sql注入,事务问题,封装工具类,悲观锁,乐观锁)相关推荐

  1. 为何要配置环境变量?

    一.前言 干了这么多年Java,配置环境变量都是第一步要做的,但是为什么要配置环境变量呢,又有什么用呢,今天哪吒就带你一探究竟. 二.百度百科 有事没事找百度,百度解释名词这一块做的是真的好. 1.环 ...

  2. 为何要配置环境变量?带你一探究竟

    一.前言 干了这么多年Java,配置环境变量都是第一步要做的,但是为什么要配置环境变量呢,又有什么用呢,今天哪吒就带你一探究竟. 二.百度百科 有事没事找百度,百度解释名词这一块做的是真的好. 1.环 ...

  3. JDK9安装和配置环境变量

    jdk下载 JDK的下载地址:https://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase9-3934878 ...

  4. 【从删库到跑路】JDBC系列——JDBC编程六步

    文章目录 001-编写程序模拟JDBC本质 1.什么是JDBC? 2.JDBC相关的类库在哪里? 3.JDBC本质上是一堆什么呢? 4.JDBC开发之前的准备工作? 002-JDBC编程六步 1.JD ...

  5. 学习Java第一天:1、Java是什么?2、面向对象的编程思想的特点 3、Java的开发工具 4、安装JDK和配置环境变量 5、Java程序的运行过程 6、Java语言的特点

    目录 1.Java是什么? 2.面向对象的编程思想的特点 3.Java的开发工具 4.安装JDK和配置环境变量 5.Java程序的运行过程 6.Java语言的特点 1.Java是什么? java是一门 ...

  6. JDBC编程六步中遇到的问题

    首先,我们介绍一下 JDBC 编程六步 (参考 pink老师) 一. 导入驱动 jar包 导入驱动jar包的两种方式 //方式1 1. 在任务栏创建文件夹(Directory)libs--复制(D:\ ...

  7. java配置环境变量path(JAVA配置环境变量失败)

    配置java环境变量path怎么设置? 只需要在path中增加%JAVA_HOME%\bin; 即可.完整的JDK安装及环境变量配置如下: 安装JDK 选择安装目录 安装过程中会出现两次 安装提示 . ...

  8. java jdk 检测安装_JDK如何安装和配置环境变量以及检验是否成功安装JDK的方法

    大家都知道JDK是JAVA运行的环境,JDK是将.java文件翻译成.class文件的虚拟机,只有经过编译后系统才能识别,不管是eclippse,还是Myeclipse,还是其他的JAVA编程的编译器 ...

  9. 为什么要配置环境变量

    1.什么是环境变量 借用百度百科的一段话: 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等. 环境变 ...

最新文章

  1. Java学习之移动文件(转)
  2. LeetCode Regular Expression Matching(.和*通配符匹配)
  3. 确保 PHP 应用程序的安全
  4. 鼠标在linux下如何工作,Linux操作系统下的鼠标操作
  5. [Hadoop] 启动HDFS缺少服务
  6. Erlang语言学习入门
  7. epoll哪些触发模式_5.epoll的水平触发和边缘触发
  8. C++远程dll注入到QQ聊天工具
  9. javascript查找关键字高亮效果
  10. 牛客网暑期ACM多校训练营(第九场)H. Prefix Sum(CDQ分治)
  11. SQL Server 2008支持将数据导出为脚本
  12. Mapper代理详解,Mapper代理的作用以及用法,结合之前写的mybatis框架讲解、mapper代理的好处
  13. 【分布式定时任务】定时任务实现几种方式
  14. matlab冲激函数的傅里叶变换,利用MATLAB对正弦,矩形脉冲函数进行傅里叶变换
  15. java培训课程有哪些
  16. HTL里面使用sling model的时候传参问题
  17. K8S pod 时区设置
  18. Epoch, Batch, Iteration 区别
  19. 关于有偿提供拼图响应式后台的通知
  20. 机器人 零境交错吧_电击文库零境交错角色大全 最强角色选择推荐及属性数据汇总[多图]...

热门文章

  1. android 屏幕适配:sw最小宽度计算规则。
  2. 个人如何炒外汇 如何进行外汇交易
  3. 极浅显编序号常识凸显有最大自然数
  4. 【电气专业知识问答】问:对断路器操动机构为何要进行分、合闸脱扣器的低电压动作特性试验?其标准是什么?
  5. kube-prometheus-stack 部署
  6. 《Linux那些事儿之我是USB》我是U盘(4)想到达明天现在就要启程
  7. Golang(go语言)的框架
  8. jspm伊人静听音乐播放器系统毕业设计(附源码、运行环境)
  9. 训练集、测试集的划分——K折交叉验证
  10. 《Visual C# 从入门到精通》第三章使用判断语句——读书笔记