一、概述
1.什么事JDBC?
Java DataBase Connectivity(Java语言连接数据库)。
2.本质
- JDBC是SUN公司制定的一套接口(interface)。
- Java.sql.*
- 接口都有调用者和实现者
- 面向接口调用,面向接口写实现类,这都属于接口编程。
3.为什么要面向接口编程?
- 解耦合:降低程序的耦合度,提高程序的扩展能力。
- 态机制就是非常典型的:面向抽象编程。
有了各个数据库厂家对JDBC的实现,程序员就不需要关心需要向数据库编程的问题了,只是面向JDBC编程,调用JDBC就可以了。
4.JDBC 开发前的准备工作。
先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath中。
二、常用接口
1.Driver接口
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
装载MySql驱动:Class.forName(“com.mysql.jdbc.Driver”);
装载Oracle驱动:Class.forName(“oracle.jdbc.driver.OracleDriver”);
2.Connection接口
- Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url,
user, password)方法建立在JDBC URL中定义的数据库Connection连接上。
- 连接MySql数据库:Connection conn =
DriverManager.getConnection(“jdbc:mysql://host:port/database”,
“user”, “password”);
- 连接Oracle数据库:Connection conn =
DriverManager.getConnection(“jdbc:oracle:thin:@host:port:database”,
“user”, “password”);
- 连接SqlServer数据库:Connection conn =
DriverManager.getConnection(“jdbc:microsoft:sqlserver://host:port;
DatabaseName=database”, “user”, “password”);
常用方法:
createStatement():创建向数据库发送sql的statement对象。prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql):创建执行存储过程的callableStatement对象。
事务
1)conn.setAutoCommit(false);//开启事务
2) conn.commit();//提交事务
3)conn.rollback();//回滚事务
添加事务的代码
事务能解决那些问题!请参考
https://blog.csdn.net/shang_0122/article/details/106048617
public class JDBCTest10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dmsd?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123");
//将自动提交机制修改为手动提交
conn.setAutoCommit(false);//开启事务
//3.获取预编译的数据库操作对象
String sql = "update t_act set balance=? where actno=?";
ps = conn.prepareStatement(sql);
//给?传值
ps.setDouble(1, 10000);
ps.setInt(2, 111);
int count = ps.executeUpdate();//执行第一条UPDATE语句
// System.out.println(count);
//String s = null;
//s.toString();
//重新给占位符传值
ps.setDouble(1, 10000);
ps.setInt(2, 222);
count += ps.executeUpdate();//执行第二条UPDATE语句
//4.执行SQL
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 throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
3.Statement接口
用于执行静态SQL语句并返回它所生成结果的对象,在下文会有Statement用法及介绍。
4.ResultSet接口
ResultSet提供检索不同类型字段的方法,常用的有:
- getString(int index)、getString(String
columnName):获得在数据库里是varchar、char等类型的数据对象。 - getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
- getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
- getBoolean(int index)、getBoolean(String
columnName):获得在数据库里是Boolean类型的数据。 - getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。
ResultSet还提供了对结果集进行滚动的方法:
- next():移动到下一行 Previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面。
- afterLast() :移动到resultSet的最后面。
使用后依次关闭对象及连接:ResultSet → Statement → Connection
三、JDBC编程六步
第一步:注册驱动
作用:告诉java程序,即将要连接的是那个品牌数据库
第二步:获取链接
作用:表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭连接。
第三步:获取数据库操作对象
作用:(专门执行sql语句的对象)
第四步:执行SQL语句(增删改查)
第五步:处理查询结果集
只有当第四步执行的是select语句的时候,才有第五步处理查询结果集。
第六步:释放资源
使用完资源之后一定要关闭资源。java和数据库之间的通信,开启之后一定要关闭。
四、典型代码
1.获取连接内容写在代码中的方式。
这种写法比较麻烦,每次都需要写,所以就想办法把这些重复性的东西得拿出来了。
public class JDBCTest01 {
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
//第一步:注册驱动
Driver driver=new com.mysql.jdbc.Driver();
DriverManager .registerDriver(driver);
//第二步:获取连接
String url="jdbc:mysql://127.0.0.1:3306/db";
String user="root";
String password="123456";
conn=DriverManager.getConnection(url,user,password);
System.out.println("数据库连接对象:="+conn);
//第三步:获取数据库操作对象
//createStatment:专门执行SQL语句。
stmt=conn.createStatement();
//第四步:执行SQL语句
String sql="insert into dept(deptno,dname,loc) values(xx,'xx','xx')";
//专门执行DML语句的(insert,delete,update)
//返回值是“影响数据库中记录条数”
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "保存成功" : "保存失败");
//第五步:处理查询结果集
}catch ( SQLException e){
e.printStackTrace();
}
//为了保证资源一定释放,在finally语句块中关闭资源
//遵循从小到大依次关闭
//分别对其try...catch
//第六步:释放资源
if (stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
2. 将获取连接写成一行代码,避免了上面那个写法,这种写法最重要的是在
Statement和preparedStatem的对比。
1)相比于上面的Satatement用法和方法,以及PreparedStatement解决了什么问题?
①解决SQL注入问题
-
只要用户提供的信息不参与SQL语句的编译,问题就解决了。
-
即使用户提供的信息中含有SQL语句的关键字,但没有参与编译,不起作用。
-
要想用户信息不参与SQL语句编译,那么使用java.sql.PreparedStatement
-
PreparedStatement接口继承了java.sql.Statement
-
PreparedStatement是属于预编译的数据库操作对象
-
PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”
②解决SQL注入的关键
用户提供的信息中即使含有sql语句的关键字,但这些关键字并没有参与编译,不起作用。
③对比Statement和preparedStatement
-
Statement存在SQL 注入问题,PreparedStatement解决了SQL注入问题。
-
Statement是编译一次执行一次,PreparedStatement是编译一次,执行N次,PreparedStatement效率较高一些。
-
PreparedStatement会在编译阶段做类型的安全检查
Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。 -
PreparedStatement
:继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL -
CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。
④常用Statement方法:
-
execute(String sql):运行语句,返回是否有结果集
-
executeQuery(String sql):运行select语句,返回ResultSet结果集。
-
executeUpdate(String sql):运行insert/update/delete操作,返回更新的行
数。
-
addBatch(String sql) :把多条sql语句放到一个批处理中。
-
executeBatch():向数据库发送一批sql语句执行。
通过对比:PreparedStatement使用较多,只有在极少数情况下使用Statement业务方面必须支持SQL注入的时候。
Statement支持SQL注入,凡是业务方面要求进行SQL语句拼接的,必须使用Statement
运行PreparedStatement解决SQL注入以后的代码
public class JDBCTest09 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
try {
//1.注册驱动
Class .forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/dmsd?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123");
//3.获取预编译的数据库操作对象
String sql="update t_user set loginName=? where id=?";
ps=conn.prepareStatement(sql);
ps.setString(1,"wangmazi");
ps.setInt(2,1);
int count =ps.executeUpdate();//执行第一条UPDATE语句
System.out.println(count);
//重新给占位符传值
ps.setString(1,"zhangsi");
ps.setInt(2,2);
count=ps.executeUpdate();//执行第二条UPDATE语句
//4.执行SQL
System.out.println(count);
}catch (Exception e){
e.printStackTrace();
}finally {
//6.释放资源
if (ps!=null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn !=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
3.代码优化,遵循面向对象的思想,运用封装。
封装JDBC 工具类
public class DBUtil {
/**
* 工具类中的构造方法都是私有的
* 因为工具类中的方法都是静态的,不需要new对象,直接采用类名调用
*/
private DBUtil() {
}
//静态代码块在类加载时执行,并且只执行一次。
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库操作对象
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/dmsd?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123");
}
/**
* 关闭资源
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集
*/
public static void close(Connection conn, Statement ps, ResultSet rs) {
if (rs != null) {
try {
rs.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();
}
}
}
}
调用工具类,让代码看起来很精简。
public class JDBCTest11 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//获取连接
conn= DBUtil.getConnection();
//获取预编译的数据库操作对象
//错误写法
/*
String sql="select loginName from t_user where loginName like'_?%'";
ps=conn.prepareStatement(sql);
ps.setString(1,"w");
*/
//正确写法
String sql="select loginName from t_user where loginName like ?";
ps=conn.prepareStatement(sql);
ps.setString(1,"z%");
rs=ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("loginName"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
//释放资源
DBUtil.close(conn,ps,rs);
}
}
}
总结:在代码中会发现,加了一句?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai,这句代码解决了在连接mysql数据库字符编码出错和时区的问题,可以参考
https://blog.csdn.net/yyp0304Devin/article/details/106061531。