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.jar
jar包中很多.class字节码文件,这是mysql数据库厂家写的接口实现!
注意:如果连接的是oracle数据库,你需要从网上下载oracle的jar包。
mysql-connector-java-5.1.23-bin.jar 这个jar包有一个专业的术语,
大家记住就行:mysql的驱动。
如果是oracle的jar,被称为oracle的驱动。
第三波:我们java程序员,面向JDBC接口写代码就行!
java">模拟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目录下找class
classpath=.;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毕竟是进程之间的通信,会占用很多资源,需要关闭)
java">/*
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.1
3306 mysql数据库端口号
bjpowernode mysql数据库的名称
jdbc:mysql://192.168.111.123:3306/abc
如果是oracle数据库的话:
oracle:jdbc:thin:@localhost:1521:bjpowernode
oracle: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();
}
}
}
}
}
java">/*
处理查询结果集
提醒一下:
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,最后关闭Connection
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();
}
}
}
}
}
next()遍历结果集的原理
6、注册驱动的第二种方式
java">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、连接数据库的信息统一写到属性配置文件中。
java">(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=tiger
package com.bjpowernode.jdbc;
import java.sql.*;
import java.util.ResourceBundle;
/*
思想:
将连接数据库的可变化的4条信息写到配置文件中。
以后想连接其他数据库的时候,可以直接修改配置文件,不用修改java程序。
四个信息是什么?
driver
url
user
password
*/
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、实现一个登录功能。
第一步:提供一个输入的界面,可以让用户输入用户名和密码。
第二步:底层数据库当中需要有一张用户表,用户表中存储了用户信息。
java">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注入
java">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、执行SQL
String 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);
// 返回Map
return userLoginInfo;
}
}
解决sql注入
java">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、执行SQL
rs = 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);
// 返回Map
return userLoginInfo;
}
}
9、Statement主要是进行sql语句的拼接。PreparedStatement可以避免sql注入,主要进行传值。
如果需要降序,用PreparedStatement可以吗
java">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
// 先使用PreparedStatement
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、获取预编译的数据库操作对象
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
java">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
// 先使用PreparedStatement
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、执行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进行增删改
java">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做模糊查询
java">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();
java">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条再返回1
System.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工具类
java">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();
}
}
}
}
测试工具类
java">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%");
// 执行sql
rs = 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 update
select 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是行锁
java">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);
}
}
}
java">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);
}
}
}