JDBC
Java DataBase Connectivity(java语言连接数据库)
JDBC的本质
JDBC是一套接口(在java.sql.*包下),接口都有调用者和实现者,面向接口调用、面向接口写实现类,都属于面向接口编程。
面向接口编程的好处:
解耦合:降低程序的耦合度,提高程序的扩展力(多态机制就是非常典型的面对抽象编程)
JDBC编程
编程步骤:
- 注册驱动(告知要连接的是哪种类型数据库)
- 获取连接(打开JVM进程和数据库之间的通道,属于进程之间的通信,用完一定要关掉)
- 获取数据库操作对象(专门执行sql语句)
- 执行sql语句(主要执行DQL,DML…)
- 处理查询结果(第四步执行select语句才会有结果集)
- 释放资源(使用完资源之后一定要关闭资源)
url
url:统一资源定位符(某个资源的绝对路径)
url组成:协议、IP、PORT、资源名
jdbc:mysql://localhost:3306/mysql
jdbc:mysql://是协议
localhost是IP地址
3306是mysql数据库的端口号
mysql是具体的数据库实例名字
注:localhost和127.0.0.1都是本机ip地址
什么是通信协议:
通信协议是通信之前就提前定好数据传送格式
数据包具体怎么传数据,格式提前定好。
运用
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Main {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
//1.注册驱动
Driver driver;
driver = new com.mysql.cj.jdbc.Driver();//使用多态,父类指向子类的对象,不同数据库Driver的包名也不同
DriverManager.registerDriver(driver);
//2.获取连接
String url = "jdbc:mysql://localhost:3306/mysql";//数据库的位置
String user = "root";//用户名
String password = "123456";//密码
conn = DriverManager.getConnection(url, user, password);//不管是mysql还是sqlserver,都用connection接口对象来调用即可
//3.获取数据库操作对象(Statement专门执行sql语句)
stmt = conn.createStatement();
//4.执行sql语句
String sql = "insert into dept(deptno,dname,loc) values('50','销售部','深圳')";
//专门执行DML语句的insert,delete,update
//返回值是影响数据库中的记录的条数
int count = stmt.executeUpdate(sql);//多少条数据改变,则count就是多少
//因为该sql语句不是select语句,所以不用处理查询结果集
} catch (SQLException e) {
e.printStackTrace();
}finally {
//6.释放资源
//为了保证资源一定释放,在finally语句块中释放资源
//并且要遵循从小到大依次关闭
//关闭的时候分别进行trycatch因为堆在一个try里面的话,只关闭一个资源就会进入catch
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stmt != null)
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
注册驱动的第二种方式
可以使用类的加载的方式来注册驱动,通过使用类的反射机制来调用类中的静态方法
用法如下:
Class.forName(“com.mysql.cj.jdbc.Driver”);//该方法不用接受返回值,因为只需要用到其中类的加载动作
Connection conn = DriverManager.getConnection(url, user, password);
从属性资源文件中读取连接数据库的信息
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
public class Main {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
//使用资源绑定器绑定属性配置文件
ResourceBundle bundle = ResourceBundle.getBundle("try2.jdbc");//注意配置文件的路径要写清楚所在包名,而且配置文件必须写在src包下
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
/*再用上面获取到的数据按照jdbc编程的步骤即可*/
}
}
配置文件jdbc.properties如下:
处理查询结果集
public class Main {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;//select查询出的结果集数据类型
try {
/*前面三步按照jdbc编程步骤来进行*/
//4.执行DML语句
String sql = "select * from emp ";
//executeUpdate(insert/delete/update)
//executeQuery(select)
rs = stmt.executeQuery(sql);//rs则是select语句的结果集
//5.处理查询结果集
boolean flag1 = rs.next();//光标当前位置的下一个位置有数据则返回true,无则返回false
// 光标指向的行有数据,光标初始位置从0开始,JDBC中所有下标从1开始。
// 取数据
// getString(列号/列名) ,如果列名有重命名则写重命名之后的列名
//方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。
String empno = rs.getString(1);
String ename = rs.getString("ename");
//数据还可以以其相应的类型取出
double sal =rs.getDouble(3);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//6.释放资源
/*关闭资源从ResultSet到Statement到Connection*/
}
}
}
sql注入
什么是sql注入
当前登录数据库查询的sql语句为 String sql = “select * from t_user where loginName = ‘”+loginName+“’ and loginPwd = ‘”+loginPwd+“’”;
当用户loginName输入fdsa,loginPwd输入fdsa’ or ‘1’=’1则会成功,该现象就是sql注入现象。
出现的原因
用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。就是因为注入了or’1’=’1,从而使得输入账号密码正不正确都能成功登录。
解决方法
只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
-
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
-
要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
-
**PreparedStatement**接口继承了java.sql.Statement
-
**PreparedStatement**是属于预编译的数据库操作对象。
-
**PreparedStatement**的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”
// 打标记的意识
boolean loginSuccess = false;
// 单独定义变量
String loginName = sc.nextLine();
String loginPwd = sc.nextLine();
// JDBC代码
Connection conn = null;
PreparedStatement ps = null; // 这里使用PreparedStatement(预编译的数据库操作对象)
ResultSet rs = null;
try {
// 1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
// 3、获取预编译的数据库操作对象
// SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来,所以不能传字符或者字符串
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
// 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
ps = conn.prepareStatement(sql);
// 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
ps.setString(1, loginName);
ps.setString(2, loginPwd);
// 4、执行sql
rs = ps.executeQuery();
// 5、处理结果集
if(rs.next()){
// 登录成功
loginSuccess = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6、释放资源
}
对比一下Statement和PreparedStatement
- Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。
- Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次。PreparedStatement效率较高一些。
- PreparedStatement会在编译阶段做类型的安全检查.
必须使用Statement的情况
Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的(需要客户输入的数据是字符或者字符串且得直接拼接到sql语句,就是说不是=某个字符串这种类型,如升序或者降序这种),则必须使用Statement。
eg: String sql = “select ename from emp order by ename ?”;如果想要降序则问号得输入desc,而输入的话会有双引号,导致语句出现错误。
事务
因为java程序中机制为自动提交机制,即语句一行一行执行,则当中途出现错误时,可能造成数据丢失。
- 设置手动提交机制conn.setAutoCommit(false);因为默认值是true
- 事务提交conn.commit();语句全部成功执行在提交
- 事务回滚conn.rollback();发生异常时撤销全部操作
Connection conn = null;
PreparedStatement ps = null;
try {
/*正常注册驱动获取连接*/
// 将自动提交机制修改为手动提交
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();
//执行任务二
ps.setDouble(1, 10000);
ps.setInt(2, 222);
count += ps.executeUpdate();
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、释放资源
}
简化jdbc编程
一些注册驱动获取连接等操作泰国冗余,则可以对其进行封装,封装好的工具类。
import java.sql.*;
public class DBUtil {
/**
* 工具类中的构造方法都是私有的。
* 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
*/
private DBUtil() {
}
// 静态代码块在类加载时执行,并且只执行一次。
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
}
/**
* 关闭资源
* @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();
}
}
}
}
应用:
import com.bjpowernode.jdbc.utils.DBUtil;//导入工具类所在的包
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class Main {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 获取预编译的数据库操作对象
// 模糊查询的错误写法
/*
String sql = "select ename from emp where ename like '_?%'";
ps = conn.prepareStatement(sql);
ps.setString(1, "A");
*/
String sql = "select ename from emp where ename like ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "_A%");
rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (Exception e) {
e.printStackTrace();
} finally{
// 释放资源
DBUtil.close(conn, ps, rs);
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84195.html