JDBC基础--[JDBC概述,JDBC的搭建,PreparedStatement和Statement执行SQL语句,结果集处理]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC基础--[JDBC概述,JDBC的搭建,PreparedStatement和Statement执行SQL语句,结果集处理]相关的知识,希望对你有一定的参考价值。
JDBC概述
Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
由一组用Java语言编写的类和接口组成
对于数据库的操作无非就是增删改查的操作;java语言的开发者并不会直接进行数据库的操作;而是定义了一些接口,最终是由数据库的开发商去完成这些接口的具体实现.
所以也就可以访问不同的数据库.
JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中),主要包括(斜体代表接口,需驱动程序提供者来具体实现):
DriverManager类:负责加载(管理)各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection).
Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
Connection接口 :与特定数据库的连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。
Statement接口 :用以执行SQL查询和更新,向数据库发送SQL语句(针对静态SQL语句和单次执行)。
PreparedStatement接口:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
CallableStatement:用以调用数据库中的存储过程。
ResultSet接口:接收查询结果;表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
SQLException:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误)。
JDBC搭建
首先;在项目栏新建文件夹;(一般命名为lib有library的意思
)
将需要的数据库实现类的jar包复制到该文件夹;
1.导入jar包;
首先点击 File -> Project Structure(快捷键 Ctrl + Alt + Shift + s);找到左侧的选项Libraries;点击加号;点击Java
然后找到对应的项目存放地址;找到jar包,选择导入.
2.加载驱动;(在jdbc8之后,可以直接不写;会自动地加载)
这需要初始化驱动程序,这样就可以打开与数据库的通信信道。
方式1:
//java的反射机制,动态加载类;
Class.forName("com.mysql.cj.jdbc.Driver");
方式2:
DriverManager.registerDriver(new Driver());
在jdbc8之后,这些操作可以不用;会自动地加载驱动.
3.建立与数据库的连接
这里需要用到的是DriverManager类中的getConnection方法;代表一个物理连接的数据库.里面有三个参数;
url:ip和端口以及过滤字符编码,时区;
user:数据库用户名;
password:数据库密码.
返回的是Connection的对象,实际上是Connection实现类的对象;
//url的参数
jdbc:mysql://127.0.0.1(ip地址):3306(端口号)/(数据库名)?characterEncoding=utf8(字符编码过滤)&useSSL=false&serverTimezone=UTC(时区)
例如:
//建立与数据库studentworld_db的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//这里的Connection对象作为与数据库连接的通道;
Connection connection=DriverManager.getConnection(url,"root","123456");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
若运行无异常;则说明连接成功.注意,一定要准确数据库名,用户名,密码
4.向数据库发送SQL语句 在发送结束后,要记得关闭通道
PreparedStatement和Statement
使用Statement接口
首先是创建对象Statement st=connection.createStatement( );
使用executeUpdate方法执行SQL语句
这个Statement接口的executeUpdate方法有int类型的返回值;
发送DDL语句时,返回的是数字0;
发送DML语句(增,删,改)时,返回的是操作的行数.
例如;向数据库studentworld_db的数据表t_course下插入数据;(使用DML语句)
public class Demo01 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection=DriverManager.getConnection(url,"root","123456");
//向数据库发送SQL语句;
Statement st=connection.createStatement();
st.executeUpdate("insert into t_course(cou_id,Cname) values(108,'数分'),(109,'英语')");
//关闭数据库连接通道;
st.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
返回值为2;即插入了两行数据.
查看数据库这边,已经成功插入数据;
模拟数据库发送数据;
例如:向数据库studentworld_db的学生表t_student中传入数据;
使用Statement接口
注意要用连接符号把数据一个一个连接起来;字符串类型的用单引号,双引号,双加号包裹起来;数值类型的用双引号,双加号即可;
public class Demo02 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//向数据库发送SQL语句;
Statement st=connection.createStatement();
//模拟向数据库输入动态数据;
//以数据库的t_student表为例;
String name="大话王";
String sex="男";
String birthday="2018-5-7";
String phone="12345665412";
int gra_id=1805;
java.util.Date date=new java.util.Date();
java.sql.Timestamp res_time= new java.sql.Timestamp(date.getTime());
int score=82;
//向数据库发送数据;
//注意要用连接符号把数据一个一个连接起来;;字符串类型的用单引号,双引号,双加号包裹起来;数值类型的用双引号,双加号即可;
st.executeUpdate("INSERT INTO t_student(NAME,sex,birthday,phone,gra_id,res_time,score)" +
"VALUES('"+name+"','"+sex+"','"+birthday+"','"+phone+"',"+gra_id+",'"+res_time+"',"+score+")");
//关闭数据库连接通道;
st.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
使用PrepareStatement接口执行sql语句 (简单且安全性高这个接口还继承了Statement
)
首先会预先地将SQL语句编译到PrepareStatement对象中;在sql语句中参数位置使用占位符(即?
),一个占位符表示一个值;然后再向SQL语句中的占位符赋值;
在向占位符赋值时;可以使用set方法;来为各种参数赋值;
这些方法的参数parameterIndex 为int类型的,即占位符的位置(第几个 ?),
而且在传入数据时,可以直接将数据一个一个地赋值;不像Statement那样;需要注意使用符号分隔数据;
对占位符赋值结束后,还需要执行PrepareStatement接口的executeUpdate方法;
这个方法也是返回int类型的值;
若执行DDL语句则返回数字0;
若执行DML语句则返回操作的数据行数.
例如:使用PrepareStatement接口执行sql语句向数据库studentworld_db的数据表t_student中插入数据
public class Demo03 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//模拟向数据库输入动态数据;
//以数据库的t_student表为例;
String name="梅尔萨斯";
String sex="女";
String birthday="2014-1-6";
String phone="12342365412";
int gra_id=1805;
Date date=new Date();
java.sql.Timestamp res_time= new java.sql.Timestamp(date.getTime());
int score=75;
//向数据库发送SQL语句;这里会使用 ?(占位符);来表示一个值的位置;
String sql="INSERT INTO t_student(NAME,sex,birthday,phone,gra_id,res_time,score)VALUES(?,?,?,?,?,?,?)";
//预先将SQL语句编译到对象中;
PreparedStatement pps=connection.prepareStatement(sql);
//向占位符赋值;(注意数据类型);
pps.setString(1,name);
pps.setString(2,sex);
pps.setObject(3,birthday);
pps.setString(4,phone);
pps.setInt(5,gra_id);
pps.setTimestamp(6,res_time);
pps.setInt(7,score);
//执行方法executeUpdate;
int res=pps.executeUpdate();
System.out.println(res);
//关闭数据库连接通道;
pps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
Statement和PreparedStatement的区别
Statement:
- 静态的SQL语句执行;每当操作一次就向数据库发送一次;
- 需要将变量以字符串的形式拼接;书写麻烦;代码的可读性以及维护性太差.
- 不可以防止SQL注入;安全性低.
PreparedStatement
- 预先地将SQL语句编译到PreparedStatement对象中;可重复使用;效率高
- 使用的是set方法为写好的占位符进行赋值;书写方便;代码的可读性以及维护性好.
- 可防止SQL注入,安全性高.
SQL注入:
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
原理:
SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法。简单的说就是在post/getweb表单、输入域名或页面请求的查询字符串中插入SQL命令,最终使web服务器执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为http://www.example.com?test=123,此时URL实际向服务器传递了值为123的变量test,这表明当前页面是对数据库进行动态查询的结果。由此,我们可以在URL中插入恶意的SQL语句并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。
模拟SQL注入:
(1)使用Statement不能防止SQL注入:
例如想要通过危险的SQL语句条件删除数据表t_fortnitepe中的person角色"战术咸鱼"那一行;但是整个数据表都被删除数据了.
public class Demo04 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//模拟暴力的SQL注入,直接将数据值拼接到字符串中,将1==1 也当做person值了;
String person="'战术咸鱼' or 1=1";
//向数据库发送数据;
Statement st=connection.createStatement();
int res= st.executeUpdate("DELETE FROM t_fortnitepe WHERE person="+person);
System.out.println(res);//获取被删除的行数;
//关闭数据库连接通道;
st.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
发现删除了10行数据,数据表被清空了.
把数据表的数据重新备份回去;
(2)PreparedStatement可以防止SQL注入:
例如想要通过带有危险的SQL语句条件删除数据表t_fortnitepe中的person角色"战术咸鱼"那一行;数据表的任何数据不会被删除.
在数据赋值给占位符时会进行预先处理;也就不会将危险的数据执行了;
public class Demo05 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//定义删除的条件;
String person="'战术咸鱼' or 1=1";
//预先将SQL语句编译到对象中;这里会使用 ?(占位符);来表示一个值的位置;
PreparedStatement pps=connection.prepareStatement("DELETE FROM t_fortnitepe WHERE person=?");
//向占位符赋值;
pps.setString(1,person);
//执行方法executeUpdate;
int res=pps.executeUpdate();
System.out.println(res);//获取被删除的行数;
//关闭数据库连接通道;
pps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
发现删除0行数据;数据表的数据都在
对于结果集的处理
注意使用方法executeQuery执行查询语句;
返回一个ResultSet 集合
ResultSet res=executeQuery( );
可以将ResultSet对象获取到;然后转化到Java中自定义的类的对象;一个对象就可以存储一行数据记录.若有多行结果集数据,可以将所有对象存到一个集合中.
- ResultSet中的next( )方法;可以判断查询出的结果集中是否还有数据;如果有就返回true;指向下一行数据.
- ResultSet中的get***( )方法可以获取到查询的结果集的数据.
例如,查询1801班的学生;
public class Demo06 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//定义查询的条件;
int gra_id=1801;
//这里会使用 ?(占位符);来表示一个值的位置;
String sql="SELECT stu_id,name,sex,phone,gra_id,res_time,score FROM t_student WHERE gra_id=?";
//预先将SQL语句编译到对象中;
PreparedStatement pps=connection.prepareStatement(sql);
//向占位符赋值;
pps.setInt(1,gra_id);
//使用方法executeQuery执行查询语句;
ResultSet res=pps.executeQuery();
//可以将数据表的数据装到自定义的集合中;
List<Student> studentList=new ArrayList<>();
//使用next方法判断结果集中是否还有数据;有就返回true,继续查下一行数据;
while(res.next()){
//每查询到一行数据就新建一个对象;
Student student=new Student();
//使用get方法获取结果集中的数据,然后存入Student类的对象中;
student.setStuID(res.getInt("stu_id"));
student.setName(res.getString("name"));
student.setSex(res.getString("sex"));
student.setPhone(res.getString("phone"));
student.setGraID(res.getInt("gra_id"));
student.setResTime(res.getTimestamp("res_time"));
student.setScore(res.getInt("score"));
//将对象中的数据存入集合;
studentList.add(student);
}
//遍历输出集合studentlist;
for(Student list:studentList){
System.out.println(list);
}
//关闭数据库连接通道;
res.close();
pps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
自定义的学生类;
public class Student {
//属性;
private int stuID;
private String name;
private String sex;
private String phone;
private int graID;
private Date resTime;
private int score;
//get,set方法;
public int getStuID() {
return stuID;
}
public void setStuID(int stuID) {
this.stuID = stuID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public int getGraID() {
return graID;
}
public void setGraID(int graID) {
this.graID = graID;
}
public Date getResTime() {
return resTime;
}
public void setResTime(Date resTime) {
this.resTime = resTime;
}
public int getScore() {
return score;
}
public void setScore(int score) {
小知识点记录:JDBC的额外知识点