一、什么是JDBC
JDBC(Java DataBase Connectivity)就是Java数据库连接,说白了就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。
二、JDBC的原理
早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动!
JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。
当然还有第三方公司专门为某一数据库提供驱动,这样的驱动往往不是开源免费的!
三、程序员,JDBC,JDBC驱动的关系及说明
JDBC API
提供者:Sun公司
内容:供程序员调用的接口与类,集成在java.sql和javax.sql包中,如
1.DriverManager类 作用:管理各种不同的JDBC驱动
2.Connection接口
3.Statement接口
4.ResultSet接口
JDBC 驱动
提供者:数据库厂商
作用:负责连接各种不同的数据库
Java程序员
JDBC对Java程序员而言是API,对实现与数据库连接的服务提供商而言是接口模型。
- 1
三方关系
SUN公司是规范制定者,制定了规范JDBC(连接数据库规范)
数据库厂商微软、甲骨文等分别提供实现JDBC接口的驱动jar包
程序员学习JDBC规范来应用这些jar包里的类。
四、JDBC的总结
简单地说,JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。
DriverManager :依据数据库的不同,管理JDBC驱动
Connection :负责连接数据库并担任传送数据的任务
Statement :由 Connection 产生、负责发送执行SQL语句
ResultSet:负责保存Statement执行后所产生的查询结果
五、JDBC连接数据的步骤
1:注册一个Driver驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)
2:获取数据库连接(Connection)(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完后一定要关闭)
3 : 获取数据库操作对象(专门执行SQL语句的对象 )
4:执行SQL语句(DML、DQL、...)
5:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集)
6:关闭数据库资源(使用完资源后一定要关闭资源,Java和数据库属于进程间的通信,开启后一定要关闭),按以下顺序关闭:
ResultSet
Statement
Connection。
六、JDBC连接数据的详细步骤
1.注册驱动
注册JDBC驱动是通过调用方法java.lang.Class.forName(),下面列出常用的几种数据库驱动程序加载语句的形式 :
Class.forName(“oracle.jdbc.driver.OracleDriver”);//使用Oracle的JDBC驱动程序
Class.forName(“com.microsoft.jdbc.sqlserver.SQLServerDriver”);//使用SQL Server的JDBC驱动程序
Class.forName(“com.ibm.db2.jdbc.app.DB2Driver”);//使用DB2的JDBC驱动程序
Class.forName(“com.mysql.jdbc.Driver”);//使用MySql的JDBC驱动程序
2.创建数据库连接
与数据库建立连接的方法是调用DriverManager.getConnection(String url, String user, String password )方法
Connection conn=null;
String url=“jdbc:oracle:thin:@localhost:1521:orcl”;
String user=“scott";
String password=“tiger";
conn = DriverManager.getConnection(url, user, password);
3.创建Statement并发送命令
Statement对象用于将 SQL 语句发送到数据库中,或者理解为执行sql语句
有三种 Statement对象:
Statement:用于执行不带参数的简单SQL语句;
PreparedStatement(从 Statement 继承):用于执行带或不带参数的预编译SQL语句;
CallableStatement(从PreparedStatement 继承):用于执行数据库存储过程的调用。
4.处理ResultSet结果
ResultSet对象是executeQuery()方法的返回值,它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。
ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行。
初始状态下记录指针指向第一条记录的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面。
5.关闭数据库资源
作为一种好的编程风格,应在不需要Statement对象和Connection对象时显式地关闭它们。关闭Statement对象和Connection对象的语法形式为:
public void close() throws SQLException
用户不必关闭ResultSet。当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭。
注意:要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,因为Statement和ResultSet是需要连接是才可以使用的,所以在使用结束之后有可能其他的Statement还需要连接,所以不能先关闭Connection。
以上内容参考博客:[添加链接描述](https://blog.csdn.net/qq_43623447/article/details/106494077)
七、代码演示
1、注册驱动第一种方式(包含:插入/删除/更新)
- JDBC执行插入
package com.zzz.jdbc;
import java.sql.*;
/**
* JDBC完成insert
*/
public class JDBCTest01 {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
try {
//1、注册驱动
Driver driver = new com.mysql.jdbc.Driver(); //多态,父类引用指向子类对象
DriverManager.registerDriver(driver);
//2、获取连接
/**
* URL:统一资源定位符(网络中某个资源的绝对路径)
* https://www.baidu.com/ 这就是URL
* URL包括哪几部分?
* 协议
* IP 计算机的代号
* PORT 计算机上某个软件的代号
* 资源名
*
* http://182.61.200.7:80/index.html
* http:// 通信协议
* 182.61.200.7 服务器IP地址
* 80 服务器上软件的端口
* index.html 服务器上的某个资源名
*
* jdbc:mysql://localhost:3306/hive
* jdbc:mysql:// 协议
* localhost IP地址
* 3306 mysql数据库端口号
* hive 具体的数据库实例名
*
* 什么是通信协议?有什么用?
* 通信协议是通信之前就提前定好的数据传送格式。数据包具体怎么传数据,格式提前定好的。
*/
String url = "jdbc:mysql://localhost:3306/hive";
String user = "root";
String password = "111111";
conn = DriverManager.getConnection(url, user, password);
//3、获取数据库操作对象Statement来将SQL语句发送到数据库
statement = conn.createStatement();
//4、执行sql语句(JDBC的SQL语句结尾不需要写分号)
String sql = "insert into people(name,age) values('Susan',20)";
//executeUpdate方法是专门执行DML语句的(insert/delete/update),返回值是影响数据库中的记录的条数
int count = statement.executeUpdate(sql);
System.out.println(count==1? "保存成功" : "保存失败");
//5、处理查询结果集
} catch (SQLException e) {
e.printStackTrace();
}finally{
//6、释放资源
//为了保证资源一定释放,在finally语句块中关闭资源
//并且要遵循从小到大一次关闭
try {
if (statement != null){
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- JDBC执行删除与更新
package com.zzz.jdbc;
import java.sql.*;
/**
* JDBC完成delete, update
*/
public class JDBCTest02 {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
try {
//1、注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive", "root", "111111");
//3、获取数据库操作对象
statement = conn.createStatement();
//4、执行SQL语句
//String sql = "delete from people where name = 'Susan'";
String sql = "update people set name = 'Daming',age = 18 where name = 'Andy'";
int count = statement.executeUpdate(sql);
//System.out.println(count==1? "删除成功" : "删除失败");
System.out.println(count==1? "更新成功" : "更新失败");
//5、处理查询结果集
} catch (SQLException e) {
e.printStackTrace();
}finally{
//6、关闭资源
try {
if (statement != null){
statement.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try {
if (conn != null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
2、注册驱动的第二种方式(类加载方式,常用方式)
package com.zzz.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 注册驱动的另一种方式:类加载的方式注册驱动(这种方式常用)
*/
public class JDBCTest03 {
public static void main(String[] args) {
try {
//1、注册驱动
//这是注册驱动的第一种写法,用的少
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
/**
* 这是注册驱动的第二种写法,
* 通过反射机制完成类加载,进而执行该类中静态代码块里的内容,该静态代码块里的内容就是注册驱动。
*
* 这是常用的写法,因为参数是字符串,字符串可以写到配置文件中
*
* 以下方法不需要接收返回值,因为我们只想用它的类加载动作
*/
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive", "root", "111111");
System.out.println(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、从属性资源文件中读取连接数据库信息
package com.zzz.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
/**
* 将连接数据库的所有信息配置到配置文件中。
* 实际开发中不建议把连接数据库的信息写死到java程序中,一般都是将信息写到xxxx.properties文件中,使用资源绑定器ResourceBundle获取信息。
*/
public class JDBCTest04 {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
//使用资源绑定器绑定资源配置文件
/**
* 注意:这里需要把jdbc.properties文件放在src下,如果是放在package下,则程序的filename应该package/jdbc
* 或者放在target文件下的classes文件夹下
*
* jdbc.properties文件中的内容为:
* driver=com.mysql.jdbc.Driver
* url=jdbc:mysql://localhost:3306/hive
* user=root
* password=111111
*
* 这样要改参数的话,只需要去jdbc.properties文件中去改就行了
*/
//ResourceBundle是专门用来读取配置文件的工具类
//bundle只能读取properties类型的文件, 读取的时候只需要文件名, 不需要后缀
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");
try {
//1、注册驱动
Class.forName(driver);
//2、获取连接
conn = DriverManager.getConnection(url,user,password);
//3、获取数据库操作对象
statement = conn.createStatement();
//4、执行SQL语句
int count = statement.executeUpdate("update people set age = 21 where name='Michael'");
System.out.println(count==1? "更新成功" : "更新失败");
//5、处理查询结果集
} catch (Exception e) {
e.printStackTrace();
}finally{
//6、关闭资源
try {
if (statement != null){
statement.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try {
if (conn != null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
4、JDBC执行查询操作(有处理查询结果集步骤)
package com.zzz.jdbc;
import java.sql.*;
/**
* 处理查询结果集(遍历结果集)
*/
public class JDBCTest05 {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive","root","111111");
//3、获取数据库操作对象
statement = conn.createStatement();
//4、执行SQL语句
String sql = "select name as na,age from people";
/**
* 注意:
* 增删改使用executeUpdate (insert/delete/update) 返回的是变化的数量
* 查询使用executeQuery (select) 返回结果集
*/
//executeQuery方法是专门执行DQL语句的(select),返回单个ResultSet对象
resultSet = statement.executeQuery(sql);
//5、查询结果集
while (resultSet.next()){ //判断光标指向的行有没有数据
//若光标指向的行有数据就执行取出
//getString方法的特点是:不管数据库中的数据类型是什么,都以String形式取出
//JDBC所有下标从1开始,不是从0开始
/*String name = resultSet.getString(1); //以列下标获取,取出第一列
String age = resultSet.getString(2); //以列下标获取,取出第二列*/
String name = resultSet.getString("na"); //以列名获取,若查询结果集的列名称有别名就填别名
int age = resultSet.getInt("age"); //以列名获取,若查询结果集的列名称有别名就填别名
System.out.println("name:" + name + ", age:" + age);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//6、关闭资源
try {
if (resultSet != null){
resultSet.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try {
if (statement != null){
statement.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try {
if (conn != null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
5、通过模拟用户登录演示SQL注入现象
package com.zzz.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 实现功能:
* 1、需求:模拟用户登录功能的实现
* 2、业务描述:
* 程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
* 用户输入用户名和密码之后,提交信息,Java程序收集到用户信息
* Java程序连接数据库验证用户名和密码是否合法
* 合法:显示登录成功
* 不合法:显示登录失败
* 3、数据的准备:
* 在实际开发中,表的设计会使用专门的建模工具,这里需要安装一个建模工具:PowerDesigner
* 使用PD工具来进行数据库表的设计(参见user-login.sql脚本)
* 4、当前程序存在问题:
* 请输入用户名:ijds
* 请输入密码:fdsa' or '1'='1
* 登录成功
* 这种现象被称为SQL注入(安全隐患)。(黑客经常使用)
* 5、导致SQL注入的根本原因是什么?
* 用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。
*
*/
public class JDBCTest06 {
public static void main(String[] args) {
//初始化一个界面
Map<String, String> userLoginInfo = initUI();
//验证用户名和密码
boolean login = login(userLoginInfo);
//最后输出结果
System.out.println(login? "登录成功" : "登录失败");
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false 登录失败 true 登录失败
*/
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess = false;
//JDBC代码
Connection conn = null;
Statement statement = null;
ResultSet resultSet = null;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive","root","111111");
//3、获取数据库操作对象
statement = conn.createStatement();
//4、执行sql语句
//在字符串中拼一个变量,有固定格式: "+变量名+"
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
/**
* 以上正好完成了sql语句的拼接,以下代码的含义是:发送sql语句给DBMS,DBMS进行sql编译。
* 正好将用户提供的“非法信息”编译进去,导致原sql语句的含义被扭曲了。进而发生了SQL注入现象。
*/
resultSet = statement.executeQuery(sql);
//5、处理结果集
if (resultSet.next()){
loginSuccess = true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//6、关闭资源
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面,返回用户输入的用户名和密码等登录信息
*/
private static Map<String,String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String username = scanner.nextLine();
System.out.print("请输入密码:");
String password = scanner.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",username);
userLoginInfo.put("loginPwd",password);
return userLoginInfo;
}
}
6、解决SQL注入问题(对比Statement和PreparedStatement)
package com.zzz.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 1、解决sql注入问题
* 只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
* 即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
* 要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
* PreparedStatement接口继承了java.sql.Statement
* PreparedStatement是属于预编译的数据库操作对象
* PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。
* 2、测试结果:
* 请输入用户名:ijds
* 请输入密码:fdsa' or '1'='1
* 登录失败
* 3、解决SQL注入的关键是什么?
* 用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用。
* 4、对比Statement和PreparedStatement?
* - Statement存在sql注入问题,而PreparedStatement解决了sql注入问题。
* - Statement是编译一次执行一次,PreparedStatement是编译一次执行多次。PreparedStatement效率比Statement高。
* - PreparedStatement会在编译阶段做类型的安全检查。
* 综上所述:PreparedStatement使用较多,只有极少数的情况下需要使用Statement
* 5、什么情况下必须使用Statement呢?
* 业务方面要求必须支持SQL注入的时候。
* Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
* 如果是单纯地给sql语句传值,则使用PreparedStatement。
*/
public class JDBCTest07 {
public static void main(String[] args) {
//初始化一个界面
Map<String, String> userLoginInfo = initUI();
//验证用户名和密码
boolean login = login(userLoginInfo);
//最后输出结果
System.out.println(login? "登录成功" : "登录失败");
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false 登录失败 true 登录失败
*/
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess = false;
//JDBC代码
Connection conn = null;
PreparedStatement ps = null; //这里使用PreparedStatement(预编译的数据库操作对象)
ResultSet resultSet = null;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive","root","111111");
//3、获取预编译的数据库操作对象
//SQL语句的框架。其中一个 ? 表示一个占位符,一个 ? 将来接收一个“值”。注意:占位符不能使用单引号括起来!
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
//程序执行到此处,会发送sql语句的框架给DBMS,然后DBMS进行sql语句的预先编译。
ps = conn.prepareStatement(sql);
//给占位符 ? 传值(第一个问号下标是1,第二个问号下标是2,JDBC中所有下标从1开始)
ps.setString(1,loginName);
ps.setString(2,loginPwd);
//4、执行sql语句
resultSet = ps.executeQuery();
//5、处理结果集
if (resultSet.next()){
loginSuccess = true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//6、关闭资源
if (resultSet != null){
try {
resultSet.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();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面,返回用户输入的用户名和密码等登录信息
*/
private static Map<String,String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String username = scanner.nextLine();
System.out.print("请输入密码:");
String password = scanner.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",username);
userLoginInfo.put("loginPwd",password);
return userLoginInfo;
}
}
7、演示Statement用途
package com.zzz.jdbc;
import java.sql.*;
import java.util.Scanner;
/**
* 需要使用Statement完成SQL注入的例子
*/
public class JDBCTest08 {
public static void main(String[] args) {
//用户在控制台输入desc就是降序,输入asc就是升序
Scanner sc = new Scanner(System.in);
System.out.println("请输入desc或asc(其中desc表示降序,asc表示升序): ");
String keyWords = sc.nextLine();
//执行SQL
Connection conn = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive", "root", "111111");
//3、获取数据库操作对象
statement = conn.createStatement();
//4、执行sql语句
String sql = "select name, age from people order by age " + keyWords; //进行sql语句的拼接,即注入
resultSet = statement.executeQuery(sql);
//5、处理结果集
while (resultSet.next()){
System.out.println(resultSet.getString("name") + "\t" + resultSet.getInt("age"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//6、关闭资源
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
8、PreparedStatement完成增删改
package com.zzz.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.jdbc.Driver");
//2、获取 连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive", "root", "111111");
//3、获取预编译的数据库操作对象
//增 insert操作
/*ps = conn.prepareStatement("insert into people(name,age) values (?,?)");
ps.setString(1,"Lucy");
ps.setInt(2,23);*/
//改 update操作
/*ps = conn.prepareStatement("update people set name=?,age=? where name = ?");
ps.setString(1,"Cauthy");
ps.setInt(2,20);
ps.setString(3,"Lucy");*/
//删 delete操作
ps = conn.prepareStatement("delete from people where name=?");
ps.setString(1,"Cauthy");
//4、执行sql语句
int count = ps.executeUpdate();
System.out.println(count==1? "更新成功" : "更新失败");
} 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();
}
}
}
}
}
9、JDBC的事务自动提交机制演示
package com.zzz.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的事务支持自动提交机制,即JDBC中只要执行任意一条DML语句,就提交一次。
*/
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/hive", "root", "111111");
//3、获取预编译的数据库操作对象
ps = conn.prepareStatement("update people set name = ? where name = ?");
//第一次给占位符传值
ps.setString(1,"Stephen");
ps.setString(2,"Daming");
int count = ps.executeUpdate(); //执行第一条Update语句
System.out.println(count);
//重新给占位符传值
ps.setString(1,"Alan");
ps.setString(2,"Michael");
count = ps.executeUpdate(); //执行第二条Update语句
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();
}
}
}
}
}
10、账户转账演示事务(手动提交事务)
package com.zzz.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 账户转账演示事务
* 建表的sql脚本:
* drop table if exists t_act;
* create table t_act(
* act_no bigint,
* balance double(7,2) //注意:7表示有效数字的个数,2表示小数位的个数
* );
* insert into t_act(act_no,balance) values (111,20000);
* insert into t_act(act_no,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.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive", "root", "111111");
//将自动提交机制改为手动提交
conn.setAutoCommit(false); //开启事务
//3、获取预编译的数据库操作对象
ps = conn.prepareStatement("update t_act set balance = ? where act_no = ?");
//给占位符 ? 传值
ps.setDouble(1,10000);
ps.setInt(2,111);
int count = ps.executeUpdate();
//给占位符 ? 传值
ps.setDouble(1,10000);
ps.setInt(2,222);
count += ps.executeUpdate();
System.out.println(count==2? "转账成功" : "转账失败");
//程序能走到这里说明以上程序没有异常,事务结束,手动提交数据
conn.commit(); //提交事务
} catch (Exception e) {
//回滚事务,保证数据的安全性
if (conn != null){
try {
conn.rollback();
} catch (SQLException e1) {
e1.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工具类的封装,并实现模糊查询
- JDBC工具类的封装
package com.zzz.jdbc.utils;
import java.sql.*;
/**
* JDBC工具类,简化JDBC编程
*/
public class DBUtil {
/**
* 工具类中的构造方法都是私有的,
* 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
*/
private DBUtil(){}
//静态代码块在类加载时执行,并且只加载一次
static{
//1、注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接,返回连接对象
public static Connection getConnection() throws SQLException {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hive", "root", "111111");
return conn;
}
/**
* 关闭资源
* @param connection 连接对象
* @param statement 数据库操作对象
* @param resultSet 结果集
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet){
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- JDBC实现模糊查询
package com.zzz.jdbc;
import com.zzz.jdbc.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 这个程序两个任务:
* 1、测试封装的工具类DBUtil是否好用
* 2、模糊查询怎么写
*/
public class JDBCTest12 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
//获取连接
conn = DBUtil.getConnection();
//获取预编译的数据库操作对象
String sql = "select name from people where name like ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"_a%"); //第二个字母含有a的名字
resultSet = ps.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//释放资源
DBUtil.close(conn,ps,resultSet);
}
}
}