SQL——JDBC的介绍

来源:互联网 发布:mac obs 插件 编辑:程序博客网 时间:2024/06/06 14:07

JDBC——Java DataBase Connectivity数据库连接;
JDBC 就是由 java提供的一套访问数据库的统一api。 使用这套api , 我们在切换库时十分方便. 并且切换库不会改变代码.

一、开发一个JDBC程序的流程
1> 导包 :导入厂商提供的数据库驱动。mysql-connector-java-5.0.8-bin.jar 下载的连接如下:
http://download.csdn.net/detail/panpan_1994/9600222
2> 注册驱动
3> 连接数据库
4> 操作数据库(执行sql)
5> 关闭资源

二、JDBC中的类
DriverManager 用于注册驱动,获得连接
Connection 代表连接 , 获得Statement对象
Statement 运送sql语句
ResultSet 将运行结果从数据库运回java端

三、DriverManager 细节问题

1> 注册驱动的问题.

DriverManager.registDriver(new Driver()); ==> 该种注册方式,在将来的开发中 不要使用.
使用如下方式:

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

2>为什么?

    在驱动类的代码中,我们可以看到有一个静态代码块。 静态代码块中已经做了注册驱动的事情。 所以我们只需要加载。

3>使用 Class.forName有什么好处?

    * 如果调用registDriver 方法, 那么相当于创建了两个Driver对象,浪费资源.    * 使用forname的方式. 因为驱动类的名称是以字符串的形式填写,那么我们把该名称放到配置文件中,每次从配置文件中读取.那么切换驱动类就非常方便. 也就意味着切换数据库方便.

四、获得Connection的细节

DriverManager.getConnection("url","用户名","密码");url 填写格式:        外层协议:内部协议://主机名称[ip地址]:端口号/库名?参数键1=参数值&参数键2=参数值        jdbc:mysql://localhost:3306/alex?useUnicode=true&characterEncoding=utf-8结合上面说的方便切换数据库.    我们在书写时,也可以把上面3个参数,写到配置文件中.

五、Connection对象的细节问题
功能:
1.代表数据库的链接
2:以根据该对象创建运送sql语句的Statement对象

方法:

 创建statement对象Statement createStatement() 调用数据库的存储过程CallableStatement prepareCall(String sql) 创建 PreparedStatement 对象PreparedStatement prepareStatement(String sql)   

六、Statement对象
该对象可以理解为一个向数据库运送sql语句的“小车”
方法:

void addBatch(String sql)向车上添加语句。(用于批量执行sql语句);insert update deleteint[] executeBatch()   将车上的语句 运送给数据库执行.  返回值存放每个语句执行后影响的行数. 因为是多个语句,所以用数组装.void clearBatch() 清除车上的语句.

以上的方法主要用来执行批量的sql相关;

boolean execute(String sql)  执行一个sql语句. 如果该语句返回结果集;返回值为true(select).如果该语句不返回结果集 返回false(insert update delete);ResultSet executeQuery(String sql)执行一个有结果集的查询. 会将结果集包装到resultset对象中.(select)int executeUpdate(String sql)  执行一个没有结果集的语句. 会将语句影响的行数返回.(insert update delete)

结论:
       执行查询语句时使用: executeQuery方法
       执行增删改等语句时使用: executeUpdate方法

七、ResultSet对象
功能: 当执行的语句是查询语句时, resultSet对象用于封装查询结果.

方法:

boolean next()该方法让结果集中的指针(游标)往下移动一行.并且判断该行是否有数据。 有返回true,没有返回falseString getString(int cloumnCount) 从当前指向的行中获得String 类型的数据.  根据列所在的索引位置取.String getString(String columnName) 从当前指向的行中获得String 类型的数据. 根据列名取.getXXX系列方法 有很多种, 没对针对的都是数据库中的不同类型.
数据库类型 对应的Get方法 char/varchar getString int getInt bigint getLong float/double getFloat/getDouble datetime/timestamp getDate

八、ResultSet了解部分

1、结果集滚动:滚动指的就是指针的位置不仅可以向下,还可以任意控制.
涉及的方法如下:

 boolean absolute(int row)  将指针移动到指定位置. 参数就是位置. 第一行的位置是1. 如果填写负数表示倒数.例如-1=>最后一行. 如果移动超出范围将会返回false.void afterLast()  将光标移动到此 ResultSet 对象的末尾,正好位于最后一行之后。 (该行没有数据)void beforeFirst()   将光标移动到此 ResultSet 对象的开头,正好位于第一行之前。(result的初始位置)boolean first()   将光标移动到第一行boolean last()    将光标移动到最后一行 boolean next()   光标向下移动一行. boolean previous()  next反方向移动.向上移动一行.

2、使用resultSet修改记录
默认情况下resultSet 是不能反向修改数据库中的记录的. 需要在创建Statement对象时, 通过指定参数创建一个可以产生可以修改数据的resultSet对象的Statement。

Statement createStatement(int resultSetType, int resultSetConcurrency)  参数1  resultSetType - 结果集类型   ResultSet.TYPE_FORWARD_ONLY、 不支持结果集滚动,只能向前.                 ResultSet.TYPE_SCROLL_INSENSITIVE  支持滚动, 迟钝,不敏感的结果集.ResultSet.TYPE_SCROLL_SENSITIVE    支持滚动, 敏感的结果集.参数2  resultSetConcurrency  - 结果是否支持修改类型ResultSet.CONCUR_READ_ONLY   不支持修改ResultSet.CONCUR_UPDATABLE   支持修改 
利用如下代码可以反向修改数据库中的数据:String sql = "select * from emp";Statement state = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);ResultSet rs = state.executeQuery(sql);rs.next();rs.updateString("ename", "haha");rs.updateRow();结论: 不要使用resultSet 做修改的操作.真的要做修改我们要手写update语句来做. 

九、释放资源

1> 从小到大释放. resultSet < Statement < Connection
2> 3个都需要释放.
3>释放时调用close方法即可. 如果其中一个对象的关闭 出现了异常. 也要保证其他的对象关闭方法被调用.

    resultSet.close();    Statement.close();    Connection.close();    以上代码是无法保证一定都能执行的.    try{        resultSet.close();    }catch(Exception e){    }finally{        try{            Statement.close();        }catch(Exception e){        }        finally{            try{                Connection.close();            }catch(Exception e){            }        }    }

举例一:

package cn.itcast.a_hello;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import org.junit.Test;public class Demo {    @Test    //发送插入语句    public void fun1() throws Exception{        //1 导入驱动类库        //2 注册驱动        DriverManager.registerDriver(new com.mysql.jdbc.Driver());         //3 连接数据库        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //4 操作数据库        Statement st = conn.createStatement();        String sql =  " INSERT INTO `t_user` "+                      " VALUES (NULL, 'tom', 18)" ;        st.executeUpdate(sql);        //5 关闭资源        st.close();        conn.close();    }    @Test    //发送查询语句    public void fun2() throws Exception{        //1 导入驱动类库        //2 注册驱动        DriverManager.registerDriver(new com.mysql.jdbc.Driver());         //3 连接数据库        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //4 操作数据库        Statement st = conn.createStatement();        String sql =  " select * from t_user " ;        ResultSet rs = st.executeQuery(sql);        //遍历结果集中的内容并打印        while(rs.next()){            String name = rs.getString("name");            int id = rs.getInt("id");            int age = rs.getInt("age");            System.out.println(name+"==>"+age+"==>"+id);        }        //5 关闭资源        st.close();        conn.close();    }}

举例二: DriverManager细节

package cn.itcast.b_dm;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import org.junit.Test;public class Demo {    @Test    public void fun1() throws Exception{        // 注册驱动        //注册方式1:不推荐 => 驱动实现类中 的静态代码以及调用过        // DriverManager.registerDriver(driver);        //注册方式2:推荐        Class.forName("com.mysql.jdbc.Driver");    }    @Test    public void fun2() throws Exception{        // 获得连接        DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/alex", "root", "1234");        //url完整 格式:  大协议:子协议://IP地址:端口号/库名?参数键=参数值        //完整:             jdbc:mysql://127.0.0.1:3306/day05?useUnicode=true&characterEncoding=utf8        //简单:             jdbc:mysql:///day05?useUnicode=true&characterEncoding=utf8    }}

举例三: Statement细节

package cn.itcast.c_st;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import org.junit.Test;public class Demo {    @Test    //execute   原始,增删改查都可以 返回值  true=> 查询有结果集  |  false=> 查询没有结果集    //executeBatch  批量执行sql    //executeUpdate 执行增删改    //executeQuery 执行查询    public void fun1() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement();        //4 书写sql        String sql =  " INSERT INTO `t_user` "+                  " VALUES (NULL, 'jerry', 16)" ;        //5 执行sql    boolean result =    st.execute(sql);    System.out.println(result);//false        //6关闭资源     st.close();     conn.close();    }    @Test    public void fun2() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement();        //4 书写sql        String sql =  "select * from t_user" ;        //5 执行sql    boolean result =    st.execute(sql);        if(result){            ResultSet  rs = st.getResultSet();            System.out.println(rs);        }        //6关闭资源     st.close();     conn.close();    }    @Test    //executeUpdate    public void fun3() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement();        //4 书写sql        String sql =  " INSERT INTO `t_user` "+                  " VALUES (NULL, 'jack', 20)" ;        //5 执行sql        int row =  st.executeUpdate(sql);        if(row!=1){            throw new RuntimeException("插入失败!");        }        System.out.println(row);//1        //6关闭资源     st.close();     conn.close();    }    @Test    //executeQuery    public void fun4() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement();        //4 书写sql        String sql =  "select * from t_user" ;        //5 执行sql        ResultSet rs = st.executeQuery(sql);        //遍历rs        System.out.println(rs);        //6关闭资源     st.close();     conn.close();    }}

举例四:ResultSet细节
功能: 封装结果集数据
操作: 如何获得(取出)结果
结论:
1. next方法,向下移动并判断是否有内容
2. getXXX方法,根据列索引或列名获得列的内容

public class Demo {    @Test    public void fun1() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement();        //4 书写sql        String sql =  "select * from t_user" ;        //5 执行sql        ResultSet rs = st.executeQuery(sql);        //向下移动一行,并判断        while(rs.next()){            //有数据            //取数据:getXXX             int id = rs.getInt(1);//获得第一列的值            //int id rs.getInt("id");// 获得id列的值            String name = rs.getString(2);//获得第二列的值            int age = rs.getInt(3);//获得第三列的值            System.out.println(id+"==>"+name+"==>"+age);        }        //6关闭资源     st.close();     conn.close();    }
数据库类型java类型intintdoubledoubledecimal doublecharStringvarchar StringdatetimeDatetimestampTimestamp/Date



举例五:ResultSet细节
1.结果集的滚动 => 移动结果集的指针就是滚动
2.结果集反向修改数据库

public class Demo2 {    @Test    public void fun1() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement();        //4 书写sql        String sql =  "select * from t_user" ;        //5 执行sql        ResultSet rs = st.executeQuery(sql);        //倒着遍历        //1> 光标移动到最后一行之后            rs.afterLast();        //2> 遍历=>            //向上移动光标,并判断是否有数据            while(rs.previous()){            int id = rs.getInt("id");// 获得id列的值            String name = rs.getString("name");//获得第二列的值        //获得第三列的值           int age = rs.getInt("age");     System.out.println(id+"==>"+name+"==>"+age);            }        //6关闭资源     st.close();     conn.close();    }}

结果集反向修改数据库

public class Demo3 {    @Test    public void fun1() throws Exception{        //1 注册驱动        Class.forName("com.mysql.jdbc.Driver");        //2 获得连接        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/alex", "root", "1234");        //3 创建Statement        Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);        //4 书写sql        String sql =  "select * from t_user" ;        //5 执行sql        ResultSet rs = st.executeQuery(sql);        //使用结果集 反向修改数据库        rs.next();//将光标移动到第一行        rs.updateString("name", "汤姆");// 修改第一行name列的值为中文汤姆        rs.updateRow();// 确认修改        //6关闭资源     st.close();     conn.close();    }}

举例六:JDBCUtils工具类

public class JDBCUtils {    private static String driver;    private static String url;    private static String user;    private static String password;    static{        try {            //0读取配置文件            Properties prop  = new Properties();            InputStream is = new FileInputStream("src/db.properties");            prop.load(is);            is.close();            driver = prop.getProperty("driver");            url = prop.getProperty("url");            user = prop.getProperty("user");            password = prop.getProperty("password");            //1 注册驱动            Class.forName(driver);        } catch (Exception e) {            e.printStackTrace();        }    }    //1 获得连接    public static Connection getConnection(){        Connection conn = null;        try {            //2 获得连接            conn = DriverManager.getConnection(url, user, password);        } catch (Exception e) {            e.printStackTrace();            throw new RuntimeException("创建连接失败!");        }        return conn;    }    //2 释放资源        //1> 参数可能为空        //2> 调用close方法要抛出异常,确保即使出现异常也能继续关闭        //3>关闭顺序,需要从小到大    public  static void  close(Connection conn , Statement st , ResultSet rs){        try {            if(rs!=null){                rs.close();            }        } catch (SQLException e) {            e.printStackTrace();        }finally{            try {                if(st!=null){                st.close();                 }            } catch (SQLException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }finally{                try {                    if(conn!=null){                        conn.close();                           }                } catch (SQLException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) {        System.out.println(getConnection());    }}

打印结果:com.mysql.jdbc.Connection@c4aad3

关于db.properties的内容:

driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/empuser=****password=****

十、SQL注入攻击
早年登录逻辑,就是把用户在表单中输入的用户名和密码带入如下sql语句. 如果查询出结果,那么 认为登录成功.

SELECT * FROM USER WHERE NAME='' AND PASSWORD='xxx';

sql注入: 请尝试以下 用户名和密码.

将用户名和密码带入sql语句, 如下:
SELECT * FROM USER WHERE NAME=’xxx’ OR 1=1; – ’ and password=’xxx’;

– 发现sql语句失去了判断效果,条件部分成为了恒等式.
– 导致网站可以被非法登录, 以上问题就是sql注入的问题.

思考会出现什么问题?
将用户名密码带入sql语句,发现sql语句变成了如下形式:

SELECT * FROM t_student WHERE NAME='abcd'OR 1=1;-- ' AND PASSWORD='1234';

该sql语句就是一个 恒等条件.所以 一定会查询出记录. 造成匿名登陆.有安全隐患

如上问题,是如何解决呢?
1>解决办法:在运送sql时,我们使用的是Statement对象. 如果换成prepareStatement对象,那么就不会出现该问题.

2>sql语句不要再直接拼写.而要采用预编译的方式来做.
完成如上两步.即可解决问题.

*为什么使用PrepareStatement对象能解决问题?
sql的执行需要编译. 注入问题之所以出现,是因为用户填写 sql语句 参与了编译. 使用PrepareStatement对象在执行sql语句时,会分为两步. 第一步将sql语句 “运送” 到mysql上编译. 再回到 java端 拿到参数 运送到mysql端.用户填写的 sql语句,就不会参与编译. 只会当做参数来看. 避免了sql注入问题;

PrepareStatement 在执行 母句相同, 参数不同的 批量执行时. 因为只会编译一次.节省了大量编译时间.效率会高.

使用PrepareStatement对象 与 Statement对象的区别        1.Statement 可以先行创建, 然后将sql语句写入.          PrepareStatement 在创建时一定要传入 sql语句, 因为它要先运送到数据库执行预编译            api:                    PreparedStatement pst = conn.prepareStatement(sql);        2. PrepareStatement 在执行之前 先要设置 语句中的参数.             api:                 pst.setString(1, name);  -- set方法的调用要看 参数的类型.                char/varchar    setString                int             setInt                double          setDouble                datatime/timestamp  setDate        3. Statement对象在真正执行时 传入sql语句           PrepareStatement 在执行之前已经 设置好了 sql语句 以及对应参数. 执行方法不需要参数            api:                ResultSet rs = pst.executeQuery();

举例一: 演示使用Statement对象,sql注入问题

public class Demo {    @Test    public void fun1()throws Exception{        String name = "xxx'OR 1=1 -- ";        String password="1234";        //获得连接        Connection conn = JDBCUtils.getConnection();        //获得Statement        Statement st = conn.createStatement();        //拼装sql语句        String sql = "SELECT * FROM t_user  WHERE NAME='"+name+"' AND   PASSWORD='"+password+"';";        //执行sql语句并拿到结果        ResultSet rs=st.executeQuery(sql);        //根据结果判断是否登录成功        if(rs.next()){            System.out.print("登录成功");        }else {            System.out.print("登录失败");        }        //关闭资源        JDBCUtils.close(conn, st, rs);    }

举例二:演示使用PrepareStatement对象,解决sql注入问题

public void fun2() throws Exception{        String name ="xxx' OR 1=1 -- ";        String password ="1234";        //1 获得连接        Connection conn= JDBCUtils.getConnection();        //2 拼装sql语句        String sql = "SELECT * FROM t_user  WHERE NAME=? AND   PASSWORD=?";        //3 获得PrepareStatement        PreparedStatement ps = conn.prepareStatement(sql);        //4 设置参数到ps对象中        ps.setString(1, name);        ps.setString(2, password);        //5 运送参数,执行sql并拿到结果    ResultSet rs =  ps.executeQuery();        //5 根据结果判断是否登录成功        if(rs.next()){            System.out.println("登录成功!");        }else{            System.out.println("登录失败!");        }        //6关闭资源        JDBCUtils.close(conn, ps, rs);    }

十一 、存取大文本(将来有可能遇到)

create table myblob(        id int primary key AUTO_INCREMENT,        file blob    )

举例一: 演示向mysql中存放大文本数据

注意:存储大文本必须使用PrepareStatement对象

public class Demo {    @Test    public void fun1()throws Exception{        //1 获得连接        Connection conn=JDBCUtils.getConnection();        //2 书写sql        String sql = "insert into mytext values(null,?)";        //3 创建PrepareStatement        PreparedStatement ps=conn.prepareStatement(sql);        //4 设置参数        //参数1:参数的索引        //参数2:需要保存的文本的流        //参数3:文件长度        File f = new File("src/text.txt");        FileReader reader = new FileReader(f);        ps.setCharacterStream(1, reader, (int)f.length());        //5 执行sql        int result = ps.executeUpdate();        System.out.println(result);        //6关闭资源        JDBCUtils.close(conn, ps, null);        }    }

举例二:演示向mysql中存放图片
注意:存储图片必须使用PrepareStatement对象

public class Demo {    @Test    public void fun1()throws Exception{        //1、获得连接        Connection conn=JDBCUtils.getConnection();        //2.书写sql语句        String sql = "insert into myblob values(null,?)";        //3创建PrepareStatement        PreparedStatement ps=conn.prepareStatement(sql);        //4.设置参数        //参数1:参数的索引        //参数2:需要保存的图片的流        //参数3:图片文件长度        File f = new File("src/wg.png");        InputStream is = new FileInputStream(f);        ps.setBinaryStream(1, is,(int)f.length());        //5.执行sql        int result = ps.executeUpdate();        System.out.println(result);        //6关闭资源        JDBCUtils.close(conn, ps, null);    }}

十二、批量执行sql (Statement/PrepareStatement)

举例:使用Statement对象批量执行sql

public class Demo {    @Test    public void fun1()throws Exception{        //1获得连接        Connection conn=JDBCUtils.getConnection();        //2.获得Statement        Statement st=conn.createStatement();        //3.添加多条数据到t_stu中        //st.addBatch("create table t_stu(id int primary key auto_increment,name varchar(20))");        //st.addBatch("insert into t_stu values(null,'tom')");        st.addBatch("create table t_stu ( id int primary key auto_increment , name varchar(20))");        st.addBatch("insert into t_stu values(null,'tom')");        st.addBatch("insert into t_stu values(null,'jerry')");        st.addBatch("insert into t_stu values(null,'jack')");        st.addBatch("insert into t_stu values(null,'rose')");        //4.执行sql        int[] results = st.executeBatch();        System.out.println(Arrays.toString(results));        //5关闭资源        JDBCUtils.close(conn, st, null);}       

举例:使用PrepareStatement对象批量执行sql

@Testpublic void fun2() throws Exception{                //1 获得连接            Connection conn = JDBCUtils.getConnection();            //2 书写sql语句            String sql = "insert into t_stu values(null,?)";            //3 创建PrepareStatement            PreparedStatement ps = conn.prepareStatement(sql);            //4 循环.添加参数            for(int i=0;i<100;i++){                ps.setString(1, "用户"+i);                ps.addBatch();            }            //5 批量执行            int[]  results =ps.executeBatch();            System.out.println(Arrays.toString(results));            //5关闭资源            JDBCUtils.close(conn, ps, null);            }}
0 0
原创粉丝点击