JDBC编程步骤与执行SQL语句的方式

(1)JDBC编程步骤:
第一步:加载数据库驱动。通常使用Class类的forName()静态方法来加载驱动:
//加载驱动
Class.forNamed(DriverClass);
其中DriverClass就是数据库驱动类所对应的字符串。例如,加载MySQL的驱动代码如下
//加载MySQL驱动
Class.forName(“com.mysql.jdbc.Driver”);

第二步:通过DriverManager获取数据库连接,DriverManager提供了如下方法:
//获取数据库连接
DriverManager.getConnection(String url,String user,String pass);

第三步:通过Connection对象创建Statement对象。Connection创建Statement的方法有三个:
**createStatement() 创建基本的Statement对象
**prepareStatement(String sql) 根据传入的sql语句创建预编译的Statement对象
**prepareCall(String sql) 根据传入的sql语句创建CallableStatement对象

使用Statement执行SQL语句。所有的Statement都有以下三个方法来执行SQL语句:
****excute() : 可以执行任何SQL语句,但是比较麻烦
****excuteUpdate() 主要用于执行DDL和DML语句,执行DML语句返回受SQL语句影响的行数,执行DDL语句返回0
****excuteQuary() 只能执行查询语句,执行后返回代表查询结果的ResultSet对象。

第五步:操作结果集
如果执行的SQL语句是查询语句,则执行结果将返回一个ResultSet对象,该对象里保存跑SQL语句的查询结果。程序可以通过操作该ResultSet对象来取出查询结果。ResultSet对象提供了如下两类方法:
**next() previous() first() last() beforeFirst() afterLast() absolute() 等移动记录指针的方法
**getXxx()方法获取记录指针指向行 特定列的值,该方法既可以使用列索引作为参数,也可以使用列名作为参数。使用列索引做参数的性能更好,使用列名的可读性更好。

第六步:回收数据库资源,包括关闭ResuleSet,Statement和Connection等资源。

简单的JDBC编程程序;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class ConnectionMysql {
     
     public static void main(String[]  args)
     throws Exception
     {

                //使用DriverManager获取数据库连接,其中返回的connection就代表Java金额数据库的连接
                Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
                //使用Connection来创建一个Statement对象
                Statement  stmt =conn.createStatement();
                //执行SQL语句

                ResultSet  rs=stmt.executeQuery("select  s. *,teacher_name"+"from  student_table  s,teacher_table t"
                +"where t.teacher_id=s.java_teacher");
                {
//ResultSet有一系列的getXxx()方法,用于获取记录指针指向特定行、列的值,不断的用next()方法将指针移动到下一行,如果移动之后记录指针依然
//指向有效行,则next()方法返回true
                     while(rs.next()){
                          System.out.println(rs.getInt(1)+"\t"+rs.getString(2)+"\t"+rs.getString(3)+"\t"+rs.getString(4));
                     }
                }


     }
}

//本程序可以使用自动关闭资源的try语句来关闭各种数据库资源,Java7改写了Connection Statement ResultSet等接口,他们都继承了AutoCloseable接口,因此他们都有可以用try语句来关闭。

————————————————————————————————————————
执行SQL语句的方式:JDBC不仅可以实现查询,也可以实现DDL、DML等语句。
(1)实例:下面程序使用excuteUpdate()方法(没有使用excuteLargeUpdate(),因为目前MySQL驱动还不支持),来创建数据表,该实例并没有把数据库连接信息写在程序里,而是使用一个mysql.ini文件(properties文件)来保存数据库连接信息,这是比较成熟的做法–当需要将应用程序从开发环境移到生产环境时,无需修改源代码,只需要修改mysql.ini文件即可。

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;
public class ExecuteDDL {
     
     String driver;
     String url;
     String user;
     String  pass;


     public void initParam(String  paramFile)
                throws Exception
     {
           //使用Propertics类来加载属性文件
           Properties  pro=new  Properties();
           pro.load(new FileInputStream(paramFile));

           driver=pro.getProperty(driver);
           url=pro.getProperty(url);
           user=pro.getProperty(user);
           pass=pro.getProperty(pass);

     }

     public void createTable(String  sql) 
     throws Exception{
           //加载驱动
           Class.forName(driver);
           //获取数据库连接
           Connection  conn=DriverManager.getConnection(url, user, pass);
           //使用Connection  来创建一个Statement对象
           Statement  st=conn.createStatement();
           {
                //执行DDL语句,创建数据表
                st.executeUpdate(sql);

           }
     }

     public static void main(String[]  args){
           ExecuteDDL   ed =new ExecuteDDL();
           try {
                ed.initParam("mysql.ini");
                ed.createTable("create table jdbc_test" + "(jdbc_id  int  auto_increment  primary,"
                +"jdbc_name  varchar(255),"
                           +"jdbc_desc   text);");
                System.out.println("建表成功");
           } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
           }

     }
}



使用excute()方法执行SQL语句:
(1)Statement的excute()几乎可以执行任何的SQL语句,但他执行SQL语句时比较麻烦,通常没有必要使用其来执行SQL语句,但是当不清楚SQL语句的类型,则只能使用excute()来执行SQL语句了。
(2)使用excute()方法执行查询语句返回值只能是boolean值,它表明该SQL语句是否返回了ResultSet对象。但是如何获取执行语句后得到的ResultSet对象呢?Statement提供了如下两个方法来获取执行结果:
***getResultSet() 获取该Statement执行查询语句所返回的ResultSet对象
***getUpdateCount() 获取该Statement()执行DML语句所影响的记录行数。

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import com.mysql.jdbc.ResultSetMetaData;
public class ExcuteSQL {
     String driver;
     String url;
     String user;
     String  pass;


     public void initParam(String  paramFile)
                throws Exception
     {
           //使用Propertics类来加载属性文件
           Properties  pro=new  Properties();
           pro.load(new FileInputStream(paramFile));

           driver=pro.getProperty(driver);
           url=pro.getProperty(url);
           user=pro.getProperty(user);
           pass=pro.getProperty(pass);

     }

     public void executeSql(String  sql) 
     throws Exception{
           //加载驱动
           Class.forName(driver);
           //获取数据库连接
           Connection  conn=DriverManager.getConnection(url, user, pass);
           //使用Connection  来创建一个Statement对象
           Statement  st=conn.createStatement();
           {
                //执行sql语句,返回boolean值表示是否包含ResultSet
                boolean  hasResultSet=st.execute(sql);
                //如果执行后有ResultSet结果集

                if(hasResultSet){
                     //获取结果集
                           ResultSet  rs=st.getResultSet();
                           {
                                //ResultSetMetaData是用于分析结果集的元数据入口
                                java.sql.ResultSetMetaData rsmd = rs.getMetaData();
                                int columnCount  =rsmd.getColumnCount();
                                //迭代输出ResultSet对象
                                while(rs.next()){
                                     //依次输出每列的值
                                     for(int i=0;i
                                          System.out.println(rs.getString(i+1)+"\t");
                                     }
                                     System.out.println("\n");
                                }
                           }
                }

                else{
                     System.out.println("该SQL语句影响的记录有"+st.getUpdateCount()+"条");
                }

           }
     }

     public static void main(String[]  args){
           ExcuteSQL   es =new ExcuteSQL();
           try {
                es.executeSql("drop  table  if  exists my_test");
                System.out.println("-----执行建表的sql---语句");

                es.executeSql("create table my_test"+"(test_id  int  auto_increment  primary_key,"
                +"test_name  varchar(255))");

                System.out.println("----执行插入数据的DML语句-----");
                es.executeSql("insert  into  my_test(test_name)"+"select  student_name from  student_table");
                System.out.println("---执行查询数据的查询语句----");
                es.executeSql("select  *  from  my_test");

           } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
           }

     }
}

使用PraparedStatement执行SQL语句:
(1)如果经常需要反复执行一条结构相似的SQL语句,如:
insert into student_table values(null,’zzz’,1);
insert into student_table values(null,’lll’,2);

JDBC提供了PreparedStatement接口,他是Statement的子接口,它可以预编译SQL语句,预编译后的SQL语句被存储在PreparedStatement
对象中,然后可以使用该对象多次高效的执行该语句。简而言之,使用PreparedStatement比Statement的效率更高。

(2)创建PreparedStatement对象使用Connection的preparedStatement()方法,该方法需要传入一个人SQL字符串,该SQL字符串可以包含占位符。
//创建一个PreparedStatement对象
pre =conn.preparedStatement(“insert into student_table values(null,?,1)”);

(3)PreparedStatement也提供了excute() excuteUpdate() excuteQuary()三个方法来执行SQL语句,不过这三个方法无需参数,因为PreparedStatement已存储了预编译的SQL语句。
(4)使用PreparedStatement预编译SQL语句时,该SQL语句可以带占位符参数,因此在执行SQL语句之前必须为这些参数传入参数值,PreparedStatement提供了一系列的setXxx(int index,Xxx value)方法来传入参数值。

(5)使用Statement和PreparedStatement插入100条记录的对比:

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.Properties;
public class PreparedStatementTest {
     

     String driver;
     String url;
     String user;
     String  pass;


     public void initParam(String  paramFile)
                throws Exception
     {
           //使用Propertics类来加载属性文件
           Properties  pro=new  Properties();
           pro.load(new FileInputStream(paramFile));

           driver=pro.getProperty(driver);
           url=pro.getProperty(url);
           user=pro.getProperty(user);
           pass=pro.getProperty(pass);
           //加载驱动
           Class.forName(driver);
     }


     public void insertUseStatement()  throws Exception{
           long start =System.currentTimeMillis();

           //获取数据库连接
           Connection conn=DriverManager.getConnection(url, user, pass);
           //使用Connection创建Statement对象
           Statement  st =conn.createStatement();
           {
                //需要使用100条语句来插入100条记录
                for(int i=0;i<100;i++){
                     st.executeUpdate("insert  into  student_table  values("
                                + "null,'姓名"+i+"',1");
                }
                System.out.println("使用Statement用时"+(System.currentTimeMillis() - start));

           }
     }

     public void insertUsePreparedStatement() throws Exception{
long start =System.currentTimeMillis();

           //获取数据库连接
           Connection conn=DriverManager.getConnection(url, user, pass);
           //使用Connection创建PreparedStatement对象
           PreparedStatement ps=conn.prepareStatement(
                     "insert  into  student_table  values(null,?,1)");
           {
                //100次为PreparedStatement的参数设值,就可以插入100条记录
                for(int i=0;i<100;i++){
                     ps.setString(1, "姓名"+i);
                     ps.executeUpdate();
                }
                System.out.println("使用PreparedStatement用时"+(System.currentTimeMillis() - start));
           }

     }

     public static void main(String []  args) throws Exception{
           PreparedStatementTest pt=new PreparedStatementTest();
           pt.initParam("mysql.ini");
         pt.insertUsePreparedStatement();
         pt.insertUseStatement();
     }

}



使用PreparedStatement有一个很好的作用,用于防止SQL注入。(SQL注入是一种比较常见的Cracker入侵方式,它利用SQL语句的漏洞来入侵)

//下面以一个简单的登录窗口为例来说明SQL注入的结果:

import java.awt.BorderLayout;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class LoginFrame {
     
     private final String PROP_FILE ="mysql.ini";
     private    String driver;
     private    String url;
     private    String user;
     private    String  pass;

     //登录界面的GUI组件
     JFrame  jf=new JFrame();
     JTextField userFiled =new JTextField(20);
     JTextField passFiled =new JTextField(20);
     JButton  loginButton=new JButton("登录");

     public void init()  throws Exception
     {
           Properties pro =new Properties();
           pro.load(new FileInputStream(PROP_FILE));
           driver=pro.getProperty(driver);
           url=pro.getProperty(url);
           user=pro.getProperty(user);
           pass=pro.getProperty(pass);
           //加载驱动
           Class.forName(driver);

           //为登录按钮添加事件监听器
           loginButton.addActionListener(e ->
           {
                //登录成功则显示登录成功
                try {
                     if(validate(userFiled.getText(),passFiled.getText())){
                           JOptionPane.showMessageDialog(jf, "登录成功");
                     }
                     else{
                           JOptionPane.showMessageDialog(jf, "登录失败");
                     }
                } catch (Exception e1) {
                     // TODO Auto-generated catch block
                     e1.printStackTrace();
                }
           });

           jf.add(userFiled, BorderLayout.NORTH);
           jf.add(passFiled);
           jf.add(loginButton,BorderLayout.SOUTH);
           jf.pack();
           jf.setVisible(true);

     }

     private  boolean  validate(String userName,String userPass) throws SQLException{
           //执行查询的sql语句
           String  sql ="select  *  from  jdbc_test" +"where  jdbc_name='" +userName+" ' and jdbc_desc='"+userPass +  "  ' "   ;
           System.out.println(sql);
           Connection  conn =DriverManager.getConnection(sql, user, pass);
           Statement  st =conn.createStatement();
           ResultSet  rs  =st.executeQuery(sql);

           {
                //如果查询的ResultSet里面有超过一条记录,则登录成功
                if(rs.next())
                {
                     return true;
                }
           }

           return false;

     }

     public static void main(String [] args) throws Exception{
           new LoginFrame().init();
     }

}

**运行程序,如果正常输入用户名、密码当然没有问题,输入正确可以正常登录,输入错误将提示失败。
但是如果输入 ’ or true or’ 可以登录成功,可以在程序运行的后台看到如下SQL语句:

//利用SQL注入后生成的SQL语句
select * from jdbc_test where jdbc_name =’ ’ or true or ’ ’ and jdbc_desc=’ ‘

因为Cracker直接输入了true,而SQL把这个true当成了直接量。

****如果把上面的validate() 方法换为使用PreparedStatement来执行验证:

private  boolean  validate(String userName,String userPass) throws SQLException{
           Connection  conn=DriverManager.getConnection(url, user, pass);
           PreparedStatement pre =conn.prepareStatement(
                     "select   *  from  jdbc_test   where  jdbc_name=? and jdbc_desc=?");
           {
                pre.setString(1, userName);

                pre.setString(2, userPass);
               ResultSet  rs=pre.executeQuery();
             //如果查询的ResultSet里面有超过一条记录,则登录成功
                if(rs.next())
                {
                     return true;
                }

           }


           return false;

     }


private  boolean  validate(String userName,String userPass) throws SQLException{
           Connection  conn=DriverManager.getConnection(url, user, pass);
           PreparedStatement pre =conn.prepareStatement(
                     "select   *  from  jdbc_test   where  jdbc_name=? and jdbc_desc=?");
           {
                pre.setString(1, userName);

                pre.setString(2, userPass);
               ResultSet  rs=pre.executeQuery();
             //如果查询的ResultSet里面有超过一条记录,则登录成功
                if(rs.next())
                {
                     return true;
                }

           }


           return false;

     }


总体来看,使用PreparedStatement比使用Statement多几个优点:
PreparedStatement预编译SQL语句,性能更好
PreparedStatement无需“拼接”SQL语句,编程更简单
PreparedStatement可以防止SQL注入,安全性更好。

你可能感兴趣的:(Java笔记,Java)