JDBC总结

JDBC总结

概述

JDBC是一组能够执行SQL语句的API
1.    连接数据库的步骤:
1)       注册驱动  
    JDBC类库向DriverManager注册数据库驱动·
2)       建立连接

    使用DriverManager提供的getConnection()方法连接到数据库
3)       创建语句

    通过数据库的连接对象的createStatement方法建立SQL语句对象
4)       执行语句

    执行SQL语句,并将结果集合返回到ResultSet中
5)       处理结果

    使用while循环读取结果
6)       释放资源(注意关闭的顺序)

demo

 DriverManager.registerDriver(newcom.mysql.jdbc.Driver());

      

       System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver");

          

        //加载驱动方式

        Class.forName("com.mysql.jdbc.Driver");//推荐方式

       //2.建立连接

        String url = "jdbc:mysql:localhost:3306/jdbc";

        String user = "root";

        String password = "mysql";

        Connection conn = DriverManager.getConnection(url,user,password);

      

       //3.创建语句

       Statement st = conn.createStatement();

      

       //4.执行语句

       ResultSet rs = st.executeQuery("select * from user");

       //5.处理结果

       while(rs.next()){//按行来遍历

        System.out.println(rs.getObject(1) + "\t" + rs.getObject(2) + "\t" +rs.getObject(3) + "\t" + rs.getObject(4)        );

       }

       //6.释放资源(注意关闭的顺序)

       rs.close();

       st.close();

       conn.close();

JDBC的使用方法

1.建立连接:

建立连接分为两个关键步骤:①加载驱动程序;②建立连接。此处所涉及到的Java类有:ClassDriverDriverManager

建立连接,要提供3个必不可少的参数:①数据库名称;②数据库账号;③数据库密码。

关键代码:

① 加载驱动程序:Class.forName(com.mysql.jdbc.Driver);

② 建立连接:

Connection conn=DriverManager.getConnection(url,name,pwd);

此处,String url=”jdbc:mysql://localhost:3306/”+数据库名称

String name=数据库账号

 String pwd=数据库密码

需要说明的是,这两句关键代码所用的方法都会抛出异常,故应将这两句放在try-catch块里

 

import java.sql.*;

public class TestConnection{

static {

try{

//加载驱动程序

Class.forName(com.mysql.jdbc.Driver);

}

catch(Exeception e){

e.printStackTrace();

}

}

 

public static void main(String[] args){

String url=”jdbc:mysql://localhost:3306/lt_atm”;

String name=”admin”;

String pwd=”lt11235”;

Connection conn=null;

try{

//建立数据库连接

conn=DriverManager.getConnection(url,name,pwd);

}

catch(SQLExeception e){

e.printStackTrace();

}

finally{

try{

conn.close();

}

catch(SQLException e){

   e.printStackTrace();

}

 

}

}

}

一般,我们在静态代码块里加载驱动程序,不需要每次调用时都重新加载一次驱动。

建立连接过程——流程图总结:

 

2.执行SQL命令:

①在建立起来的连接线上创建Statement对象;②用新建的Statement对象传送SQL命令;③接收Statement对象返回过来的结果。

管理系统将所接受的SQL命令分为两类:①写,具体来说就是增删改;②读,即查。对于写性质的SQL命令,管理系统会将受到影响的记录条数交给信使Statement;对于读性质的SQL命令,管理系统会将满足条件的所有记录封装在一个对象里交给信使Statement,而这个对象就是ResultSet类的对象。现在我们来看一些这些动作的关键代码:

 

增删改:stmt.executeUpdate(sql)返回修改的记录条数

查:stmt.executeQuery返回结果对象

 

关键代码:

创建信使

Statement stmt=conn.createStatement();

传送SQL命令&接收返回结果

添加记录:        

String sql=”INSERT INTO account VALUES (‘1132’,’金龙’,’’,10000) ”;

int count=stmt.executeUpdate(sql);

 

修改记录:

String sql=”UPDATE account SET name=’无花’ WHERE accId=’1132’”;

int count=stmt.executeUpdate(sql);

 

删除记录:

String sql=”DELETE FROM account WHERE accId=’1132’”;

int count=stmt.executeUpdate(sql);

读性质的命令:

查询记录:

String sql=”SELECT * FROM account”;

ResultSet rs=stmt.executeQuery(sql);

 

接下来,我们以具体的程序例子来说明传送SQL命令的过程;再以流程图来总结该过程:

传送SQL命令过程——代码示例:

import java.sql.*;

public class TestExecute{

static {

try{

//加载驱动程序

Class.forName(com.mysql.jdbc.Driver);

System.out.println(Success loading Driver!);

}

catch(Exeception e){

e.printStackTrace();

}

}

 

public static void main(String[] args){

String url=”jdbc:mysql://localhost:3306/lt_atm”;

String name=”admin”;

String pwd=”lt11235”;

Connection conn=null;

try{

//建立数据库连接

conn=DriverManager.getConnection(url,name,pwd);

Statement stmt=conn.createStatement();

//添加记录的SQL命令

String sql_1=INSERT INTO account VALUES

(1132,金龙,,10000) ;

//修改记录的SQL命令

String sql_2=UPDATE account SET name=无花’ 

WHERE accId=1132’”;

//删除记录的SQL命令

String sql_3=DELETE FROM account WHERE accId=1132’”;

 

//查询记录的SQL命令

String sql_4=SELECT * FROM account;

 

//传送SQL命令

int count_1=stmt.executeUpdate(sql_1);

int count_2= stmt.executeUpdate(sql_2);

int count_3= stmt.executeUpdate(sql_3);

ResultSet rs=stmt.executeQuery(sql_4);

 

}

catch(SQLExeception e){

e.printStackTrace();

}

finally{

try{

conn.close();

}

catch(SQLException e){

   e.printStackTrace();

}

}

}

}

 

传送SQL命令过程——流程图总结:

 

3.从返回的对象结果集中读取数据:

当是查找数据时,返回的是结果对象集合。

如何从返回的ResultSet对象中读取数据

ResultSet对象中,除了每一条记录各占用一个位置,还有一些特殊作用的位置,其中有两个分别是:beforeFirstafterLast。他们分别是第一条记录的前一个位置、最后一条记录的下一个位置。当我们用ResultSet对象封装了查询到的记录,游标初始位置置为beforeFirst

ResultSet类中有一个next()方法,其作用就是移动游标,使其指向下一个位置。该方法的返回值是一个boolean类型变量,当移动游标后能指向一个有效记录,返回true,否则(比如说移动到了afterLast位置)返回falseResultSet类还有一系列方法,可以统一写成getXXX()。其作用是从游标指向的记录中读取数据。其中XXXStringIntDouble类的变量类型,取决于所要读取的字段的数据类型。

思路:①用得到的ResultSet对象调用next()方法,若返回true转向②,否则转向③;②调用getXXX方法,从游标当前指向的记录中读取各个字段的数值;然后调用next方法,若返回true,转向②,否则转向③。③读取过程完成,正常结束。

读取数据的过程——代码示例:

import java.sql.*;

public class TestExecute{

static {

try{

//加载驱动程序

Class.forName(com.mysql.jdbc.Driver);

System.out.println(Success loading Driver!);

}

catch(Exeception e){

e.printStackTrace();

}

}

public static void main(String[] args){

String url=”jdbc:mysql://localhost:3306/lt_atm”;

String name=”admin”;

String pwd=”lt11235”;

Connection conn=null;

try{

//建立数据库连接

conn=DriverManager.getConnection(url,name,pwd);

System.out.println(Success eastablishing the Connection…”);

Statement stmt=conn.createStatement();

 

//查询记录的SQL命令

String sql=SELECT * FROM account;

//传送SQL命令

ResultSet rs=stmt.executeQuery(sql);

//读取数据

while(rs.next()){

String accId=rs.getString(1); //或者 rs.getString(accId);

String name=rs.getString(2);//或者 rs.getString(name);

Char sex=rs.getChar(3);//或者 rs.getChar(sex);

int count=rs.getInt(4);//或者 rs.getInt(count);

System.out.println(accId+/t+name+/t+sex+/t+count);

}

}

catch(SQLExeception e){

e.printStackTrace();

}

finally{

try{

conn.close();

}

catch(SQLException e){

   e.printStackTrace();

}

}

}

}

读取数据的过程——流程图总结:

 

二 动态执行SQL命令

动态SQL程序运行时根据条件执行具体的内容。

1、条件动态化:

         编写一个方法,他从外界接收一个参数,并用这个参数使方法内部的SQL条件具体化,然后return一个结果。具体代码如下:

public static ResultSet sqlQuery(String accName){

String sql=SELECT * FROM account WHERE name=’”+ accName +”’”;

.............................

return ResultSet

}  

 

 

2、字段动态化:

  承上所述,此处所谓的字段动态化,就是我们不仅能按姓名来查询记录,还能根据其他字段来查询:比如说根据编号,根据身份,甚至是根据性别。在上文sqlQuery方法的基础上继续扩展,其代码如下:

 

public static ResultSet sqlQuery(String column,String value){

String sql=SELECT * FROM account WHERE

’”column +”’=’”value +”’”;

.............................

return ResultSet

}

 

 

   3、数据表动态化:

 目标是使他能不受具体数据表的限制传入一个数据表参数,而是根据我们不同的需要,访问不同的数据表。代码:

 

     public static ResultSet sqlQuery(String table,String column,String value){

String sql=SELECT * FROM ’”+ table +”’ WHERE

’”column +”’=’”value +”’”;

.............................

.............................

return ResultSet

}

4PreparedStatement类的介绍:

PreparedStatement是继承自Statement的类

PreparedStatement拥有Statement所有的特性和方法。但是与Statement不同在于PreparedStatement可以将SQL命令事先编译,并存储在PreparedStatement对象中,当需要执行多次相似的SQL命令时,能够比较高效地执行。

另外,PreparedStatement最主要的功能就是能够输入条件式的SQL命令   

     import java.sql.*;

public class VisitDB{

static {

try{

//加载驱动程序

Class.forName(com.mysql.jdbc.Driver);

}

catch(Exeception e){

e.printStackTrace();

}

}

//负责建立与数据库管理系统之间的连接

public static Connection getCon(){

String url=”jdbc:mysql://localhost:3306/lt_atm”;

String name=”admin”;

String pwd=”lt11235”;

Connection conn=null;

try{

//建立数据库连接

conn=DriverManager.getConnection(url,name,pwd);

   }

catch(SQLExeception e){

    conn=null;

e.printStackTrace();

}

      return conn;

}

//负责动态执行查询语句

public static ResultSet sqlQuery(String table,String column,String value){

Connection conn= VisitDB.getCon();

//条件式SQL命令,?表示待定参数

String sql=SELECT * FROM ? WHERE ?=?;

try{

//创建pstmt时就将sql命令传过去,使其预先编译

PreparedStatement pstmt=conn.prepareStatement(sql);

//表示第1个?的值为String类型的table

pstmt.setString(1,table);

    //表示第2个?的值为String类型的column

pstmt.setString(2, column);

//表示第3个?的值为String类型的value

pstmt.setString(3, value);

//pstmt对象执行将编译好的SQL命令传过去,并接收结果

ResultSet rs=pstmt.executeQuery();

return rs;

}

catch(Exception e){

return null;

e.printStackTrace();

}

}

//负责关闭不再用的对象,释放系统资源

public static void close(ReaultSet rs,Statement stmt,Connection conn){

try{

rs.close();

stmt.close();

conn.close();

}

catch(Exception e){

e.printStackTrace();

}

}

}

    

  好了,此时我们的三个动态扩展也用PreparedStatement实现了。不得不提的是PreparedStatement类的setXXX系列的方法并不是只有setString一种,只是上例中我们只用到了这一种而已

三 批处理SQL命令

1Statement的批处理功能

        Statement提供了批处理的功能,批处理多用在执行写操作的SQL命令时,如果一次要增删改很多条记录,使用批处理会减少对象在程序与数据库系统之间的交互,从而提高其性能。批处理的流程如下所示:

方法说明:

1public void clearBatch() throws SQLException

 作用:将Statement对象的Batch中的所有SQL命令清空

2public void addBatch(String sql) throws SQLException

 作用:插入一个SQL命令到Statemen对象中的Batch

3public int[] executeBatch() throws SQLException

作用:将Statement对象的Batch中的所有SQL命令传给数据库系统

代码示例:

Import java.sql.*;

public class TestBatch{

String[] sqls={INSERT INTO account VALUES(13579’ ’云飞’ ‘男’ , 100),

DELETE FROM account WHERE name=’无花’”,

UPDATE account SET name=’铁手’ WHERE accId=2468’”,}

public void testBatch(){

Connection conn=null;

try{

conn= VisitDB.getCon();

Statement stmt=conn.createStatement();

stmt.clearBatch();

for(int i=0;i<sqls.length;i++){

stmt.addBatch(sqls[i]);

}

stmt.executeBatch();

}

catch(Exception e){

e.printStackTrace();

}

 

}

}

 

2PreparedStatement的批处理功能:

    PreparedStatementStatement的子类,所以他也有批处理功能,其流程如下所示:

图中所涉及到的方法都已说过,就不再赘述了。现在我们直接来看一下代码示例:

public void testPreBatch(){

try{

Connection conn= VisitDB.getCon();

PreparedStatement  pstmt=conn.prepareStatement(

DELETE FROM account WHERE name=?);

pstmt.clearBatch();

pstmt.setString(金龙);

pstmt.addBatch();

pstmt.setString(无花);

pstmt.addBatch();

pstmt.setString(楚香帅)

pstmt.addBatch();

 

pstmt.executeBatch();

}

catch(Exception e){

e.printStachTrace();

}

}

四 事务机制

事务最主要的功能是用来确保多个连续的数据库操作,能作为一个整体被对待。即在执行时,要么全部执行成功,否则全部执行失败——回到最初状态。

流程说明:

1Connection对象调用setAutoCommit()方法,将AutoCommit设置为false,以取消自动提交事务机制(自动提交事务机制将每一条SQL命令看做是一个事务,每做一次数据库操作,会自动提交一次)。

    2、当一个逻辑事务所包含的SQL命令都被执行后,调用commit()方法,向数据库系统提交事务;这条语句通常是try块里最后一句。

3、中途如果发生任何错误或不明原因导致操作中断,调用rollback()方法,要求数据库系统执行Rollback操作。

 

方法说明:

public void setAutoCommit(boolean autoCommit) throws SQLException

    说明:设置是否自动提交事务。当true时,每执行一条SQL命令,数据库系统自动提交一次事务,即对数据库里的数据进行操作;当为false时,数据库系统不自动提交事务,知道遇到调用的commit()方法。

    

public boolean getAutoCommit() throws SQLException

说明:获得当前的Commit方式

 

public void commit() throws SQLException

说明:指示数据库系统提交事务,将结果写进数据库

 

public void rollback() throws SQLException

说明:指示数据库执行恢复操作,将数据恢复到最初状态

 

流程图:

代码示例:

public void testTransaction(){

try{

Connection conn= VisitDB.getCon();

conn.setAutoCommit(false);

Statement stmt=con.createStatement();

String sql_1=UPDATE account SET balance=balance-1000 

WHERE accId=A’”;

String sql_1=UPDATE account SET balance=balance+1000 

WHERE accId=B’”;

conn.commit();

}

catch(SQLException ex){

ex.printStackTrace();

try{

conn.rollback();

}

catch(SQLException e){

e.printStackTrace();

}

}

 

}

 

SQL注入PreparedStatementStatement 

PreparedStatement(从Statement扩展而来)相对Statement的优点:

1、没有SQL注入的问题。

2Statement会使数据库频繁编译SQL,可能会造成数据库缓冲区溢出。

3、数据库和驱动可以对PreparedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效)。

 

为什么要始终使用PreparedStatement代替Statement?

代码可维护性

辨别: Statement使用 +  PreparedStatement使用?)
stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values       ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");

注意StatementPreparedStatement语句上的区别:Statement是在执行的时候才传入SQL语句,而PreparedStatement在创建的时候就已经传入了SQL语句。
perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();

 

防止SQL注入
对于JDBC而言,SQL注入攻击只对Statement有效,PreparedStatement是无效的,这是因为PreparedStatement不允许在插入时改变查询的逻辑结构.

SQL注射原理

SQL 注射能使攻击者绕过认证机制,完全控制远程服务器上的数据库

登陆验证

假如后台的sql语句时这样拼接的

select id from test where username='"+myname+"' and password='"+mypasswd+"' ";

表面上看,如果用户名和口令对匹配,那么该用户通过认证;否则,该用户不会通过认证——但是,事实果真如此吗?这里并没有对SQL命令进行设防,所以攻击者完全能够在用户名或者口令字段中注入SQL语句,从而改变SQL查询 。为此,我们仔细研究一下上面的SQL查询字符串:

上述代码认为字符串username和password都是数据,不过,攻击者却可以随心所欲地输入任何字符 。如果一位攻击者输入的用户名为

‘’OR 1=1—

而口令为

x

双划符号--告诉SQL解析器,右边的东西全部是注释,所以不必理会。这样,查询字符串相当于:

select id from test where username='' or 1=1;

因为现在只要用户名为长度为零的字符串''或1=1这两个条件中一个为真,就返回用户标识符ID——我们知 道,1=1是恒为真的。所以这个语句将返回user_table中的所有ID。在此种情况下,攻击者在username字段放入的是SQL指令 'OR1=1--而非数据。

更为严重的情况是当username对应的是'OR1=1;DROPTABLEuser_table;--

数据库中执行的sql语句就变成了:

select id from test where username='' or 1=1;drop table test

这个语句将执行句法上完全正确的SELECT语句,并利用drop命令清空test表。

应对策略

问题的关键就是不要用string构造sql语句,这样就不会利用输入的参数构造sql语句了。所以要用PreparedStatement替换Statement,即用占位符作为实参定义sql语句,从而避免sql注入攻击。

不管什么框架,还是纯JDBC,只用Preparedstatement,一定要用占位符作为实参来构造sql(或hql)语句。

String sql= "select * from test where usernmae=? and password=? " ;
PreparedStatement psm=conn.preparedStatement(sql);
psm.setString(1,myname);
psm.setString(2,mypasswd);
Result rs=psm.executeQuery(); 
if (rs.next){
rs.close();
con.close();
return false ;
}
else {
rs.close();
con.close();
return true ;
}

 

 





posted @ 2015-05-06 21:43  白大虾  阅读(1601)  评论(0编辑  收藏  举报