JDBC
1、概念
JDBC(Java Database Connectivity)
概念
用Java语言操作数据库
本质
是官方(Sun公司)定义的一套操作所有关系型数据库的规则(接口),
不同的数据库厂商定义不同的接口实现类(数据库驱动)来实现这套接口,提供数据库驱动jar包
我们可以使用这套JDBC接口来编程,但实际上执行的代码是数据库驱动jar包中的实现类,
如接口Person 实现类Worker Person p = new Worker(); p.eat();使用接口对象调用实现类的实现方法
快速入门
(1)导入数据库驱动jar包 mysql-connector-java-版本号-bin.jar
里面放的都是字节码文件***.class
将jar包复制到项目目录下libs文件夹下
libs文件夹右键Add As Library…
(2)注册驱动
(3)获取数据库连接对象Connection
(4)定义sql语句
(5)获取执行sql语句的对象Statement(Connection不能直接执行sql语句)
(6)执行sql,接收返回结果
(7)处理结果
(8)释放资源
测试类
public class Demo01JDBC {
public static void main(String[] args) throws Exception {
//1、导入数据库驱动jar包
//2、注册驱动
Class.forName("com.mysql.jdbc.Driver");//将该类的字节码文件加载进内存
//3、获取数据库连接对象Connection
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root");
//4、定义sql语句
String sql= "update account set balance = 1000 where name = '张三'";
//5、获取执行sql语句的对象Statement
Statement statement = conn.createStatement();
//6、执行sql,接收返回结果
int count = statement.executeUpdate(sql);
//7、处理结果
System.out.println(count);//1
//8、释放资源
statement.close();
conn.close();
}
}
2、JDBC各个对象
DriverManager 驱动管理对象
功能:
(1)注册驱动
告诉程序该使用哪个数据库驱动jar包
使用MySQL5之后的驱动jar包可以省略注册驱动的步骤
方法:static void registerDriver(Driver driver);
写代码时使用的方法:Class.forName(“com.mysql.jdbc.Driver”);
om.mysql.jdbc.Driver类中有静态代码块,其中的代码在类加载进内存时会自动执行
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch(SQLException E) {
throw new RuntimeException(“Can’t register driver!”);
}
}
(2)获取数据库连接
方法:static Connection getConnection(String url, String user, String password);
url:指定连接的路径
MySQL的写法 jdbc:mysql://IP地址(域名):端口号/数据库名称
jdbc:mysql://localhost:3306/db2
IP地址可以找到指定计算机,端口号可以找到该计算机上安装的MySQL服务器
如果操作的是本机的MySQL服务器,且端口为3306,可以简写为
jdbc:mysql:///数据库名称
jdbc:mysql:///db2
Connection 数据库连接对象
功能:
(1)获取执行sql语句的对象
方法:Statement createStatement();
PreparedStatement prepareStatement(String sql);
(2)管理事务
开启事务
方法:
void setAutoCommit(boolean autoCommit);
设置参数为false,即开启事务(关闭自动提交)
提交事务
方法:void commit();
回滚事务
方法:void rollback();
Statement 执行sql语句的对象
功能:
(1)执行sql语句
方法:boolean execute(String sql);可以执行任意的sql 了解即可
int executeUpdate(String sql);
执行DML(增删改表中数据)语句,DDL(操作数据库和表)语句
一般不执行DDL,因为这些操作一般在MySQL中直接操作
返回值:影响的行数,可以通过其判断DML语句是否执行成功 >0则执行成功
执行DDL语句返回0
ResultSet executeQuery(String sql);执行DQL(查询表中数据记录)语句
ResultSet 结果集对象 封装查询结果 游标初始指向表头行
方法:
boolean next();
游标向下移动一行,判断当前行是否是最后一行末尾,是否有数据
返回false,是最后一行末尾,则没有数据,
返回true,不是最后一行末尾,则有数据
getXXX(参数);获取数据
XXX:数据类型 如获取ID使用int getInt();
参数:两种形式,形成该方法的两种重载
(1)int 要获取的列的编号,从1开始
(2)String 要获取的列的名称
注意:
使用步骤:
(1)游标下移
(2)判断游标当前指向的这一行是否有数据
(3)获取数据
PreparedStatement 执行sql语句的对象 Statement的子接口
2、JDBC练习
内存泄漏:没有释放资源,程序有一些垃圾一直驻留在内存中,导致内存用着用着就变小了
(1)添加一条记录
public class Demo03PraInsert {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
try {
//1、注册驱动 可以省略
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql语句
String sql = "insert into account values(null, '王五', 2000);";
//3、获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql:///db2", "root", "root");
//4、获取执行sql的对象
statement = conn.createStatement();
//5、执行sql
int count = statement.executeUpdate(sql);
//6、处理结果
System.out.println(count);
if (count > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//避免获取statement的语句出错,其调用方法时出现空指针异常
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
(2)修改记录
public class Demo04PraUpdate {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql语句
String sql = "update account set balance = 1000 where name = '王五';";
//3、获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql:///db2", "root", "root");
//4、获取执行sql的Statement的对象
statement = conn.createStatement();
//5、执行sql
int count = statement.executeUpdate(sql);
//6、处理结果
System.out.println(count);
if (count > 0) {
System.out.println("更新成功!");
} else {
System.out.println("更新失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
(3)删除一条记录
public class Demo05PraDelete {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义mysql语句
String sql = "delete from account where name = '王五';";
//3、获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql:///db2", "root", "root");
//4、获取执行sql的Statement对象
statement = conn.createStatement();
//5、执行sql
int count = statement.executeUpdate(sql);
//6、处理结果
System.out.println(count);
if (count > 0) {
System.out.println("删除成功!");
} else {
System.out.println("删除失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
(4)查询表中数据
这里只是简单演示只查询第一行数据的情况
public class Demo06PraSelect {
public static void main(String[] args) {
Connection conn = null;
Statement statement = 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:///db2", "root", "root");
//4、获取执行sql的Statement对象
statement = conn.createStatement();
//5、执行sql
rs = statement.executeQuery(sql);
//6、处理结果
//6.1 游标下移
rs.next();
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble("balance");
System.out.println(id + "--" + name + "--" + balance);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.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();
}
}
}
}
}
(5)查询表中数据的改进
使用while循环查询表中所有数据
public class Demo07PraSelectImpr {
public static void main(String[] args) {
Connection conn = null;
Statement statement = 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:///db2", "root", "root");
//4、获取执行sql的Statement对象
statement = conn.createStatement();
//5、执行sql
rs = statement.executeQuery(sql);
//6、处理结果
while (rs.next()) {
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble("balance");
System.out.println(id + "--" + name + "--" + balance);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.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();
}
}
}
}
}
(6)定义一个方法,将查询到的emp表的数据封装为对象,装载集合,再返回这个集合
步骤:
定义Emp类
定义方法 返回List
方法中查询表数据
Emp类
public class Emp {
private int id;
private String ename;
private int job_id;
private int mgr;
private Date joindate;
private double salary;
private double bonus;
private int 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 double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public int getDept_id() {
return dept_id;
}
public void setDept_id(int dept_id) {
this.dept_id = 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 class Demo08PraSelectObjectList {
public static List<Emp> selectAll() {
List<Emp> list = new ArrayList<>();
Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql
String sql = "select * from emp;";
//3、获取数据库连接对象Connection
conn = DriverManager.getConnection("jdbc:mysql:///db2", "root", "root");
//4、获取执行sql的Statement的对象
statement = conn.createStatement();
//5、执行sql
rs = statement.executeQuery(sql);
//6、处理结果
Emp emp = null;
while (rs.next()) {
emp = new Emp();
emp.setId(rs.getInt("id"));
emp.setEname(rs.getString("ename"));
emp.setJob_id(rs.getInt("job_id"));
emp.setMgr(rs.getInt("mgr"));
emp.setJoindate(rs.getDate("joindate"));
emp.setSalary(rs.getDouble("salary"));
emp.setBonus(rs.getDouble("bonus"));
emp.setDept_id(rs.getInt("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 (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
public static void main(String[] args) {
List<Emp> list = new ArrayList<>();
list = selectAll();
for (Emp emp : list) {
System.out.println(emp);
}
}
}
3、JDBC工具类的使用
JDBC工具类,复用代码
(1)抽取注册驱动
(2)抽取一个方法获取连接对象
需求:不希望在方法中传递参数,又希望该方法对于任意的数据库、用户名和密码是通用的
解决:配置文件jdbc.properties
数据库 url=jdbc:mysql:///db2
用户名 user=root
密码 password=root
驱动 driver=com.mysql.jdbc.Driver
(3)获取一个方法释放资源
jdbc.properties
url=jdbc:mysql:///db2
user=root
password=root
driver=com.mysql.jdbc.Driver
测试类
public class JDBCUtils {
//静态变量才能被静态代码块访问
private static String url;
private static String user;
private static String password;
private static String driver;
//使用静态代码块,在类加载时读取配置文件,并且只会读取这一次
static {
try {
//1、创建Properties对象
Properties pro = new Properties();
//2、加载配置文件
//使用类加载器ClassLoader获取src路径下的文件
ClassLoader cl = JDBCUtils.class.getClassLoader();
//getResource以src文件夹为根目录
URL res = cl.getResource("jdbc.properties");
String path = res.getPath();
System.out.println(path);///G:/java/idea/JavaWeb/out/production/day05-code/jdbc.properties
pro.load(new FileReader(path));
//pro.load(new FileReader("day05-code\\src\\jdbc.properties"));
//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();
}
}
//获取连接对象的方法
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
//释放资源的方法
public static void releaseRes(Statement stat, Connection conn) {
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void releaseRes(ResultSet rs, Statement stat, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void releaseRes(Statement stat1, Statement stat2, Connection conn) {
if (stat1 != null) {
try {
stat1.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat2 != null) {
try {
stat2.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试类
public class Demo09JDBCUtils {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
List<Emp> list = new ArrayList<>();
try {
//1、注册驱动 JDBC工具类静态代码块中已写
//2、定义sql
String sql = "select * from emp;";
//3、获取数据库连接对象 使用JDBC工具类的方法
conn = JDBCUtils.getConnection();
//4、获取执行sql的Statement对象
stat = conn.createStatement();
//5、执行sql
rs = stat.executeQuery(sql);
//6、处理结果
Emp emp = null;
while (rs.next()) {
emp = new Emp();
emp.setId(rs.getInt("id"));
emp.setEname(rs.getString("ename"));
emp.setJob_id(rs.getInt("job_id"));
emp.setMgr(rs.getInt("mgr"));
emp.setJoindate(rs.getDate("joindate"));
emp.setSalary(rs.getDouble("salary"));
emp.setBonus(rs.getDouble("bonus"));
emp.setDept_id(rs.getInt("dept_id"));
list.add(emp);
}
for (Emp emp1 : list) {
System.out.println(emp1);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.releaseRes(rs, stat, conn);
}
}
}
4、JDBC工具类的练习
通过键盘输入用户名和密码进行登录,并判断用户是否登录成功
创建用户表,添加两条记录
CREATE TABLE USER(
ID INT PRIMARY KEY AUTO_INCREMENT,
USERNAME VARCHAR(32),
PASSWORD VARCHAR(32)
);
INSERT INTO USER VALUES(NULL,“zhangsan”,“123”),(NULL,“lisi”,“456”);
public class Demo10JDBCPraLogin {
//登录方法
public static boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//用户名和密码都不为空,可以进行登录判断
//1、注册驱动 在JDBC工具类的静态代码块中已写
//2、定义sql 其中的字符型变量要用引号引起来,否则会报错
String sql = "select * from user where username = '" + username + "' and password = '" + password +"' ";
System.out.println(sql);
//3、获取数据库连接对象 使用JDBC工具类中的方法
conn = JDBCUtils.getConnection();
//4、获取执行sql的Statement的对象
stat = conn.createStatement();
//5、执行sql
rs = stat.executeQuery(sql);
//6、处理结果
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.releaseRes(rs, stat, conn);
}
return false;
}
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();
if (login(username,password)) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
}
}
5、PreparedStatement对象
SQL注入问题
在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题
用户名随便输入,密码输入a’ or ‘a’ = 'a
与select * from user
where username = ‘" + username + "’&& password = ‘" + password +"’"拼接得到
select * from user where username = ‘djjcndsub’ && password = ‘a’ or ‘a’ = ‘a’
false or true 返回true
解决:使用PreparedStatement对象
PreparedStatement
表示预编译的sql的对象
Statement执行的是静态sql,即sql语句中的参数在生成sql时都是拼接好的,传入参数之后就自动生成
预编译sql:参数使用占位符?,执行sql时给?赋值
步骤:
(1)注册驱动
(2)定义sql
sql的参数使用?作为占位符,
如:select * from emp where username = ? && password = ?;
(3)获取数据库连接对象
(4)获取执行sql的Statement的对象
Connection.preparedStatement(String sql);
(5)给参数赋值
PreparedStatement.setXXX(参数1,参数2);给参数赋值
XXX 数据类型
参数1 ?的位置编号,从1开始
参数2 ?的值
(6)执行sql
使用空参方法,因为前面已经传递了参数sql语句
PreparedStatement.executeUpdate();
PreparedStatement.executeQuery();
(7)处理结果
(8)释放资源
后期都会使用PreparedStatement对象进行增删改查的操作
(1)可以防止sql注入
(2)效率更高
public class Demo11PreparedStatement {
//改进后的登录方法
public static boolean login(String username, String password) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
try {
//1、注册驱动,JDBC工具包中已写
//2、定义sql
String sql = "select * from user where username = ? && password = ?";
//3、获取数据库连接对象
conn = JDBCUtils.getConnection();
//4、获取执行sql的Statement的对象
stat = conn.prepareStatement(sql);
//5、给sql的参数赋值
stat.setString(1, username);
stat.setString(2, password);
//6、执行sql
rs = stat.executeQuery();
//7、处理结果
return rs.next();//游标下移有数据,则返回true
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.releaseRes(rs, stat, conn);
}
return false;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
if (login(username, password)) {
System.out.println("登录成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
}
6、在JDBC中管理事务
事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理,这些步骤要么同时成功要么同时失败
在JDBC中管理事务使用Connection对象
(1)开启事务 执行sql之前
Connection.setAutoCommit(false);
(2)提交事务 执行完所有sql之后
Connection.commit();
(3)回滚事务 catch里
Connection.rollback();
public class Demo12JDBCTransaction {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement stat1 = null;
PreparedStatement stat2 = null;
try {
//1、注册驱动 JDBC工具包中已写
//2、定义sql
String sql1 = "update account set balance = balance - ? where name = ?";
String sql2 = "update account set balance = balance + ? where name = ?";
//3、获取连接对象
conn = JDBCUtils.getConnection();
//在执行sql之前开启事务
conn.setAutoCommit(false);
//4、获取执行sql的Statement的对象
stat1 = conn.prepareStatement(sql1);
stat2 = conn.prepareStatement(sql2);
//5、给sql的参数赋值
stat1.setDouble(1, 500);
stat1.setString(2, "张三");
stat2.setDouble(1, 500);
stat2.setString(2, "李四");
//6、执行sql
int count1 = stat1.executeUpdate();
//手动制造异常
//int i = 3 / 0;
int count2 = stat2.executeUpdate();
//7、处理结果
System.out.println(count1);
System.out.println(count2);
if (count1 > 0 && count2 > 0) {
System.out.println("修改成功!");
}
//处理完所有sql之后提交事务
conn.commit();
} catch (Exception e) {
//有任何异常都回滚事务
try {
if (conn != null) {
//防止空指针异常
conn.rollback();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.releaseRes(stat1, stat2, conn);
}
}
}