jdbc(java database connection)是java内部集成的一套负责与database交互api。用以在java端执行增删查找数据库数据的操作。
这一套操作为:
1.准备好数据库
先准备操作的数据库表为选课表。表结构如下:
2.连接数据库
- 为项目引用Jar包
- 注册Driver到DriverManager里面
上面的代码中有加载类Class.forName("com.mysql.jdbc.Driver")的描述。
查看com.mysql.jdbc.Driver源码如下:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// ~ Static fields/initializers
// ---------------------------------------------
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
// ~ Constructors
// -----------------------------------------------------------
/**
* Construct a new driver and register it with DriverManager
*
* @throws SQLException
* if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
可知,在加载com.mysql.jdbc.Driver的时候,已经生成了一个Driver实例,并将其注册到DriverManager里面了。
3.执行insert的sql语句
查询Category表中数据为:
可见insert的sql语句起了作用。
上述代码中:
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/tmall?characterEncoding=UTF-8", "root", "admin");
用以连接tmall数据库
Statement s = c.createStatement();
String sql = "insert into Category values(85," +"'ruff开发板')";
s.execute(sql);
用以执行SQL语句,向Category表中插入数据。
4.执行select语句,查询表中数据
其中
while(mResultSet.next()){
System.out.println("Category表中id > 80的产品名称为:" + mResultSet.getString(2));
}
取出结果集中,所有的第二列数据。其中ResultSet.next()方法的解释为:
Moves the cursor forward one row from its current position. A ResultSet cursor is initially positioned before the first row; the first call to the method next makes the first row the current row; the second call makes the second row the current row, and so on.
到此我们就基本上实现了JDBC的基本功能,可以开始业务开发了。
5.使用PreparedStatement实现数据库的操作。
public static void main(String args[]){
//new CategoryDao().executePreparedStatement();
//new CategoryDao().executeStatement("空调 or 1= 1");
//new CategoryDao().executePreparedStatement("空调 or 1= 1");
new CategoryDao().executePreparedStatement("空调");
}
private void executePreparedStatement(String userInput){
String sql = "select * from Category where name = ?";
try{
Connection c = getConnection();
PreparedStatement ps = c.prepareStatement(sql);
ps.setString(1, userInput);
ps.execute();
ResultSet rs = ps.getResultSet();
while(rs.next()){
System.out.printf("%d,%s",rs.getInt(1),rs.getString(2));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
上面的函数执行了从Category数据库中查找id=80的项目并将name打印出来的功能,执行结果如下:
Console界面将name=空调 的条目打印出来了。
5.使用PreparedStatement与使用Statement的对比。
上述示例代码中的PreparedStatement.setString(1, userInput);是值往sql第一个参数传值,然而Statement中sql是通过字符串拼接得到的。下面的代码对比表示了其区别:
public static void main(String args[]){
//new CategoryDao().executePreparedStatement();
new CategoryDao().executeStatement("75");
//new CategoryDao().executePreparedStatement("空调 or 1= 1");
//new CategoryDao().executePreparedStatement(75);
}
private void executeStatement(String userInput){
String sql = "select * from Category where id = " + userInput ;
try{
Connection c = getConnection();
//PreparedStatement ps = c.prepareStatement(sql);
//ps.setInt(1, 80);
//ps.execute();
Statement s = c.createStatement();
s.execute(sql);
ResultSet rs = s.getResultSet();
while(rs.next()){
System.out.printf("%d,%s",rs.getInt(1),rs.getString(2));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
得到的结果为:
由此可见Statement的sql语句是采用字符串拼接得到的。
因此PreparedStatement可以在两方面比Statement好:
(1)书写很麻烦 。尤其是当sql字段涉及到char, varchar类型的数据时,转义字符(\”)写的人脑袋都要绕晕了。
(2)很重要的一点,PreparedStatement可以防止sql注入。
public static void main(String args[]){
//new CategoryDao().executePreparedStatement();
new CategoryDao().executeStatement("75 or 1 = 1");
//new CategoryDao().executePreparedStatement("空调 or 1= 1");
//new CategoryDao().executePreparedStatement(75);
}
private void executeStatement(String userInput){
String sql = "select * from Category where id = " + userInput ;
try{
Connection c = getConnection();
//PreparedStatement ps = c.prepareStatement(sql);
//ps.setInt(1, 80);
//ps.execute();
Statement s = c.createStatement();
s.execute(sql);
ResultSet rs = s.getResultSet();
while(rs.next()){
System.out.printf("%d,%s",rs.getInt(1),rs.getString(2));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
注意这行:
new CategoryDao().executeStatement("75 or 1 = 1");
执行结果为:
结果得知,打印出了Category表中所有的信息,这就是sql注入导致的信息泄露了。而在PreparedStatement中就不会产生这个结果,因为 “75 or 1 = 1”这个参数在Category表中是查不出来东西的。
(3)还有一点重要的区别是执行效率的区别
使用 PreparedStatement 最重要的一点好处是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。为了减少数据库的负载,生产环境中德JDBC代码你应该总是使用PreparedStatement 。值得注意的一点是:为了获得性能上的优势,应该使用参数化sql查询而不是字符串追加的方式。