在上一篇博客中我们就使用了jdbc ,它可以连接数据库,并用java语言操作数据库,但是我们只是用它做了一下事务的演示,在这篇博客中我们来详细的谈谈JDBC;
定义:
Java database connectivity 的缩写,即Java数据库连接接口;
是java语言操作数据库的 api (应用程序编程接口) ,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC常用接口:
Class.forName("驱动类名");
例如:
Class.forName("com.mysql.jdbc.Driver");
Class.forName("oracle.jdbc.driver.OracleDriver");
注:jdbc 3.0 以上版本都可以省略加载驱动这一步;
Connection conn =
DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
常用方法:
createStatement():创建向数据库发送sql的statement对象。
prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit() :在链接上提交事务。
rollback() :在此链接上回滚事务。
Statement 接口:
这个语句用来执行静态SQL语句并返回它所生成结果的对象。
可以用来执行 insert, update, delete , select . 语句
两个Statement 常用类:
(1). Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
(2). PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
四个常用Statement方法:
(1). executeQuery(String sql):运行select语句,返回ResultSet结果集。
(2). executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
(3). addBatch(String sql) :把多条sql语句放到一个批处理中。
(4). executeBatch():向数据库发送一批sql语句执行。
ResultSet 接口
结果集 代表的是查询的结果;
resultSet是我们运行了查询语句之后,获得的结果集合;它还提供了对结果集进行滚动的方法;
ResultSet.next(); 向下滚动一条记录;
ResultSet.Previous(); 向前滚动一条记录;
同时我们需要用特定的get方法检索不同类型字段的方法;
resultSet.getString(int index/String columnName); 获得数据库里是varchar、char等类型的数据对象;
这里注意它的参数,可以是index 即数据表中的列号,或者也可以是直接明确的columnName 即列名,确保获取到对应想要的列的数据;
resultSet.getInt(int index/String columnName); 获得数据库里是 int 类型的数据对象;
…
…
这里我们省略了加载驱动的步骤,在jdk 3,以上加载驱动的过程都可以省略;
DriverManager.getConnection(url, 用户名, 密码);
// 内部调用了 Driver 对象获取数据库连接
url 的格式 :
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库;
jdbc:mysql://ip地址:端口号/数据库名?参数
这里的参数我们后面会有解释;
例如:mysql数据库
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
Statement stmt = conn.createStatement();
int rows = stmt.executeUpdate();
// 用来执行 insert , update, delete , DDL , 返回值代表影响行数
ResultSet rs = stmt.executeQuery(); // 用来执行 select
//boolean moreRows = rs.next() 取得下一条记录,
while(rs.next()) {
System.out.println(rs.getString(1))//获取第一行第一列的值;
}
从上面我们看到,executeQuery()得到的结果集调用next()方法,它的返回值是boolean类型,因此我们为了获得所有结果集中的数据,可以用while()方法,给传入的条件就是rs.next(),如果 是true ,表示有下一条记录, false 表示没有了,这样确保我们获取到所有查询到的数据;
resultSet.close();
stat.close();
conn.close();
例子:
我们现在数据库中有一张student表,然后我们可以用JDBC对他进行一系列操作;
我们先写一个学生类:
import java.sql.Date;
public class Student {
private int sid;
private String sname;
private String birthday;
private String sex;
public Student(int sid, String sname, String birthday, String sex) {
this.sid = sid;
this.sname = sname;
this.birthday = birthday;
this.sex = sex;
}
public Student() {
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", birthday='" + birthday + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
比如我们现在要先删除数据表中名叫 “ 李四 ”的学生的信息,然后查询获取这张学生表中的所有数据;
private static List<Student> sqlqueryaction(Statement stat) throws SQLException {
ResultSet resultSet = stat.executeQuery("select * from student");
List<Student> list = new ArrayList<>();
while(resultSet.next()){
Student stu = new Student(resultSet.getInt("sid"),
resultSet.getString("sname"),
resultSet.getString("birthday"),
resultSet.getString("sex"));
list.add(stu);
}
return list;
}
然后我们将结果集中的数据分别获取,当作Student构造类的参数,得到Student类的对象,将对象存入一个集合中,以便我们对获取到的数据进行操作;
public class jdbcutils {
static final String url ="jdbc:mysql://localhost:3306/test";
static final String username = "root";
static final String pass = "123456";
public static Connection getconnection() throws SQLException {
return DriverManager.getConnection(url,username,pass);
}
}
public static void main(String[] args) {
try(Connection conn = jdbcutils.getconnection()){
try (Statement stat = conn.createStatement()){
int lows =
stat.executeUpdate("delete from student where sname = '李四'");
System.out.println("影响行数为:"+lows);
List<Student> stu = sqlqueryaction(stat);
stu.forEach(s -> System.out.println(s));
}
} catch (SQLException e) {
e.printStackTrace();
}
这里我们将获取连接的代码写成一个工具类,每次直接调用该工具类的getconnection()方法,直接获取一个连接对象;同时我们也用了try-with-resources方法,省去了释放资源的操作;这个例子也只是将获取到的结果进行了输出操作,其实我们返回了student类的数据集合,完全可以再对其进一步操作,这里我们演示就只将它输出吧;
结果如上图;
PreparedStatement 能够实现sql语句的预编译,同时它可以动态的传入参数,而不像Statement那样每次传入一个固定的sql语句,这样的固定sql语句表示它只能完成特定的sql操作,而PreparedStatement 可以用 ?来占位某个参数,你动态的传入参数,就可以实现不同效果的操作了;
PreparedStatement psmt = conn.prepareStatement(String sql);
insert into student(sid,sname,birthday,sex) values(null, ?, ?, ?)
//使用PreparedStatement中一系列以 set开头的方法
//setString(?的位置, 值)
//setInt(?的位置, 值)
//setDate(?的位置, 值)
psmt.setString(1, "李四");
psmt.setString(2, "1999-10-7");
psmt.setString(3,"男");
psmt.executeUpdate();
注意: ?能够占位的只有值, 不能是表名、列名、关键字
resultSet.close();
stat.close();
conn.close();
上面的操作就是我们将之前删除的李四学生重新插入回表中;
try(Connection conn = jdbcutils.getconnection()){
PreparedStatement psmt =
conn.prepareStatement("insert into student(sname,birthday,sex) values(?,?,?)");
psmt.setString(1,"李四");
psmt.setString(2,"1999-10-7");
psmt.setString(3,"男");
int low = psmt.executeUpdate();
System.out.println("数据写入完成,影响行数为:"+low);
try(Statement stat = conn.createStatement()){
List<Student> stu = sqlqueryaction(stat);
stu.forEach(s -> System.out.println(s));
}
} catch (SQLException e) {
e.printStackTrace();
}
结果如图,我们看到李四学生已经添入student表;
其实我们不光可以添加李四学生,我们动态的传入别的学生信息,就可以实现信息的写入,这样就灵活多了;
所以这种机制使得我们做相似的sql操作时能尽大可能的提高sql的执行效率;
SQL注入攻击:
由于jdbc中执行的SQL语句是拼接出来的,其中有一部分内容是由用户从客户端传入,所以当用户传入的数据中包含sql关键字时,就有可能通过这些关键字改变sql语句的语义,从而执行一些特殊的操作,这样的攻击方式就叫做sql注入攻击;
例如:
String sql = “select * from tb_name where name= '”+varname+"’ and passwd=’"+varpasswd+"’";
如果我们把[’ or ‘1’ = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?
select * from tb_name = ‘随意’ and passwd = ‘’ or ‘1’ = ‘1’;
因为’1’='1’肯定成立,所以可以任何通过验证.
这就是应为用户恶意的传参,传入sql 的关键字,使得原本的语义改变了;
那我们应该如何防范sql注入呢?