JDBC
概念
概念:JDBC,Java DataBase connectivity,Java 数据库连接,Java语言操作数据库。
JDBC本质:其实是官方(sun公司)定义了一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动的jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
快速入门
使用步骤
下载jar包:https://blog.csdn.net/weixin_42116701/article/details/113129290
- 导入驱动jar包
- 复制jar包到libs目录下
- 右键->Add as Library
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql
- 获取执行sql语句的对象,Statement对象
- 执行sql,接收返回结果
- 处理结果
- 释放资源
注意:后期都会使用PreparedStatement对象来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
详解各个对象
DriverManager
驱动管理对象,功能:
-
注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
注意:在mysql5之后的驱动jar包,可以省略注册驱动的步骤
-
获取数据连接
getConnection(String url,String user,String password)
url:jdbc:mysql://ip地址:端口号/数据库名称
user:用户名
password:密码
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名
Connection connect = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456");
Connection
数据库连接对象
-
获取执行sql的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
-
管理事务
开启事务:setAutoCommit(boolean autoCommit) 调用该方法设置参数为false,即开启事务
提交事务:commit()
回滚事务:rollback()
Statement
执行sql对象
-
boolean execute(String sql):可以执行任意的sql(了解)
-
int executeUpdate(String sql):执行DML(insert、update、delete)语句和DDL(create、alter、drop)语句
返回值:是影响的行数,可以通过这个影响行数判断DML语句是否执行成功,>0执行成功,<0执行失败
-
ResultSet executeQuery(String sql):执行DQL(select)语句
练习:
-
account表 添加一条记录
package com.myz.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * @author: 小码农 * @create: 2021-08-16 10:48 * 1. account表 添加一条记录 * 2. account表 修改记录 * 3. account表 删除一条记录 **/ public class Demo2 { public static void main(String[] args) { //1 注册驱动,可省略 //2 定义sql String sql = "insert into account value(null,'王五',3000);"; Connection root = null; Statement statement = null; try { //3 获取connection对象 root = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456"); //获取sql执行对象 statement = root.createStatement(); //执行sql语句 int count = statement.executeUpdate(sql); System.out.println(count); if (count > 0) { System.out.println("执行成功"); }else{ System.out.println("执行失败"); } } catch (SQLException e) { e.printStackTrace(); } finally { if(statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (root!=null){ try { root.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
-
account表 修改记录
package com.myz.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * @author: 小码农 * @create: 2021-08-16 10:59 **/ public class Demo3 { public static void main(String[] args) { Connection con = null; Statement state = null; //获取连接对象 try { con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456"); state = con.createStatement(); //定义sql语句 String sql = "update account set balance = 1500 where id=3"; //执行sql int i = state.executeUpdate(sql); if (i>0){ System.out.println(i+",执行成功"); }else{ System.out.println(i+",执行失败"); } } catch (SQLException e) { e.printStackTrace(); } } }
-
account表 删除一条记录
package com.myz.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * @author: 小码农 * @create: 2021-08-16 10:59 **/ public class Demo4 { public static void main(String[] args) { Connection con = null; Statement state = null; //获取连接对象 try { con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456"); state = con.createStatement(); //定义sql语句 String sql = "delete from account where id=2"; //执行sql int i = state.executeUpdate(sql); if (i>0){ System.out.println(i+",执行成功"); }else{ System.out.println(i+",执行失败"); } } catch (SQLException e) { e.printStackTrace(); } finally { if (state != null) { try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con!=null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
ResultSet
结果集对象,封装查询结果
boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是则返回false,如果不是则返回true
getXXX(参数):获取数据
-
XXX:表述数据类型,如:int getInt(),String getString()
-
参数:
int:代表列的编号,从1开始。如:getString(int i);
String:代表列名称。如:getDouble(“balance”);
注意,使用步骤
-
游标向下移动一行
-
判断是否有数据
-
获取数据
while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); int balance = resultSet.getInt(3); System.out.println(id + "--" + name + "--" + balance); }
package com.myz.jdbc;
import java.sql.*;
/**
* @author: 小码农
* @create: 2021-08-16 10:59
**/
public class Demo4 {
public static void main(String[] args) {
Connection con = null;
Statement state = null;
ResultSet resultSet = null;
//获取连接对象
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456");
state = con.createStatement();
//定义sql语句
String sql = "select * from account";
//执行sql
resultSet = state.executeQuery(sql);
resultSet.next();
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int balance = resultSet.getInt(3);
System.out.println("id:"+id+" name:"+name+" balance:"+balance);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
练习:查询emp表数据将其封装为对象,然后装载集合,返回一个emp对象集合。
- 定义一个emp类
- 定义一个查询方法 public List findAll(){}
- 实现方法 select * from emp;
package com.myz.jdbc;
import com.myz.domain.Emp;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author: 小码农
* @create: 2021-08-16 11:44
* 练习:查询emp表数据将其封装为对象,然后装载集合,返回一个emp对象集合。
**/
public class Demo6 {
public static void main(String[] args) {
List<Emp> list = new Demo6().findAll();
System.out.println(list);
for (Emp emp:list){
System.out.println(emp.toString());
}
System.out.println(list.size());
}
public List<Emp> findAll() {
Connection connect = null;
Statement state = null;
ResultSet rs = null;
List<Emp> list = null;
try {
// 1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2 获取连接
connect = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456");
state = connect.createStatement();
String sql = "select * from emp";
rs = state.executeQuery(sql);
// 创建emp对象
Emp emp = new Emp();
list = new ArrayList<>();
while (rs.next()){
// 获取数据
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
int salary = rs.getInt("salary");
int bonus = rs.getInt("bonus");
int dept_id = rs.getInt("dept_id");
//创建emp对象,并赋值
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
list.add(emp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (state!=null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connect!=null){
try {
connect.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
}
/*
输出结果:
[Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}, Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}]
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
Emp{id=1008, ename='琵琶精', job_id=4, mgr=1004, joindate=2000-07-17, salary=5000, bonus=3000, dept_id=30}
8
*/
emp类
package com.myz.domain;
import java.util.Date;
/**
* @author: 小码农
* @create: 2021-08-16 11:44
* 封装emp表数据的javaBean
**/
public class Emp {
private int id;
private String ename;
private int job_id;
private int mgr;
private Date joindate;
private int salary;
private int bonus;
private int dept_id;
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", ename='" + ename + '\'' +
", job_id=" + job_id +
", mgr=" + mgr +
", joindate=" + joindate +
", salary=" + salary +
", bonus=" + bonus +
", dept_id=" + dept_id +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getJob_id() {
return job_id;
}
public void setJob_id(int job_id) {
this.job_id = job_id;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public Date getJoindate() {
return joindate;
}
public void setJoindate(Date joindate) {
this.joindate = joindate;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public int getBonus() {
return bonus;
}
public void setBonus(int bonus) {
this.bonus = bonus;
}
public int getDept_id() {
return dept_id;
}
public void setDept_id(int dept_id) {
this.dept_id = dept_id;
}
}
*抽取JDBC工具类:JDBCUtils
目的:简化书写
分析:
-
注册驱动也抽取
-
抽取一个方法
-
需求:不想传递参数(麻烦),还得保证工具类的通用性
-
解决方案:配置文件
jdbc.properties
url=
user=
passwrod=
-
package com.myz.utils;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
* @author: 小码农
* @create: 2021-08-16 13:54
* JDBC工具类
**/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
/*
文件的读取,只需要读取一次即可拿到这些值,使用静态代码块
*/
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);
// 2. 加载文件
// pro.load(new FileReader("E:\\MYZ\\Code\\JAVAWEB\\JavaWEB\\0815_jdbc\\src\\jdbc.properties"));
pro.load(new FileReader(path));
// 3. 获取属性,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
String driver = pro.getProperty("driver");
// 4. 注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 获取连接的方法 返回连接对象connect
public static Connection getConnection(){
Connection conn = null;
try {
conn = DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//释放资源方法
public static void close(Statement state,Connection conn){
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//重载释放资源方法
public static void close(ResultSet rs,Statement state, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
练习:
- 通过键盘录入用户名和密码
- 判断用户是否登录成功
步骤:
-
创建数据库表,添加数据
CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32), password VARCHAR(32) ); SELECT * FROM USER; INSERT INTO USER(username,password) VALUE('xiaoming','123456'),('xioali','12345');
-
package com.myz.jdbc; import com.mysql.cj.jdbc.JdbcConnection; import com.myz.utils.JDBCUtils; import java.sql.*; import java.util.Scanner; /** * @author: 小码农 * @create: 2021-08-16 15:01 * 需求 * - 通过键盘录入用户名和密码 * - 判断用户是否登录成功 **/ public class Demo8 { public static void main(String[] args) { // 1.键盘录入 Scanner sc = new Scanner(System.in); System.out.print("用户名:"); String username = sc.nextLine(); System.out.print("密码:"); String password = sc.nextLine(); // 2.调用方法 boolean flag = new Demo8().login(username, password); // 3.判断结果,输出不同语句 if (flag) { 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 state = null; ResultSet rs = null; try { // 1.获取连接 conn = JDBCUtils.getConnection(); // 2.定义sql语句 String sql = "select * from user where username='" + (username) + "'&& password='" + (password) + "'"; // 3.获取执行sql的对象 state = conn.createStatement(); // 4.执行查询 rs = state.executeQuery(sql); // 5.判断 return rs.next(); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, state, conn); } return false; } } /* 用户名:jgo;hag 密码:a' or 'a' = 'a 登陆成功! */
注意
-
SQL注入问题:在拼接sql时,有一些sql的关键字参与字符串的拼接。会造成安全性问题
例如:在上个练习中,用户名随便输,密码输入a’ or ‘a’ = 'a
就会登录成功
-
解决SQL注入问题:使用PreparedStatement对象
*PreparedStatement
PreparedStatement对象使用步骤
-
导入驱动jar包
- 复制jar包到libs目录下
- 右键->Add as Library
-
注册驱动
-
获取数据库连接对象 Connection
-
定义sql
注意:sql的参数使用?作为占位符。如:select * from user where username=? and password=?;
-
获取执行sql语句的对象
- 使用Statement对象
- 使用PreparedStatement对象,PreparedStatement Connection.prepareStatement(String sql)
-
给?赋值:
方法:setXXX(参数1,参数2)
参数1:?的位置编号,从1开始
参数2:?的值
-
执行sql,接收返回结果
-
处理结果
-
释放资源
注意:后期都会使用PreparedStatement对象来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
JDBC管理事务
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作:
- 开启事务
- 提交事务
- 回滚事务
使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
- 提交事务:commit()
- 回滚事务:rollback()
使用转账案例练习JDBC的事务管理
package com.myz.jdbc;
import com.myz.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author: 小码农
* @create: 2021-08-16 16:45
* 事务操作
**/
public class Demo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pre1 = null;
PreparedStatement pre2 = null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 开启事务
conn.setAutoCommit(false);
// 2.定义sql
String sql1 = "update account set balance = balance - ? where id = ?";
String sql2 = "update account set balance = balance + ? where id = ?";
// 3.获取PreparedStatement对象
pre1 = conn.prepareStatement(sql1);
pre2 = conn.prepareStatement(sql2);
// 4.对PreparedStatement对象使用set方法传入参数
pre1.setInt(1,500);
pre1.setInt(2,1);
pre2.setInt(1,500);
pre2.setInt(2,3);
// 5.执行sql
pre1.executeUpdate();
int s = 1/0;
pre2.executeUpdate();
// 6.提交事务
conn.commit();
} catch (Exception e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 6.释放资源
JDBCUtils.close(pre1,conn);
JDBCUtils.close(pre2,null);
}
}
}