一.JDBC基本概念
JDBC,全拼为:Java DataBase Connectivity ,是一种Java程序访问数据库的标准接口。
当使用Java程序访问数据库时,Java代码并不是直接就去操作数据库的,而是通过JDBC接口去访问,JDBC接口再通过JDBC驱动来实现真正的数据库访问。
在Java中,JDBC接口是JDK自带的,可以直接调用,但是具体的JDBC驱动是根据不同的数据库厂商来提供的,其实也是java语言编写的,是一个jar包,在我们使用的时候导入相关的数据库驱动jar包就好了。(目的就在于同一套Java代码处理访问数据库的代码,但可以访问各种不同的数据库,根据数据库驱动去实现)
Java与数据库驱动和数据库之间的关系,如下:
JDBC.png
好处:
- 使用同一套java代码,针对不同的数据库,不需要重新开发。
- 不受限于底层数据库,即使换一个其他数据库,java代码基本不变。
总结:
在开发时,只需完成对JDBC接口的调用,底层的数据库具体操作不需要我们去实现,由驱动完成。
二.JDBC开发主要步骤
先记录开发步骤,后面再详细学习各个对象。
-
导入驱动jar抱
比如mysql,需要导入mysql-connector-java-5.1.37-bin.jar
在idea中的导入的步骤:
- 复制jar包到项目的lib文件夹下
- 右键lib文件夹 --> Add As Library
-
注册驱动
-
获取数据库连接对象 Connection
-
定义sql语句
-
获取执行sql语句的对象 Statement/PreparedStatement
-
执行sql,接收返回结果
-
处理结果
-
释放资源
有两种执行sql语句的对象:Statement和PreparedStatement版本(以后都是用PreparedStatement,区别下面再解释)
Statement版本:
public class JDBCDemo2 {
public static void main(String[] args) {
Connection conn = null;
Statement state = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取数据库连接对象Conn
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
// 3. 定义sql语句
String sql = "insert into student3 values(15,'亚瑟',28,'男','深圳',80,85)";
// 4. 获取执行sql对象
state = conn.createStatement();
// 5. 执行sql
int count = state.executeUpdate(sql);
if (count > 0) {
System.out.println("添加成功");
} else {
System.out.println("执行失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6. 释放资源
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
PreparedStatement版本
public class JDBCDemo12 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstate = null;
ResultSet rs = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取数据库连接对象Connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
// 3. 定义sql语句
String sql = "select * from user where id = ?";
// 4. 获取sql执行对象
pstate = conn.prepareStatement(sql);
pstate.setInt(1,2);
// 5. 执行sql
rs = pstate.executeQuery();
// 6. 处理结果
if (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String password = rs.getString("password");
System.out.println("id = " + id + ",name = " + name + ",password = " + password);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7.释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstate != null) {
try {
pstate.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
三.JDBC各个对象的详解
-
DriverManager:驱动管理对象
作用:
-
用于管理和注册数据库驱动。
使用方法:
Class.forName("com.mysql.jdbc.Driver")
查看Driver源码,发现在com.mysql.jdbc.Driver类中存在静态代码块
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
代码块里调用了
DriverManager.registerDriver(new Driver())
去注册给定的驱动程序注:mysql5之后可以省略注册驱动的步骤。也就是可以省略
Class.forName("com.mysql.jdbc.Driver")
-
获取数据库连接对象。
代码中使用方式:
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
使用方法:
static Connection getConnection(String url, String user, String password)
:通过连接字符串,用户名和密码来获取数据库的连接对象。参数:
-
url:指定连接的路径,不同的数据库url是不同的。
mysql的是:dbc:mysql://localhost:3306/数据库名字
-
user:数据库用户名
-
password:数据库密码
-
-
-
Connection:数据库连接对象
Connection接口的具体实现是由数据库厂商实现的,代表了一个连接对象。
作用:
-
获取执行sql的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
-
管理事务
事务管理就是指针对一系列的操作,这些操作要么同时成功,要么同时失败。
-
setAutoCommit(boolean autoCommit)
:开启事务,设置参数为false,即开启事务 -
commit()
:提交事务 -
rollback()
:回滚事务
-
-
-
Statement:执行sql语句的对象
Statement是一个sql语句对象,用于发送sql给服务器,执行静态sql语句,并返回相关的结果对象。
作用:
- 执行sql
-
int executeUpdate(String sql)
:用于执行增删改的操作(insert,update,delete),返回值是对数据库影响的行数。 -
ResultSet executeQuery(String sql)
:用于执行查询的操作(select),返回值为一个ResultSet结果集。
-
- 执行sql
-
ResultSet:查询结果对象,封装了查询结果
ResultSet是执行了查询操作
executeQuery(String sql)
后返回的一个对象,封装的是数据库返回的数据,把每一条记录封装成一个ResultSet对象。ResultSet提供了一些方法方便获取具体的数据:
-
getXxxx(参数)
:获取数据。提供了一系列的方法来获取不同类型的数据,比如:
int getInt()
,String getString()
数据类型依据于要获取的数据的那一列的数据类型。
这个方法有两种传参方式:一种是传递int值,代表编号(编号从1开始),一种是传递String,代表列名称。
比如:
String getSrting(int columnIndex)
:传递参数为列的编号,从1开始的。String getString(String columnIndex)
:传递参数为列的名称。 -
boolean next()
:游标向下移动一行,判断当前行是否是最后一行(是否有数据),如果是最后一行,没有数据,则返回false,否则就返回true。
基于ResultSet提供的方法,可以通过以下操作获取数据库返回的所有查询结果:
// 循环判断是不是最后一行,如果是最后一行会返回false while (rs.next()) { // 获取这一条记录的每一列的数据 int id = rs.getInt("id"); String name = rs.getString(2); ... }
-
-
PreparedStatement:执行sql语句的对象
-
使用Statement存在问题:SQL注入问题
在拼接SQL语句时,有一些SQL的特殊关键字参与字符串的拼接,会造成安全性问题
举例:让用户输入用户名和密码,正确即可登录数据库。
public class JDBCDemo7 { public static void main(String[] args) { String name; String pw; Connection conn = null; Statement state = null; ResultSet rs = null; // 1. 获取用户输入 Scanner sc = new Scanner(System.in); System.out.print("请输入用户名:"); name = sc.nextLine(); System.out.print("请输入密码:"); pw = sc.nextLine(); // 2. 连接数据库 try { conn = JDBCUtil.getConnection(); // 定义sql String sql = "select * from user where name='" + name + "'and password='" + pw + "'"; state = conn.createStatement(); rs = state.executeQuery(sql); if (rs.next()) { System.out.println("登录成功"); } else { System.out.println("登录失败"); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtil.close(rs,state,conn); } } }
当输入随意用户名,密码为:a' or 'a' = 'a时,会发现居然登录成功了!!!
PreparedStatement.png这就是Statement带来的一个问题。
-
解决方法就是使用PreparedStatement(以后都是使用PreparedStatement来完成操作)
-
PreparedStatement使用方法
PreparedStatement使用?作为占位符,然后再调用方法填充数据,避免了字符串拼接的方式
具体步骤是这样的(前面几个步骤和之前一样,这里从定义sql开始)
-
定义sql,使用?作为占位符。
比如:
String sql = select * from user where username = ? and password = ?;
-
获取执行sql的对象PreparedStatement
pstate = conn.prepareStatement(sql);
-
给sql语句的占位符?赋值
使用PreparedStatement提供的方法:
setXxxx(参数1,参数2)
:这个方法和ResultSet中的方法类似,也是根据数据类型调用对应的方法。- 参数1:占位符?的位置编号,从1开始。
- 参数2:占位符?的值。
比如:
pstate.setInt(1,2);
-
执行sql,获取返回结果,不需要再传递sql了(在获取PreparedStatement对象时候已经传递了)
rs = pstate.executeQuery();
-
处理结果
-
释放资源
-
-
四.实现自己的JDBC工具类:JDBCUtils
在真正编写代码之后,我们发现每一次操作数据库,都要经历上述的至少7,8个步骤,步骤很繁琐,而且重复度很高,所以为了方便使用以及简化书写,可以把JDBC的相关操作抽取出来,作为一个工具类,提供相关方法供调用。
步骤:
-
整合注册驱动部分,驱动的注册只需要注册一次即可,不需要每一次都注册。
可以使用静态代码块,在第一次加载类时调用一次。
-
整合一个获取连接对象的方法
数据库用户名,密码等信息可以通过配置文件获取,使用Properties集合类。
-
整合一个释放资源的方法
针对不同操作,查询操作需要释放ResultSet,可以使用重载方法的方式提供同名的方法。
public class JDBCUtil {
private static String url;
private static String root;
private static String password;
private static String driver;
/*配置文件的读取,只需要读取一次即可,使用静态代码块*/
static {
// 读取资源文件,获取值
// 1. 创建Properties集合类
Properties pro = new Properties();
// 获取src路径下的文件的方式 -- ClassLoader 类加载器
ClassLoader classLoader = JDBCUtil.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
// 2. 加载文件
try {
pro.load(new FileInputStream(path));
} catch (IOException e) {
e.printStackTrace();
}
// 3. 获取数据,赋值
url = pro.getProperty("url");
root = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
// 4. 注册驱动
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/* 获取连接对象 */
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, root, password);
}
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();
}
}
}
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();
}
}
}
}
网友评论