03_06_JavaWeb||day05_JDBC基础【重点掌握】||day05_1_JDBC基础【重点掌握第五点和会使用JDBCUtils工具类,了解JDBC事务处理位置即可】

时间:2023-02-26 19:01:24


今日内容【重点掌握】

  1. JDBC基本概念
  2. 快速入门
  3. 对JDBC中各个接口和类详解

1. JDBC:

  1. 概念:Java数据库连接(Java DataBase Connectivity)
  • Java语言操作数据库
  1. JDBC本质
  • 其实是官方(sun公司)定义的一套操作++所有关系型数据库的规则++,即接口
  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包。
  • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
  1. 快速入门
  • 步骤:
  1. 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
  1. 复制到项目的libs目录下
  2. 右键libs文件–>Add As Library(这样了才真正的加入到了项目中)
  1. 注册驱动(编写代码)
Class.forName("com.mysql.jdbc.Driver");
  • Class.forName(“”);的作用是将代码加载进内存
  • 会有异常:正常写的时候要处理
  1. 获取数据库的连接对象(Connection)
//数据库db3的url,账号,密码
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "password");
  1. 定义sql
String sql = "update account set balance = 500 where id = 1";
  1. 获取执行sql语句的对象(Statement)
Statement stmt = conn.createStatement();
  1. 执行sql,接收返回结果
int count = stmt.executeUpdate(sql);
  1. 处理结果
System.out.println(count);
  1. 释放资源
stmt.close();
conn.close();
  1. ++详解各个对象++

1. ++DriverManager:驱动管理对象++

  1. 是一个类
  2. 功能:
  1. 注册驱动告诉程序该使用哪一个数据库驱动jar包
  • 方法:static void registerDriver(Driver driver):注册与给定的驱动程序(DriverManager)
  1. 写代码时使用的是Class.forName(“com.mysql.jdbc.Driver”);【重点掌握】//作用是将代码(类文件)加载进内存
  • ++这样有一些代码会随着类的加载而自动执行(静态代码块static)++
  • 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static{
try{
java.sql.DriverManager.registerDriver(new Driver());
}catch(SQLException E){
throw new RuntimeException("Can't register driver!");
}
}
  1. 所以底层实现就是DriverManager,只不过使用Class.forName(“com.mysql.jdbc.Driver”);简单【总结】
  • 注:mysql之后的驱动jar包可以省略注册驱动的步骤【一般写上】
  • 在jar包–>META-INF–>services–>java.sql.Driver中
  • 注:Class.forName会有异常:正常写的时候要处理
  1. 获取数据库连接
  • 方法:static Connection getConnection(String url, String user, String password);//静态方法,可以直接通过类名调用
  • 这个也是会抛出异常,和Class.forName抛出的异常放到一起
  • 参数:
  • url:指定连接的路径
  • 语法【重点】(每个sql语言都不太一样):
  • MySQL
//ip找到某计算机,端口号可以找到该计算机上的MySQL
jdbc:mysql://ip地址(域名):端口号/数据库名称
  • 例子:
jdbc:mysql://localhost:3306/db3
  • 细节:如果访问本地则ip和端口号可以省略(了解)
jdbc:mysql:///db3
  • user:用户名
  • password:密码

2. ++Connection:数据库连接对象++

  • 是个接口
  1. 功能:
  1. 获取执行sql的对象
  • 方法:
  • Statement createStatement():【重点掌握】
  • 例子:获取==执行sql语句的对象(Statement是返回值类型)
Statement stmt = conn.createStatement();
  • PreparedStatement preparedStatement(String sql)
  1. 管理事务
  1. 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false(自动变为手动),即为开启事务
  2. 提交事务:commit()
  3. 回滚事务:rollback()

3. ++Statement:执行sql的对象【重点】++

  • 是个接口
  1. 功能:
  1. 执行sql
  • 方法:
  • boolean execute(String sql):可以执行任意的sql–>(了解)
  • int executeUpdate(String sql):【重点掌握】–>执行DML(增删改insert,delete,update)语句、DDL(表和库的操作create,alter,drop)语句
  • 注:返回值就是执行语句影响的行数
  • 执行DDL的时候不返回(默认0)
  • 可以通过这个影响的行数判断DML语句是否执行成功,返回值>0则执行成功,反之,则失效
  • ResultSet executeQuery(String sql):【重点掌握】–>执行DQL(查询select)语句
  • 返回值是:结果集对象(就是查询sql后显示的呢个结果)
  1. 练习:
  1. account表 添加一条记录(insert)
  • 注:释放资源放到finally里面(不管代码对不对都要释放)
  • 导入sql包不要看错了
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Test {
public static void main(String[] args) {
Statement stmt = null;
Connection conn = null;
try {
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2. 定义sql
String sql = "INSERT INTO account value(null, '王五', 3000)";
//3. 获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "password");
//4. 获取执行sql的对象:Statement
stmt = conn.createStatement();
//5. 执行sql
int count = stmt.executeUpdate(sql); //影响的行数
//处理的结果
System.out.println(count);
if(count > 0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//为了避免空指针异常
//7. 释放资源
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
  1. account表 修改记录
  • 修改id为3的钱为1500
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Test2 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
//1. 驱动注册
Class.forName("com.mysql.jdbc.Driver");//**
//2. 编写sql
String sql = "UPDATE account SET balance=1500 WHERE id=3";
//3.创建Connection对象
conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password");
//4. 获取执行sql对象
stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);
//6.执行结果
System.out.println(count);
if(count > 0) {
System.out.println("执行成功");
}else {
System.out.println("执行失败");
}
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//7.释放资源
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
  1. account表 删除一条记录
  • 删除id=3的记录
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class Test3 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
//1.驱动注册
Class.forName("com.mysql.jdbc.Driver");
//2.sql语句
String sql = "DELETE FROM account WHERE id = 3";
//3.获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password"); //***
//4.获取执行sql对象
stmt = conn.createStatement();
//5.执行sql语句
int count = stmt.executeUpdate(sql);
//6.执行结果
System.out.println(count);
if(count > 0) {
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}
}

4. ++ResultSet:结果集对象(用来封装查询结果)++

  • 是一个接口
  1. 意义:从ResultSet结果集对象中,把查出来的结果数据拿出来
  • 默认在第一行,所以先向下
  • 执行方法让游标一行一行向下进行数据获取
  • 每次获取的是该行的某一列数据
  1. 方法:
  1. boolean next():++游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果没有数据,返回false++
  2. getXxx(参数):获取数据
  • Xxx:代表数据类型
  • 比如获取id的时候getInt()
  • 获取name的时候getString()
  • 参数:(体现了方法的重载)
  1. Int:代表列的编号(从1开始)
  • 获取id的列数据
  • getInt(1):表示获取id列数据
  1. String:代表列的名称。
  • 获取balance列的数据(余额)
  • getDouble(“balance”);
  1. 演示:(将来不是这样使用的,这里只是基本使用)
  • 结果集对象也是资源,所以也需要释放
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Test4 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.sql语句
String sql = "SELECT * FROM account";
//3.获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password");
//4.创建获取执行sql对象
stmt = conn.createStatement();
//5.执行sql(DQL-->用executeQuery)
rs = stmt.executeQuery(sql);
//6. 执行结果(获取的数据)
//6.1先让游标向下移动一行
rs.next();
//6.1获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble("balance");

//打印
System.out.println(id + "---" + name + "---" + balance);
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}
}
  • 出现的问题
  • 获取第二行的时候又要复制所有获取数据代码
  1. ResultSet真正使用方式
  1. 游标向下移动一行
  2. 判断是否有数据
  3. 获取数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Test5 {
public static void main(String[] args) {
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;

try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.sql语句
String sql = "SELECT * FROM account";
//3.获取Connection对象
conn = DriverManager.getConnection("jdbc.mysql://localhost.3306/db3", "root", "password");
//4.获取执行sql对象
stmt = conn.createStatement();
//5.执行sql(DQL-->Query)
rs = stmt.executeQuery(sql);

//6. 执行结果(获取的数据)
//循环判断游标是否是最后一行末尾
while(rs.next()) {
//6.1获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble("balance");
//打印
System.out.println(id + "---" + name + "---" + balance);
}
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
  • 练习:
  • 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
  1. 定义Emp类
  2. 定义方法 public List findAll(){}
  3. 实现方法 select * from emp;
//Emp类(JavaBean)
public class Emp {
private int id;
private String balance;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getBalance() {
return balance;
}

public void setBalance(String balance) {
this.balance = balance;
}

@Override
public String toString() {
return "Emp{" +
"id=" + id +
", balance='" + balance + '\'' +
'}';
}
}

//JdbcDemo类
import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class JdbcDemo {
public static void main(String[] args){
List<Emp> list = new JdbcDemo().findAll();
System.out.println(list);
}


public List<Emp> findAll(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<Emp> listA = null;

try {
//1注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2sql语句
String sql = "SELECT * FROM emp";
//3获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "psaaword");
//4.获取执行sql的对象
stmt = conn.createStatement();
//5.执行sql(查询的DQL,所以query)
rs = stmt.executeQuery(sql);


Emp emp = null;
//因为要返回所以写在外面
listA = new LinkedList<>(); //用来存储信息的集合
//获取信息后存到集合中
while (rs.next()){ //判断下一行是否有值
//先获取到数据库里面的值
int id = rs.getInt("id");
String balance = rs.getString("balance");

//创建emp对象并赋值
emp = new Emp();
//将值添加到emp类中
emp.setId(id);
emp.setBalance(balance);

//加到集合中
listA.add(emp);
}

} catch (ClassNotFoundException | SQLException 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();
}
}
}

return listA;
}
}

5. ++PreparedStatement:表示预编译sql语句的对象【开发真正用的】(功能更多)++

  • Statement接口的子接口
  • 注:浏览顺序,先看JDBC工具类在回头来看该内容
  1. JDBC工具类中综合案例出现的问题:SQL注入问题【重点】
  1. SQL注入概念【*】在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。(会造成安全问题)
  • 例子:
  1. 输入用户随便(gxw),输入密码(特殊密码):
//输入的特殊密码
a' or 'a' = 'a
  1. 就会生成一段sql语句
select * form user where username='gxw' and password='a' or 'a' = 'a'
  • or之前确实是false但是后面加的为true,or是或运算,所有就恒等于true了(就很危险)
  1. ++解决SQL注入问题++:使用PreparedStatement对象来解决
  2. 预编译的sql【重点】参数使用?作为占位符
  3. 步骤【真正要掌握的】:
  1. 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
  2. 注册驱动(编写代码)
  • Class.forName(“”);的作用是将代码加载进内存
  1. 获取数据库的连接对象(Connection)
  2. 定义sql
  • 注意:sql的参数使用?作为占位符【#】
  • 如:
select * form user where username=? and password=?;
  1. 获取执行sql语句的对象(PreparedStatement
  • 调用:Connection.prepareStatement(String sql)方法
  • 要传递一个sql进来(不同之处)【注】
  1. 给?去赋值
  • 方法:setXxx(参数1,参数2);
  • 参数1:?的位置编号从1开始
  • 参数2:?的值
  1. 执行sql,接收返回结果(这里就不需要传递sql语句了
  2. 处理结果
  3. 释放资源
  1. 优化JDBC工具类中的登录方法:
public class JdbcTest1 {
public static void main(String[] args) {
//输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号");
String username = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
boolean login = new JdbcTest1().login(username, password);//非静态方法
if(login){
System.out.println("登录成功");
}else {
System.out.println("账号或者密码错误");
}
}


/*
* 登录方法,使用PreparedStatement实现
*/
public boolean login(String username, String password){
if(username == null || password == null){
return false;
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;

try {
//1.获取资源
conn = JDBCUtils.getConnection();
//2.sql语句
String sql = "SELECT * FROM username=? AND password=?";
//3.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
//4.给?号赋值
pstmt.setString(1, username);
pstmt.setString(2, password);
//5.执行sql,不需要传递参数
rs = pstmt.executeQuery();
//判断
return rs.next(); //有下一行则返回true

} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs, pstmt, conn);
}

return false;
}
}
  1. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作【重点】
  1. 防止SQL注入
  2. 效率更高

2. 抽取JDBC工具类 : JDBCUtils【工具类重点,会使用即可】

  1. 目的:简化书写
  2. 分析:【抽取的:注册驱动,获取连接对象,释放资源】
  1. 注册驱动也抽取
  2. 抽取一个方法获取连接对象【DriverManager.getConnection】
  • 需求:++不想传递参数(麻烦),还得保证工具类的通用性。++
  • 解决:配置文件
    jdbc.properties
    url=
    user=
    password=
  1. 抽取一个方法释放资源
  1. 代码实现【*】:
  • 配置文件:jdbc.properties
//数据库db3的位置
url=jdbc:mysql:///db3
user=root
password=root
driver=com.mysql.jdbc.Driver
  • 方便调取,所以都是静态
public class JDBCUtils {
//先声明4个静态成员变量
private static String url;
private static String user;
private static String password;
private static String driver;

/**
* 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
*/
static{
//读取资源文件,获取值。

try {
//1. 创建Properties集合类。
Properties pro = new Properties();

//获取src路径下的文件的方式--->ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath(); //System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties


//2. 加载文件
pro.load(new FileReader(path));//读取字符文件

//3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4. 注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}


/**
* 获取连接
* @return 连接对象
*/
public static Connection getConnection() throws SQLException {

return DriverManager.getConnection(url, user, password);
}

/**
* 释放资源【dml时候,增删改时】
* @param stmt
* @param conn
*/
public static void close(Statement stmt,Connection conn){
if( stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

if( conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}


/**
* 释放资源【dql时候,查询时】
* @param rs
* @param stmt
* @param conn
*/
public static void close(ResultSet rs,Statement stmt, Connection conn){
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();
}
}
}

}
  • 什么时候使用静态代码块
  • 如果需要程序在启动之前就进行一些操作,以及一些经常用到却不占用多少内存的代码或变量直接写在static静态代码块中,当程序启动的时候随之初始化,这样可以节省部分后续操作的时间
  • 格式:
static{

}
  • ClassLoader 类加载器【先了解】
  1. 作用
  • 获取src路径下的(资源路径)文件的方式
  • 可以来加载我们的字节码文件进内存
  1. (JDBC工具类获取配置文件路径的)使用步骤【重点】
  1. 获取到ClassLoader,首先获取到对应的字节码文件对象
  • 类名.class对象
  • 其中一个getClassLoader()方法:获取到ClassLoader
ClassLoader classloader = JDBCUtils.class.getClassLoader();
  1. ClassLoader一个方法:getResource(String name):传入一个文件名(配置文件),就可以获取Resource资源。
  • ++返回的是一个URL对象(表示:统一资源标识符/定位符):可以用来定位一个文件的绝对路径++
URL res = classloader.getResource("jdbc.properties");
  1. 使用URL对象的一个方法,getPath():可以获取一个字符串路径
String path = res.getPath();
  1. 最后就可以将获取到的path,写到加载文件中了(上面三步就可以获取对应的资源文件的绝对路径了)
  • Properties集合类
  • 双列集合,键值都是String,唯一一个可以直接结合IO流使用的集合类,++从流中读取文件,直接写入文件到流中++
  1. 读取资源文件获取值的步骤
  1. 创建Properties集合类
Properties pro = new Properties();
  1. 加载文件:
  • Properties集合类中load()方法从字节输入流中读取键值对。参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。该方法常用于读取配置文件
pro.load(new FileReader(path));
  1. 获取值
  • Properties集合类中getProperty()方法将搜索此属性列表中指定键的属性值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
  1. 演示JDBC工具类
import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class JdbcDemo {
public static void main(String[] args){
List<Emp> list = new JdbcDemo().findAll();
System.out.println(list);
}


public List<Emp> findAll(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<Emp> listA = null;

try {
//注册驱动和获取Connection对象直接换成:
conn = JDBCUtils.getConnection();



//3sql语句
String sql = "SELECT * FROM emp";
//4.获取执行sql的对象
stmt = conn.createStatement();
//5.执行sql(查询的DQL,所以query)
rs = stmt.executeQuery(sql);


Emp emp = null;
//因为要返回所以写在外面
listA = new LinkedList<>(); //用来存储信息的集合
//获取信息后存到集合中
while (rs.next()){ //判断下一行是否有值
//先获取到数据库里面的值
int id = rs.getInt("id");
String balance = rs.getString("balance");

//创建emp对象并赋值
emp = new Emp();
//将值添加到emp类中
emp.setId(id);
emp.setBalance(balance);

//加到集合中
listA.add(emp);
}

} catch (SQLException e) { //ClassNotFoundException异常就不需要了
e.printStackTrace();
}finally {
//直接使用JDBCUtils工具类
JDBCUtils.close(rs, stmt, conn);
}

return listA;
}
}
  • 练习:
  • 需求(给db4数据库):
  1. 通过键盘录入用户名和密码
  2. 判断用户是否登录成功
* select * from user where username = "" and password = "";
  • 如果这个sql有查询结果,则成功,反之,则失败
  • 步骤:
  1. 创建数据库表 user
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
PASSWORD VARCHAR(32)

);

INSERT INTO USER VALUES(NULL,'zhangsan','123');
INSERT INTO USER VALUES(NULL,'lisi','234');
  1. 代码实现:
  • 配置文件中的路径要进行修改:
//数据库现在在db4的位置
url=jdbc:mysql:///db4
user=root
password=root
driver=com.mysql.jdbc.Driver
  • 前提要有前面写好的JDBCUtils工具类和配置文件
//创建一个类:JdbcTest1
public class JdbcTest1 {
public static void main(String[] args) {
//输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号");
String username = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
boolean login = new JdbcTest1().login(username, password);//非静态方法
if(login){
System.out.println("登录成功");
}else {
System.out.println("账号或者密码错误");
}
}


/*
登录方法
*/
public boolean login(String username, String password){
if(username == null || password == null){
return false;
}
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;

try {
//1.获取资源
conn = JDBCUtils.getConnection();
//2.sql语句
String sql = "SELECT * FROM user WHERE username='"+username+"' AND password='"+password+"'";
//3.获取执行sql的对象
stmt = conn.createStatement();
//4.执行sql
rs = stmt.executeQuery(sql);
//判断
return rs.next(); //有下一行则返回true

} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs, stmt, conn);
}

return false;
}
}
  • 注:定义sql语句时,里面传入的username和password都是字符串,所以拼接的时候别忘了单引号
  • 存在的问题SQL注入问题,请看上面第五个内容有讲解【注】

3. 使用JDBC控制事务:

  1. 事务:一个包含多个步骤的业务操作。如果这个业务操作++被事务管理,则这多个步骤要么同时成功,要么同时失败++
  2. 操作JDBC事务处理的位置【重点】
  1. 开启事务:++执行sql之前++
  • 在获取到Connection对象之后开启–>(因为事务管理功能就属于Connection对象)
  1. 提交事务:++sql都执行完++
  • 在执行完sql语句之后
  1. 回滚事务:++catch中++
  • 在catch当中,且注意判断conn是否为空
  1. Connection对象的两个功能:
  1. 获取执行sql的对象
  2. 管理事务
  1. 所以使用Connection对象来管理事务
  1. 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false(false–>手动:开启事务后如果没有提交事务,就会回滚事务),(true–>自动:每次执行完自动提交事务)
  2. 提交事务:commit()
  3. 回滚事务:rollback()
  • 注:只要有异常就让回滚(放到catch中)
  • 回滚的时候,conn有可能为null,所以在回滚前判断是否为空
if(conn != null){
conn.rollback();
}
  1. 案例:银行转账
  • 注:catch一般写的范围比较大
/**
* 事务的操作
*/
public class JDBCDemo3 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;

try {
//1.获取数据库的链接
conn = JDBCUtils.getConnection();

///开启事务
conn.setAutoCommit(false);

//2.定义sql
//2.1张三-500
String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";
//2.2李四+500
String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";

//3.获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);

//4.给?赋值
pstmt1.setDouble(1, 500);
pstmt1.setInt(2, 1);

pstmt2.setDouble(1, 500);
pstmt2.setInt(2, 2);

//5.执行sql语句(DML所以,使用executeUpdate)
pstmt1.executeUpdate();

//手动制造一点问题,但能否回滚(可以)
int i = 3 / 0;
pstmt2.executeUpdate();

///提交事务
conn.commit();
} catch (Exception e) { //一般抓一个大的异常,防止多次添加
///事务回滚
try {
if(conn != null){
conn.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally {
//这里写的不规范,只是为了演示事务
JDBCUtils.close(pstmt1, conn);
JDBCUtils.close(pstmt2, null);
}
}
}