JDBC学习笔记

  *Java DataBase Connectivity, Java 数据库 连接.

  *学习JDBC 学习的就是如何通过Java代码执行SQL语句

  *JDBC是Sun公司提供的一套Java语言和数据库进行连接的API(Application Programma Interface 应用程序 编程 接口)

  *为什么使用JDBC

    如果没有JDBC接口,Java程序员有可能每一种数据库都学习一套新的方法,Sun公司为了避免Java程序员做无用功, 通过JDBC接口规范了各个数据库厂商的方法

名, 各个数据库厂商必须根据JDBC接口中的方法名去写各自的实现类(实现类称为驱动,其实就是一个jar包), Java程序员只需要遵循JDBC的标准写代码,即使将

来换了数据库代码都不需要做任何的改动.

  *如何使用JDBC连接数据库

  * 创建maven工程

  * 在pom.xml文件中添加 驱动jar包的依赖 (从苍老师文档服务器中找到)

  * 创建Demo01.java 在main方法中添加以下代码:

//1.注册驱动  告诉编译器使用的数据库是什么
        Class.forName( "com.mysql.cj.jdbc.Driver" );
                //注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
        //2.获取数据库连接对象 Connection
        Connection conn= DriverManager.getConnection( "jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true",
                "root","root" );                     //ip       端口号/数据库名称  ?   字符集      & 安全认证    &  时区                      &是否支持批量操作

        //3.创建执行SQL语句的对象
        Statement statement=conn.createStatement();
        String sql="create table jdbct1(id int,name varchar(20))";
        //4.执行SQL语句
        statement.execute( sql );
        //5.关闭资源
        conn.close();

 

  * Statement执行SQL语句的对象

    * execute(sql);  execute执行 ,此方法可以执行任意SQL语句 但是推荐执行DDL(数据定义语言包括数据库相关和表相关的)

    * executeUpdate(sql); 执行增删改相关的SQL语句

    * ResultSet rs = executeQuery(sql);

  * 详解各个对象:

    一、 DriverManager:驱动管理对象

      * 功能:

      1. 注册驱动:告诉程序该使用哪一个数据库驱动jar

        static void registerDriver(Driver driver) :注册与给定的驱动程序DriverManager 。

      写代码使用: Class.forName("com.mysql.jdbc.Driver");

      通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块

 

      static {

        try {

          java.sql.DriverManager.registerDriver(new Driver());

         } catch (SQLException E) {

          throw new RuntimeException("Can't registerdriver!");

          }

        }

 

      注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。

 

      2. 获取数据库连接:

      * 方法:static Connection getConnection(String url, String user,String password)

      * 参数:

        * url:指定连接的路径

          * 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称

          * 例子:jdbc:mysql://localhost:3306/db3

          * 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称

        * user:用户名

        * password:密码

 

    二、 Connection:数据库连接对象

      1. 功能:

      1. 获取执行sql 的对象

        * Statement createStatement()

        * PreparedStatement prepareStatement(String sql)

      2. 管理事务:

        * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务

        * 提交事务:commit()

        * 回滚事务:rollback()

 

    三、Statement:执行sql的对象

      1. 执行sql

        1. boolean execute(String sql) :可以执行任意的sql 了解

        2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句

          * 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。

        3. ResultSet executeQuery(String sql) :执行DQL(select)语句

    四、ResultSet:结果集对象,封装查询结果

      * boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true

      * getXxx(参数):获取数据

        * Xxx:代表数据类型 如: int getInt() , String getString()

        * 参数:

          1. int:代表列的编号,从1开始 如: getString(1)

          2. String:代表列名称。 如: getDouble("balance")

      * 注意:

        * 使用步骤:

        1. 游标向下移动一行

        2. 判断是否有数据

        3. 获取数据

 

        //循环判断游标是否是最后一行末尾。(遍历结果集)

        while(rs.next()){

          //获取数据

          int id = rs.getInt(1);

          String name = rs.getString("name");

          double balance = rs.getDouble(3);

          System.out.println(id + "---" + name + "---" + balance);

        }

  * JDBC控制事务:

    1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

    2. 操作:

      1. 开启事务

      2. 提交事务

      3. 回滚事务

    3. 使用Connection对象来管理事务

      * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务

      * 在执行sql之前开启事务

      * 提交事务:commit()

      * 当所有sql都执行完提交事务

      * 回滚事务:rollback()

      * 在catch中回滚事务

    4.代码

        public class JDBCDemo10 {
            public static void main(String[] args) {
                Connection conn = null;
                PreparedStatement pstmt1 = null;
                PreparedStatement pstmt2 = null;
                try {
                    //1.获取连接
                    conn = JDBCUtils.getConnection();
                    //开启事务
                    conn.setAutoCommit( false );
                    //2.定义sql
                    //2.1 张三 - 500
                    String sql1 = "update account set balance = balance - ? where id = ?";
                    //2.2 李四 + 500
                    String sql2 = "update account set balance = balance + ? where id = ?";
                    //3.获取执行sql对象
                    pstmt1 = conn.prepareStatement( sql1 );
                    pstmt2 = conn.prepareStatement( sql2 );
                    //4. 设置参数
                    pstmt1.setDouble( 1, 500 );
                    pstmt1.setInt( 2, 1 );
                    pstmt2.setDouble( 1, 500 );
                    pstmt2.setInt( 2, 2 );
                    //5.执行sql
                    pstmt1.executeUpdate();
                    // 手动制造异常
                    int i = 3 / 0;
                    pstmt2.executeUpdate();
                    //提交事务
                    conn.commit();
                } catch (Exception e) {
                    //事务回滚
                    try {
                        if (conn != null) {
                            conn.rollback();
                        }
                    } catch (SQLException e1) {
                        e1.printStackTrace();
                    }
                    e.printStackTrace();
                } finally {
                    JDBCUtils.close( pstmt1, conn );
                    JDBCUtils.close( pstmt2, null );
                }
            }
        }

  * DBCP数据库连接池

    * DataBaseConnectionPool,

    * 使用连接池可以将连接重用,避免的频繁开关连接导致的资源浪费

    * 如何使用?

      将连接池相关依赖添加到pom.xml中
        <!-- 连接MySQL数据库的依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>

        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>    

      相关代码:

    public static void main(String[] args) throws IOException, SQLException {
        //创建数据库连接池对象
        DruidDataSource ds=new DruidDataSource();
        //设置数据库连接信息
            //通过配置文件获取数据库的信息
        Properties p=new Properties(  );
        //创建输入流
        InputStream ips=Demo03.class.getClassLoader().getResourceAsStream( "jdbc.properties" );
        //配置文件和属性建立关系
        p.load( ips );
        String driver = p.getProperty( "db.driver" );
        String url = p.getProperty( "db.url" );
        String name = p.getProperty( "db.username" );
        String password = p.getProperty( "db.password" );
        ds.setDriverClassName( driver );
        ds.setUrl( url );
        ds.setUsername( name );
        ds.setPassword( password );
        //设置初始连接数量
        ds.setInitialSize( 3 );
        //设置最大连接数量
        ds.setMaxActive( 5 );
        //获取链接对象
        Connection connection = ds.getConnection();
        System.out.println(connection);
    }
}

注意:

//获取文件输入流的方式有很多,此处的使用类加载器调用getResourcesAsStream的方式获取输入流的方式的优势是:会自动去工程的resources目录下找配置文件

 

//读取数据 不管存的是什么类型,获取出来的都是字符串

  * PreparedStatement预编译SQL执行对象

    * SQL注入: 在用户输入值的地方,输入进了SQL语句导致原有SQL语句业务逻辑发生改变,最终造成不可估量的后果.
    * 通过预编译SQL执行对象如何解决了注入问题?
      将SQL语句编译的时间点从执行时编译提前到创建对象时编译, 在创建对象时编译好处是可以将SQL语句业务代码锁死, 不受用户输入内容影响.
    * 如果SQL语句中有变量则使用PreparedStatement(可以解决注入问题,还可以避免字符串拼接), 如果没有变量则使用Statement

  * 批量操作

    将多次和数据库的交互合并成一次交互,从而提高执行效率
    
public static void main(String[] args) {
        //获取连接对象
        try (Connection connection = DBUtils.getConnection()) {
            //创建多条SQL语句
            String sql1="insert into user values(null,'aaa','aaa')";
            String sql2="insert into user values(null,'bbb','bbb')";
            String sql3="insert into user values(null,'ccc','ccc')";

            //创建执行SQL语句的对象
            Statement statement = connection.createStatement();
            //添加批量操作
            statement.addBatch( sql1 );
            statement.addBatch( sql2 );
            statement.addBatch( sql3 );
            //执行批量操作
            statement.executeBatch();
            System.out.println("执行完成!");
        }catch(Exception e){
            e.printStackTrace();
        }
    }

注意:

  //每隔20次执行一次,防止内存溢出。因为加入批量操作的SQL语句都存在内存中

  if (i%20==0){

  //执行批量操作

    preparedStatement.executeBatch();

  }

  * 获取自增主键值

    注意:

    * ResultSet 对象

    * getGeneratedKeys() 方法

    * Statement.RETURN_GENERATED_KEYS 参数

     //获取连接
       try (Connection connection = DBUtils.getConnection()) {
           String sql="insert into user values (null,?,?)";
           //sql后面添加参数Statement.RETURN_GENERATED_KEYS    用于获取自增主键值
           PreparedStatement preparedStatement=connection.prepareStatement( sql, Statement.RETURN_GENERATED_KEYS );
           preparedStatement.setString( 1,username );
           preparedStatement.setString( 2,password );
           preparedStatement.executeUpdate();
           //获取装着自增主键值的结果集对象
           ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
           //让游标下移
           generatedKeys.next();
           int id = generatedKeys.getInt( 1 );
           System.out.println("用户id为:"+id);

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

  * 元数据

    包括数据库相关元数据和表相关元数据 , 数据库元数据指和数据库相关的一些信息.

    注意:

      * 获取表元数据之前先得查询表

        try (Connection connection = DBUtils.getConnection()) {
            //获取数据库元数据对象   里面保存着与数据库有关的信息
            DatabaseMetaData databaseMetaData=connection.getMetaData();
            System.out.println("数据库驱动名:"+databaseMetaData.getDriverName());
            System.out.println("数据库名"+databaseMetaData.getDatabaseProductName());
            System.out.println("数据库连接地址"+databaseMetaData.getURL());
            System.out.println("数据库驱动版本"+databaseMetaData.getDriverVersion());


            //获取表元数据之前先得查询表
            String sql="select * from user";
            Statement statement=connection.createStatement();
            ResultSet resultSet = statement.executeQuery( sql );

            //获取表的元数据对象
            ResultSetMetaData resultSetMetaData=resultSet.getMetaData();
            //获取表字段数量
            int count=resultSetMetaData.getColumnCount();
            //遍历每个字段信息
            for (int i = 1; i <=count; i++) {
                //获取字段名
                String columnName = resultSetMetaData.getColumnName( i );
                //获取字段类型
                String columnTypeName = resultSetMetaData.getColumnTypeName( i );
                System.out.println(columnName+":"+columnTypeName);
            }
        }catch(Exception e){
            e.printStackTrace();
        }

 

posted @ 2021-05-10 12:29  我挺菜  阅读(86)  评论(0)    收藏  举报