Java基础之JDBC 上篇

概述

很多时候数据不可能在内存中报错数据毕竟内存只要重启数据就会被擦掉,而且也不可能存储很多数据,虽然现在的内存条比以往大太多了。哪怕我电脑内存条64G也无法存储大量的数据。

所以说JDBC就是将这些数据持久化。把数据保存到可掉电式存储设备中义工之后使用。大多数情况下,特别是企业级应用数据持续化意味着将内存中的数据保存到硬盘上,而一般持久化的实现过程大多数通过各种关系数据库来完成。

既然在java中聊持久化,那自然就是通过Java对数据库进行一些操作。而这些具体分:

  • JDBC直接访问数据库

  • JDO(Java Data Object)技术

  • 第三方O/R工具,如Mybatis,Hibernate等

    JDBC是Java访问数据库的基石,JDO,Mybatis等只是更好的封装JDBC.

JDBC介绍

JDBC(Java Database Connectivity)是一种独立特定数据管理习惯,通用的SQL来操作数据存取修改等行为的接口。定义了用来访问数据库的标准Java类库,使用这些类库可以用一种标准的方法方便的访问数据库资源。

但是数据库不单单是一种,有MYSQL和ORALCE等,所以JDBC为访问不同的数据库提供了一种统一的途径,毕竟JDBC的目标就是JAVA开发者可以使用JDBC连接任何使用了JDBC驱动的数据库系统,这样无序开发者对特定的数据库系统的特点有过多了解,从而大大简化和加快了开发过程。

如果没有JDBC,那么JAVA需要直接对用所有的数据库如下:

在这里插入图片描述

但是现实情况如下:

在这里插入图片描述

说白了就是几个大厂和sun公司商量一些,你们满足我提供接口,然后提供一个驱动,然后让开发者几乎可用相同的方法去操作数据库,而只需要换一下不同的数据库驱动即可。

具体如何使用,图示如下:

在这里插入图片描述

驱动器–Driver

Java.sql.Driver接口是对所有的JDBC驱动程序定义的要实现的接口,而不同的数据库厂商提供不同的实现。而程序不允许直接去访问实现Driver接口的类,而是由驱动程序管理器类(Java.sql.DriverManager)去调用这游戏Driver实现。

  • Oracle的驱动: oracle.jdbc.driver.OracleDriver
  • Mysql的驱动:com.mysql.jdbc.Driver

因为演示通过MYsql演示所以自然就是导入om.mysql.jdbc.Driver

演示创建连接

这个还是老规矩直接演示比较靠谱,但是导入也分好几种,依次进行演示

方式1

        // 引入包了,所以自然要直接导入mysql的驱动
        Driver driver=new com.mysql.jdbc.Driver();
//      创建一个地址,毕竟来连接数据库,需要一个地址的
//        jdbc:mysql 是协议名,比如http和http的一样
//        localhost :这个是ip地址,也就是本机,如果是mysql在其他机子上可以将这个变成服务器所在的ip
//        3306 :mysql 默认的端口
//        test : 目标数据库名
        String url="jdbc:mysql://localhost:3306/test";
        Properties info=new Properties();
        info.setProperty("user","root");
        info.setProperty("password","root");
        // 这个会报一个错误,直接将其抛出
        Connection connection=driver.connect(url,info);
        System.out.println(connection);

在这里插入图片描述

方式2

上一个是直接通过new第三方的类创建驱动,但是有时候为了不显示第三方的类,会通过反射创建这个第三方的驱动;

    // 引入包了,所以自然要直接导入mysql的驱动
        Class driverclass=Class.forName("com.mysql.jdbc.Driver");
        Driver driver= (Driver) driverclass.newInstance();
//      创建一个地址,毕竟来连接数据库,需要一个地址的
//        jdbc:mysql 是协议名,比如http和http的一样
//        localhost :这个是ip地址,也就是本机,如果是mysql在其他机子上可以将这个变成服务器所在的ip
//        3306 :mysql 默认的端口
//        test : 目标数据库名
        String url="jdbc:mysql://localhost:3306/test";
        Properties info=new Properties();
        info.setProperty("user","root");
        info.setProperty("password","root");
        // 这个会报一个错误,直接将其抛出
        Connection connection=driver.connect(url,info);
        System.out.println(connection);

在这里插入图片描述

这样写的意义说白,有点脱裤子放屁的意思,但是有时候就会有这样的要求。不过人家提出这样的规范自然尤其存在的意义,所以不必纠结。还是那句话明白最好,不明白就遵守后面慢慢了解。

方式3

前面说的了一个类驱动管理类(DriverManager),那么自然可以通过这个类对驱动进行管理。先看一下官方文档。

在这里插入图片描述

可以看出驱动管理类可以直接得到connection类,但是如果将驱动管理类和驱动关联起来呢?将其注册给驱动管理类即可:

      // 引入包了,所以自然要直接导入mysql的驱动
        Class driverclass=Class.forName("com.mysql.jdbc.Driver");
        Driver driver= (Driver) driverclass.newInstance();
//         将驱动注册给驱动管理器
        DriverManager.registerDriver(driver);
        String url="jdbc:mysql://localhost:3306/test";
        Properties info=new Properties();
        String user="root";
        String password="root";
        Connection connection=DriverManager.getConnection(url,user,password);
        System.out.println(connection);

在这里插入图片描述

方式4

其实这个是方式3的简化,有时候会这样写:

      // 引入包了,所以自然要直接导入mysql的驱动
        Class driverclass=Class.forName("com.mysql.jdbc.Driver");
        Driver driver= (Driver) driverclass.newInstance();
//         将驱动注册给驱动管理器
      //  DriverManager.registerDriver(driver); 不注册了
        String url="jdbc:mysql://localhost:3306/test";
        Properties info=new Properties();
        String user="root";
        String password="root";
        Connection connection=DriverManager.getConnection(url,user,password);
        System.out.println(connection);

在这里插入图片描述

可以看出没有DriverManager.registerDriver(driver)依然可以得到连接。为什么?

打开com.mysql.jdbc.Driver的源码可以看出,有一个static代码块,人家会自动帮你注册一个。

在这里插入图片描述

  • 补充

    如果在省略一点可以吗?

          // 引入包了,所以自然要直接导入mysql的驱动
       //     Class driverclass=Class.forName("com.mysql.jdbc.Driver");
        //    Driver driver= (Driver) driverclass.newInstance();
    //         将驱动注册给驱动管理器
          //  DriverManager.registerDriver(driver); 不注册了
          
          
          String url="jdbc:mysql://localhost:3306/test";
            Properties info=new Properties();
            String user="root";
            String password="root";
            Connection connection=DriverManager.getConnection(url,user,password);
            System.out.println(connection);
    

在这里插入图片描述

竟然也可以啊,为什么呢?

因为人家本身就帮你配置好了:

在这里插入图片描述

但是一般不要如此省略,因为这种省略只能在Mysql中可以,如果Oralce就不行,因为oracle却没有这样帮你设置这个配置文件。

方式5

其实很多配置信息会单独放在一个配置文件下面的:

比如建立一个mysql.properies.

user = root
password = root
driver =  com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/test

然后写JDBC

Properties properties=new Properties();
                 properties.load(jdbc.class.getClassLoader().getResourceAsStream("mysql.properies"));
 String user=properties.getProperty("user");
 String password=properties.getProperty("password");
 String url=properties.getProperty("url");
 String drivername=properties.getProperty("driver");
        // 引入包了,所以自然要直接导入mysql的驱动
 Class driverclass=Class.forName(drivername);
 Driver driver= (Driver) driverclass.newInstance();

 Connection connection=DriverManager.getConnection(url,user,password);
 System.out.println(connection);

在这里插入图片描述

演示操作数据库

上面演示了如何连接数据库,但是连接数据库的目的就是操作数据库,而操作数据库需要接口:Statement

在这里插入图片描述

但是一般的时候,在开发的时候不会使用Statement这个接口,而是使用其子接口:PreparedStatement。因为Statement接口不安全,下面演示一下;

使用 Statement

首先在msyql中创建一个数据库test,然后创建一个表如下:

如果不了解,就看一下发表的的mysql基础文章。

CREATE DATABASE IF NOT EXISTS  test
USE test;
CREATE TABLE  IF NOT EXISTS uer_table(
user_name  VARCHAR(10),
user_psd   VARCHAR(10)

);
# 插入一条数据到数据库
INSERT INTO  uer_table VALUES ('haoren','123456');

然后写一个Java进行测试

public class jdbc {
    public static void main(String[] args) throws  Exception {
        test();
    }

    public  static  void test(){
        String userName="";
        String userPassWord="";
        Connection con=null;
        Statement sts=null;
        ResultSet rst=null;
        try {
            con=getCon();
            sts=con.createStatement();
            String sql="SELECT user_name, user_psd FROM test.uer_table  WHERE user_name='"+userName+"'  AND user_psd='"+userPassWord+"'";
            rst=sts.executeQuery(sql);
            if(rst.next()){// rst 可以这样理解 :和迭代器的结构有点相似
                System.out.println("数据库里面有这个人");
            }else{
                System.out.println("数据库没有返回符号条件的人");
            }

        }catch (Exception e){
        System.out.println(e);
        }finally {
            try {
                rst.close();
                sts.close();
                con.close();
            }catch (Exception e ){

            }

        }

    }
    // 将返回连接的单独放在这个方法里面
    public  static Connection getCon(){
        Connection connection=null;
        try{
            Properties properties=new Properties();
            properties.load(jdbc.class.getClassLoader().getResourceAsStream("mysql.properies"));
            String user=properties.getProperty("user");
            String password=properties.getProperty("password");
            String url=properties.getProperty("url");
            String drivername=properties.getProperty("driver");
            // 引入包了,所以自然要直接导入mysql的驱动
            Class driverclass=Class.forName(drivername);
            Driver driver= (Driver) driverclass.newInstance();
            connection=DriverManager.getConnection(url,user,password);
            return connection;
        }catch (Exception e){
             System.out.println(e);
        }
        return connection;
//        System.out.println(connection);
    }


}

然后不停的修改上面:

        String userName="haoren";
        String userPassWord="123456";

在这里插入图片描述

然后这样调用:

        String userName="haoren1";
        String userPassWord="123456";

在这里插入图片描述

因为数据库只有一条数据,为什么说不安全呢?现在变成这样

        String userName=" ' OR ' ";
        String userPassWord="'=1 OR 1='1";

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWswnszo-1645596909343)(F:\文档\笔记\java\基础\28:jdbc.assets\image-20220218152028587.png)]

其实这个本质就是用来一种简单sql注入方式而已,很多时候这个就是不安全。所以更多是时候使用的是其子类接口。

PreparedStatement

在这里插入图片描述

其实已经预编译了官方文档说的,这个就不再具体解释了。

为什么说preparedstatement安全,现在演示一下还是用上面的代码,如下:

public class jdbc {
    public static void main(String[] args) throws  Exception {
        test();
    }

    public  static  void test(){
        String userName="";
        String userPassWord="";
        Connection con=null;
        PreparedStatement psts=null;
        ResultSet rst=null;
        try {
            con=getCon();
            String sql="SELECT user_name, user_psd FROM test.uer_table  WHERE user_name=?  AND user_psd=? ";  // ? 是占位符, 就是将这个位置作为一个条件值站住

            psts= con.prepareStatement(sql);
            psts.setString(1,userName);
            psts.setString(2,userPassWord);
            rst=psts.executeQuery();
            if(rst.next()){
                System.out.println("数据库里面有这个人");
            }else{
                System.out.println("数据库没有返回符号条件的人");
            }

        }catch (Exception e){
        System.out.println(e);
        }finally {
            try {
                rst.close();
                psts.close();
                con.close();
            }catch (Exception e ){

            }

        }

    }
    public  static Connection getCon(){
        Connection connection=null;
        try{
            Properties properties=new Properties();
            properties.load(jdbc.class.getClassLoader().getResourceAsStream("mysql.properies"));
            String user=properties.getProperty("user");
            String password=properties.getProperty("password");
            String url=properties.getProperty("url");
            String drivername=properties.getProperty("driver");
            // 引入包了,所以自然要直接导入mysql的驱动
            Class driverclass=Class.forName(drivername);
            Driver driver= (Driver) driverclass.newInstance();
            connection=DriverManager.getConnection(url,user,password);
            return connection;
        }catch (Exception e){
             System.out.println(e);
        }
        return connection;
//        System.out.println(connection);
    }


}

现在进行测试:

       String userName="haoren";
        String userPassWord="123456";

在这里插入图片描述

       String userName="haoren1";
        String userPassWord="123456";

在这里插入图片描述

        String userName=" ' OR ' ";
        String userPassWord="'=1 OR 1='1";

在这里插入图片描述

可以看出preparedstatement在创建的时候用的sql其中有,这个其实是占位符,意思这个地方传入的值就是相当于SQL中在=后面’‘中间添加数据,哪怕是OR 这样的逻辑运算符符号也会成为引号内的值。

看一下两个方法:

在这里插入图片描述

不过SQL语句中有比如删除,更新,插入不用返回值,如果需要返回值的话也就是一个false和true来证明其是否执行成功所以用方法:execute()

如果是查询语句的话,就需要有一个返回的类似集合的类对象保存数据了,就要用到:executeQuery()

不过还有一个

ResultSet

其实这个前面也演示,那就是可以如果是查询语句的话,可以得到查询语句的数据,方便Java进行处理。虽然编程语言中都是从0开始但是这个取出的数据是从1开始的。这个大家需要注意一下。

上面的Connection,ResultSet和preparedstatement记住都要关闭,毕竟其 操作也是有连接的,这个是IO操作的流有点相似。

这个需要注意ResultSet中两个方法:

在这里插入图片描述

ResultSetMetaData 可用于获取有关ResultSet对象中列的类型和属性的信息的对象。

在这里插入图片描述

补充-通用的更新方法

 public static   void prepareStamentUtil(String sql,   String ... arg){
        Connection con=null;
        PreparedStatement prst=null;
        try {
            con=getCon();
            prst=con.prepareStatement(sql);
            int len =arg.length;
            for (int i = 0; i < len; i++) {
                prst.setObject(i,arg[i]);
            }
            prst.execute();

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                prst.close();
                con.close();
            }catch (Exception e ){
                e.printStackTrace();
            }

        }

    }

但是如果是查询数据的话,需要如下:

    // 如果是集合的话,那就按照自己的要求来修改,这个利用的反射
    public static <T>  T prepareStamentUtil(String sql,Class<T> classz,   String ... arg){
        Connection con=null;
        PreparedStatement prst=null;
        ResultSet rst=null;
        T t=null;
        try {
            con=getCon();
            prst=con.prepareStatement(sql);
            int len =arg.length;
            for (int i = 0; i < len; i++) {
                prst.setObject(i,arg[i]);
            }
            rst=prst.executeQuery();
            ResultSetMetaData rstdata =rst.getMetaData();
//          这个有两个方法可以得到SQL中的字段名,如果SQL中的字段名和类的属性最好一样
//
//            rstdata.getColumnName(int column)  如果SQL语句字段名不一样,使用AS得到别名和类的属性一样 这个就无法使用这个方法了
//            rstdata.getColumnLabel(int column)  这个得到无论是否别名都可以得到字段名
            int colmumnConut=rstdata.getColumnCount();
            while(rst.next()) {
                t=classz.newInstance();
                for (int i = 0; i < colmumnConut; i++) {
                    String colmumnName=rstdata.getColumnLabel(1+i);
                    Field field=classz.getDeclaredField(colmumnName);
                    field.setAccessible(true);
                    field.set(t,rst.getObject(i+1));
                }
            }
            return t;

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                rst.close();
                prst.close();
                con.close();
            }catch (Exception e ){
                e.printStackTrace();
            }

        }
        return t;
    }


其实上面的方法可以以及链接数据库可以单独弄一个工具类的,那样会更加方便。

批量操作

在JDBC其实在插入大量的数据时候,有些方式会加快这个速度,现在演示。

# 首先创建一个一个测试表 这个表就是测试,没有什么意义
CREATE TABLE test_p1(
num INT,
t_name VARCHAR(10)
)

首先用Statement测试;

public static void test_Statement(){

        Connection con=null;
        Statement stm=null;
        try {
            long start=System.currentTimeMillis();
            con=getCon();
            stm=con.createStatement();
            for (int i = 0; i < 20000; i++) {
             String sql="INSERT INTO yuarenxue.test_p1 VALUES('"+i+"', '张"+i+"')";
             stm.execute(sql);
            }
            long end=System.currentTimeMillis();
            System.out.println("Statement用的时间为:  "+(end-start));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {

                stm.close();
                con.close();
            }catch (Exception e ){

            }

        }
    }


在这里插入图片描述

记得将数据库表中数据删除,然后再试一下,这样排除了更多的干扰因素。

    public static void test_PreparedStatement(){

        Connection con=null;
        PreparedStatement pstm=null;
        try {
            long start=System.currentTimeMillis();
            con=getCon();
            String sql="INSERT INTO yuarenxue.test_p1 VALUES(?, ?)";
            pstm=con.prepareStatement(sql);
            for (int i = 0; i < 20000; i++) {
                pstm.setObject(1,i);
                pstm.setObject(2,"张"+i);
                pstm.execute();
            }
            long end=System.currentTimeMillis();
            System.out.println("PreparedStatement用的时间为:  "+(end-start));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {

                pstm.close();
                con.close();
            }catch (Exception e ){

            }

        }
    }

在这里插入图片描述

因为PreparedStatement会将sql预编译,所以要比Statement更快。

真正的批处理

#这个能首先说一下 MYSQL服务器默认是关闭批处理的所以
#需要一个参数开启批处理
url = jdbc:mysql://localhost:3306/test?rewriteBatchedStatments=true

然后还有三个方法:

  • addBatch():

在这里插入图片描述

  • executeBatch():

在这里插入图片描述

  • clearBatch():

在这里插入图片描述

public static void test_Batch(){

        Connection con=null;
        PreparedStatement pstm=null;
        try {
            long start=System.currentTimeMillis();
            con=getCon();
            String sql="INSERT INTO yuarenxue.test_p1 VALUES(?, ?)";
            pstm=con.prepareStatement(sql);
            for (int i = 0; i < 20000; i++) {
                pstm.setObject(1,i);
                pstm.setObject(2,"张"+i);
                pstm.addBatch();
                if ((i+1)%500==0){
                    pstm.executeBatch();
                    pstm.clearBatch();
                }
            }
            long end=System.currentTimeMillis();
            System.out.println("批处理用的时间为:  "+(end-start));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {

                pstm.close();
                con.close();
            }catch (Exception e ){

            }

        }
    }

在这里插入图片描述

当然上面写的是(i+1)%500==0也可以控制整个500变成更大的数字减少交互变得更快,还有一种提高的方式,那就是因为MYSQL是自动提交的,所以可以手动提交如下:

public static void test_Batch(){

        Connection con=null;
        PreparedStatement pstm=null;
        try {
            long start=System.currentTimeMillis();
            con=getCon();
//           因为mysql会自动更新,所以需要关闭才能使用 如果不设置为关闭也可以但是
            con.setAutoCommit(false);

            String sql="INSERT INTO yuarenxue.test_p1 VALUES(?, ?)";
            pstm=con.prepareStatement(sql);
            for (int i = 0; i < 20000; i++) {
                pstm.setObject(1,i);
                pstm.setObject(2,"张"+i);
                pstm.addBatch();
                if ((i+1)%500==0){
                    pstm.executeBatch();
                    pstm.clearBatch();
                }
            }
            con.commit();
            long end=System.currentTimeMillis();
            System.out.println("手动提交批处理用的时间为:  "+(end-start));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {

                pstm.close();
                con.close();
            }catch (Exception e ){

            }

        }
    }

在这里插入图片描述

因为篇幅有点长,所以分一下,现在了解一下JDBC,后面再了解一下事务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值