JAVA EE-JDBC

JAVA EE-JDBC

塑成一个雕像,把生命赋给这个雕像,这是美丽的;创造一个有智慧的人,把真理灌输给他,这就更美丽。 —— 雨果

什么是JDBC?
  • JDBC 就是由 java提供的一套访问数据库的统一api. 使用这套api , 我们在 切换库时 十分方便. 并且切换库不会改变代码.学习成本也降低了.
如何开发一个JDBC程序?
1 导包 ==> 导入厂商提供的数据库驱动. ==> mysql-connector-java-5.0.8-bin.jar
2> 注册驱动 
3> 连接数据库
4> 操作数据库(执行sql)
5> 关闭资源
JDBC中的类
DriverManager 用于注册驱动,获得连接
Connection    代表连接 , 获得Statement对象
Statement     运送sql语句
ResultSet     将运行结果从数据库运回java端
一个简单的JDBC连接实例
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/day05", "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/day05", "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的细节问题
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/day05", "root", "1234");

        //url完整 格式:  大协议:子协议://IP地址:端口号/库名?参数键=参数值
        //完整:             jdbc:mysql://127.0.0.1:3306/day05?useUnicode=true&characterEncoding=utf8
        //简单:             jdbc:mysql:///day05?useUnicode=true&characterEncoding=utf8
    }
}
1> 注册驱动的问题.
        DriverManager.registDriver(new Driver()); ==> 该种注册方式,在将来的开发中 不要使用.
    使用如下方式:
        Class.forName("com.mysql.jdbc.Driver");
    2>为什么?
        在驱动类的代码中,我们可以看到有一个静态代码块。 静态代码块中已经做了注册驱动的事情。 所以我们只需要加载
    驱动类,就相当于调用了 registDriver 方法。
    3>使用 Class.forName有什么好处?
        * 如果调用registDriver 方法, 那么相当于创建了两个Driver对象,浪费资源.
        * 使用forname的方式. 因为驱动类的名称是以字符串的形式填写,那么我们把该名称放到配置文件中,每次从配置文件中读取.
    那么切换驱动类就非常方便. 也就意味着切换数据库方便.
关于Statement的细节问题
//Statement细节
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/day05", "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/day05", "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/day05", "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/day05", "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();
    }
}
6.Statement 对象
    该对象可以理解为一个 向数据库运送sql语句的 "小车";
    方法:
        void addBatch(String sql)  向车上添加语句. (用于批量执行sql语句); insert update delete
        int[] executeBatch()   将车上的语句 运送给数据库执行.  返回值存放每个语句执行后影响的行数. 因为是多个语句,所以用数组装.
        void clearBatch() 清除车上的语句.
        ----以上3个方法是批量执行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的细节

Demo 1

//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/day05", "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类型
        int              int
        double           double
        decimal          double
        char             String
        varchar          String
        datetime         Date
        timestamp        Timestamp/Date

     */
}

Demo 2

//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();
    }
    /* 数据库类型            java类型
        int              int
        double           double
        decimal          double
        char             String
        varchar          String
        datetime         Date
        timestamp        Timestamp/Date

     */
}

Demo 3

//ResultSet细节
// 2.结果集反向修改数据库
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/day05", "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();
    }
}

参数问题:

        参数1  resultSetType - 结果集类型   
        ResultSet.TYPE_FORWARD_ONLY、 不支持结果集滚动,只能向前.
        ResultSet.TYPE_SCROLL_INSENSITIVE  支持滚动, 迟钝,不敏感的结果集.
        ResultSet.TYPE_SCROLL_SENSITIVE    支持滚动, 敏感的结果集.
        参数2  resultSetConcurrency  - 结果是否支持修改类型
        ResultSet.CONCUR_READ_ONLY   不支持修改
        ResultSet.CONCUR_UPDATABLE   支持修改
JDBC中关于释放资源的一些问题
9.释放资源
    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){

            }
        }
    }
自己封装的一JDBC工具类
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

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());
    }
}
SQL的注入问题
  • 早年登录逻辑,就是把用户在表单中输入的用户名和密码 带入如下sql语句. 如果查询出结果,那么 认为登录成功.
SELECT * FROM USER WHERE NAME='' AND PASSWORD='xxx';
  • 如果将用户名以及密码数据写成如下的形式:
SELECT * FROM USER WHERE NAME='xxx' OR 1=1 -- ' and password='xxx';
  • 发现sql语句失去了判断效果,条件部分成为了恒等式.
  • 导致网站可以被非法登录, 以上问题就是sql注入的问题.
那么我们如何解决SQL注入问题?
  • 解决办法:在运送sql时,我们使用的是Statement对象. 如果换成prepareStatement对象,那么就不会出现该问题.
  • sql语句不要再直接拼写.而要采用预编译的方式来做.
  • 完成如上两步.即可解决问题.
    *为什么使用PrepareStatement对象能解决问题?
    sql的执行需要编译. 注入问题之所以出现,是因为用户填写 sql语句 参与了编译.  使用PrepareStatement对象
    在执行sql语句时,会分为两步. 第一步将sql语句 "运送" 到mysql上编译.  再回到 java端 拿到参数 运送到mysql端.
    用户填写的 sql语句,就不会参与编译. 只会当做参数来看. 避免了sql注入问题;
    PrepareStatement 在执行 母句相同, 参数不同的 批量执行时. 因为只会编译一次.节省了大量编译时间.效率会高.
演示向mysql中存放大文本文件
public class Demo {
    @Test
    //演示向mysql中存放大文本数据
    //存储大文本必须使用PrepareStatement对象
    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);
        }

}
通过JDBC存储二进制文件
public class Demo {
    @Test
    //演示向mysql中存放图片
    //存储图片必须使用PrepareStatement对象
    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);
        }

}
批量执行MYSQL语句
public class Demo {
    @Test
    //1 使用Statement对象批量执行sql
    public void fun1() throws Exception{

        //1 获得连接
        Connection conn = JDBCUtils.getConnection();
        //2 获得Statement
    Statement st =  conn.createStatement();
        //3 添加多条sql语句到st中

    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);
        }
    @Test
    //2 使用PrepareStatement对象批量执行sql
    public 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值