目录
1.JDBC
Java程序在进行与数据库连接时通常需要通过JDBC来实现。JDBC的全称是Java Data Base Connectivity(Java数据库连接),主要由接口组成,是一种用于执行Sql语句的Java API。各个不同的数据库厂家基于JDBC实现了各自的驱动程序(Driver)。 Java程序在获取数据库连接时,需要以URL方式指定不同类型的数据库Driver,在获取特定的Connection连接后,可按照JDBC规范对不同类型的数据库进行数据操作,代码如下所示:
//第一步,注册驱动程序
Class.forName("数据库驱动完整名");
//第二步,获取一个数据库的连接
Connection conn = DriverManager.getConnection("数据库地址","用户名","密码");
//第三步,创建一个会话
Statement stmt=conn.createStatement();
//第四步,执行SQL语句
stmt.executeUpdate("SQL语句");
//或者查询记录
ResultSet rs= stmt.executeQuery("查询记录的SQL语句");
//第五步对查询结果进行处理
while(rs.next()){
//操作
}
//第六步,关闭连接
rs.close();
stmt.close();
conn.close();
1.1JDBC核心接口介绍
1.1.1Driver
JDBC接口是驱动类必须要实现的接口。
1.1.2Connection
Conoection接口的主要作用是连接数据库。
1.1.3Statement
Statement接口负责封装和发送SQL语句。
1.1.4ResultSet
当使用Statement接口发送查询的SQL语句后,我们需要获得查询结果的返回值。ResultSet接口的功能是封装返回值得结果,它的数据组织形式类似于二维表格,在二维表格中查询得到的数据。
1.1.5PreparedStatement
Statement接口提供了执行SQL语句的功能,但在执行效率和SQL语句运行的安全性上,不能满足需求。因此,出现了子接口PreparedStatement。
预编译是在执行SQL语句之前就对SQL语句进行分析、校验和优化,只执行一次预编译,后面可以复用多次预编译的对象,提升程序运行的效率。
1.2JDBC的使用
1.2.1创建Driver对象
使用JDBC操作数据库的第一步是注册驱动,在注册驱动之前需要先创建Driver对象,示例代码如下所示:
运行结果:
1.2.2创建Connnection对象
想要获得Connection对象,需要使用Driver类的connect(String url,java.util.Properties info)方法。
在使用connect方法时,需要先在Properties对象中存储user和password信息,然后再传递给connect方法。如下所示:
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws SQLException {
String url="jdbc:mysql://localhost:3306/nocv?serverTimezone=Asia/Shanghai";
String username="root";
String password="123456";
Properties prop=new Properties();
prop.setProperty("user",username);
prop.setProperty("password" ,password);
Driver driver=new Driver();
Connection connection=driver.connect(url,prop);
System.out.println(driver);
System.out.println(connection);
connection.close();
}
}
运行结果:
使用完毕后的Connection对象一定要调用close方法关闭连接,不然就会像占着窗口不买票一样,会导致其他人无法获得Connection对象,因为可用的连接数是有限的。
1.2.3创建Statement对象
先使用Connection创建Statement对象,然后使用Statement对象发送SQL语句,进行数据库操作。示例代码如下:
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws SQLException {
String url="jdbc:mysql://localhost:3306/nocv?serverTimezone=Asia/Shanghai";
String username="root";
String password="123456";
Properties prop=new Properties();
prop.setProperty("user",username);
prop.setProperty("password" ,password);
Driver driver=new Driver();
Connection connection=driver.connect(url,prop);
Statement statement=connection.createStatement();
statement.executeUpdate("insert into nocv_data(id,name,value) values(35,'纽约','1000') ");
statement.close();
connection.close();
}
}
运行结果:
1.2.4创建ResultSet对象
查询数据时一般会通过ResultSet遍历查询到的结果集,示例代码如下图所示:
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws SQLException {
String url="jdbc:mysql://localhost:3306/nocv?serverTimezone=Asia/Shanghai";
String username="root";
String password="123456";
Properties prop=new Properties();
prop.setProperty("user",username);
prop.setProperty("password" ,password);
Driver driver=new Driver();
Connection connection=driver.connect(url,prop);
Statement statement=connection.createStatement();
ResultSet resultSet=statement.executeQuery("select * from nocv_data");
while (resultSet.next()){
long id=resultSet.getLong("id");
String name=resultSet.getString("name");
int value=resultSet.getInt("value");
System.out.println(id+" "+ name+" "+value);
}
resultSet.close();
statement.close();
connection.close();
}
}
运行结果:
Result(结果集)封装了一个从数据表中查询数据记录的二维表,在表中存储从数据库中查询到的结果,再使用while语句进行遍历。
1.3使用Statement接口的弊端
用于测试的数据表如下图所示:
查询操作是登录功能实现的原理,下面登录功能的测试代码;
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws SQLException {
boolean isLogin=false;
String loginUserName="joe";
String loginPassword="1234";
String url="jdbc:mysql://localhost:3306/studeng?serverTimezone=Asia/Shanghai";
String username="root";
String password="123456";
Properties prop=new Properties();
prop.setProperty("user",username);
prop.setProperty("password" ,password);
Driver driver=new Driver();
Connection connection=driver.connect(url,prop);
Statement statement=connection.createStatement();
ResultSet resultSet=statement.executeQuery("select * from keyo where name='"+loginUserName+"'and password='"+loginPassword+"'");
while (resultSet.next()){
isLogin=true;
}
resultSet.close();
statement.close();
connection.close();
System.out.println("登录是否成功:"+isLogin);
}
}
运行结果:
改变一下代码的内容(输入一个错误的密码):
值改变后的运行结果:
虽然登录失败了,但上面的代码可以在不知道账号和密码的前提下实现成功登录,更改代码如下:
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws SQLException {
boolean isLogin=false;
String loginUserName="任意内容";//随便写的任意内容
String loginPassword="任意内容' or '1' = '1" ;//随便写的任意内容
String url="jdbc:mysql://localhost:3306/studeng?serverTimezone=Asia/Shanghai";
String username="root";
String password="123456";
Properties prop=new Properties();
prop.setProperty("user",username);
prop.setProperty("password" ,password);
Driver driver=new Driver();
Connection connection=driver.connect(url,prop);
Statement statement=connection.createStatement();
String sql="select * from keyo where name='"+loginUserName+"'and password='"+loginPassword+"'";
System.out.println("Sql:"+sql);
ResultSet resultSet=statement.executeQuery(sql);
while (resultSet.next()){
isLogin=true;
}
resultSet.close();
statement.close();
connection.close();
System.out.println("登录是否成功:"+isLogin);
}
}
运行结果:
可以看到,再次运行程序后,在没有正确账号和密码的情况下,居然成功登陆了。这种行为是SQL注入,因为Ststement接口使用的SQL语句是拼接的。
1.3.1PreparedStatement接口
解决上述问题的方法是使用PreparedStatement接口,它是Statement的子接口。
代码如下所示:
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws SQLException {
boolean isLogin=false;
String loginUserName="任意内容";//随便写的任意内容
String loginPassword="任意内容' or '1' = '1" ;//随便写的任意内容
String url="jdbc:mysql://localhost:3306/studeng?serverTimezone=Asia/Shanghai";
String username="root";
String password="123456";
Properties prop=new Properties();
prop.setProperty("user",username);
prop.setProperty("password" ,password);
Driver driver=new Driver();
Connection connection=driver.connect(url,prop);
String sql="select * from keyo where name=?and password=?";
PreparedStatement ps=connection.prepareStatement(sql);
ps.setString(1,loginUserName);//第一个问号传值
ps.setString(2,loginPassword);//第二个问号传值
System.out.println("Sql:"+sql);
ResultSet resultSet=ps.executeQuery();
while (resultSet.next()){
isLogin=true;
}
resultSet.close();
ps.close();
connection.close();
System.out.println("登录是否成功:"+isLogin);
}
}
运行结果:
需要注意的是,使用PreparedStatement接口后,方法executeQuery()就不需要传入SQL语句了,这时是通过connection.prepareStatement(sql)方法传入的。
与Statement接口相比,PreparedStatement接口有如下三大优势:
- 防止SQL注入,提高软件系统的安全性。
- 方便开发,使用问号占位的方式传入参数值,替代了拼接字符串的方式。
- 使用预编译机制提高程序的运行效率。