• JDBC基础(二)


    前面说是,注册驱动程序有多方法,Class.forName();是一种显式地加载.当一个驱
    动程序类被Classloader装载后,在溶解的过程中,DriverManager会注册这个驱动类的实例.
    这个调用是自动发生的,也就是说DriverManager.registerDriver()方法被自动调用了,当然
    我们也可以直接调用DriverManager.registerDriver()来注册驱动程序,但是,以我的经验.
    MS的浏览中APPLET在调用这个方法时不能成功,也就是说MS在浏览器中内置的JVM对该方法的
    实现是无效的.
        另外我们还可以利用系统属性jdbc.drivers来加载多个驱动程序:
    System.setProperty(\"jdbc.drivers\",\"driver1:driver2:.....:drivern\");多个驱动程序之
    间用\":\"隔开,这样在连结时JDBC会按顺序搜索,直到找到第一个能成功连结指定的URL的驱动
    程序.
        在基础篇里我们先不介绍DataSource这些高级特性.

        在成功注册驱动程序后,我们就可以用DriverManager的静态方法getConnection来得
    到和数据库连结的引用:
        Connection conn = DriverManager.getConnection(url);
        如果连结是成功的,则返回Connection对象conn,如果为null或抛出异常,则说明没有
    和数据库建立连结.
        对于getConnection()方法有三个重载的方法,一种是最简单的只给出数据源即:
    getConnection(url),另一种是同时给出一些数据源信息即getConnection(url,Properties),
    另外一种就是给出数据源,用户名和密码:getConnection(url,user,passwod),对于数据源信息.
    如果我们想在连结时给出更多的信息可以把这些信息压入到一个Properties,当然可以直接压
    入用户名密码,别外还可以压入指定字符集,编码方式或默认操作等一些其它信息.
        
        在得到一个连结后,也就是有了和数据库找交道的通道.我们就可以做我们想要的操
    作了.
        还是先来介绍一些一般性的操作:
        如果我们要对数据库中的表进行操作,要先缘故绑定一个语句:
        Statement stmt = conn.createStatement();
        然后利用这个语句来执行操作.根本操作目的,可以有两种结果返回,如果执行的查询
    操作,返回为结果集ResultSet,如果执行更新操作,则返回操作的记录数int.
        注意,SQL操作严格区分只有两个,一种就是读操作(查询操作),另一种就是写操作(更
    新操作),所以,create,insert,update,drop,delete等对数据有改写行为的操作都是更新操作.

        ResultSet rs = stmt.executeQuery(\"select * from table where xxxxx\");
        int x = stmt.executeUpdate(\"delete from table where ......\");
        如果你硬要用executeQuery执行一个更新操作是可以的,但不要把它赋给一个句柄,
    当然稍微有些经验的程序员是不会这么做的.
        至于对结果集的处理,我们放在下一节讨论,因为它是可操作的可选项,只有查询操作
    才返回结果集,对于一次操作过程的完成,一个非常必要的步骤是关闭数据库连结,在你没有了
    解更多的JDBC知识这前,你先把这一步骤作为JDBC操作中最最重要的一步,在以后的介绍中我会
    不断地提醒你去关闭数据库连结!!!!!!!!!!!

        按上面介绍的步骤,一个完成的例子是这样的:(注意,为了按上面的步骤介绍,这个例
    子不是最好的)
        try{
            Class.forName(\"org.gjt.mm.mysql.Driver\");
        }catch(Exception e){
            System.out.println(\"没有成功加载驱动程序:\"+e.toString());
            return;
        }//对于象我这样的经验,可以直接从e.toString()的简单的几个字判断出异常原因,
         //如果你是一个新手应该选捕获它的子类,如何知道要捕获哪几个异常呢?一个简单
         //的方法就是先不加try{},直接Class.forName(\"org.gjt.mm.mysql.Driver\");,编
         //译器就会告诉你要你捕获哪几个异常了,当然这是偷机取巧的方法,最好还是自己
         //去看JDK文档,它会告诉你每个方法有哪些异常要你捕获.
        Connection conn = null;
        try{
            conn = DriverManager.getConnection(
                            \"jdbc:mysql://host:3306/mysql\",
                            \"user\",
                            \"passwd\");
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(\"select * from table\");
            //rs 处理
            [rs.close();]
            [stmt.close();]
        }
        catch(Exception e){
            System.out.println(\"数据库操作出现异常:\"+e.toString());
        }
        finally{
            try{conn.close();}catch(Exception){}
        }//不管你以前是学习到的关于数据库流程是如何操作的,如果你相信我,从现在开始,
         //请你一定要把数据库关闭的代码写到finally块中,切切! 

    关于Statement对象:
        前面说过,Statement对象是用来绑定要执行的操作的,在它上面有三种执行方法:
    即用来执行查询操作的executeQuery(),用来执行更新操作的executeUpdate()和用来执行
    动态的未知的操作的execute().
        JDBC在编译时并不对要执行的SQL语句检测,只是把它看着一个String,只有在驱动
    程序执行SQL语句时才知道正确与否.
        一个Statement对象同时只能有一个结果集在活动.这是宽容性的,就是说即使没有
    调用ResultSet的close()方法,只要打开第二个结果集就隐含着对上一个结果集的关闭.所以
    如果你想同时对多个结果集操作,就要创建多个Statement对象,如果不需要同时操作,那么可
    以在一个Statement对象上须序操作多个结果集.
        
        这里我不得不特别说明一下,很多人会用一个Statement进行嵌套查询,然后就来问
    我说为什么不能循环?道理上面已经说清楚了.我们来详细分析一下嵌套查询:
        Connection conn = null;
        Statement stmt = null;
        conn = .......;
        stmt = conm.createStatement(xxxxxx);
        ResultSet rs = stmt.executeQuery(sql1);
        while(rs.next()){
            str = rs.getString(xxxxx);
            ResultSet rs1 = stmt.executeQuery(\"select * from 表 where 字段=str\");
        }
    当stmt.executeQuery(\"select * from 表 where 字段=str\");赋给rs1时,这时隐含的操作
    是已经关闭了rs,你还能循环下去吗?
    所以如果要同时操作多个结果集一定要让它他绑定到不同的Statement对象上.好在一个connection
    对象可以创建任意多个Statement对象,而不需要你重新获取连结.

        关于获取和设置Statement的选项:只要看看它的getXXX方法和setXXX方法就明白了,这儿
    作为基础知识只提一下以下几个:
        setQueryTimeout,设置一个SQL执行的超时限制.
        setMaxRows,设置结果集能容纳的行数.
        setEscapeProcessing,如果参数为true,则驱动程序在把SQL语句发给数据库前进行转义替
    换,否则让数据库自己处理,当然这些默认值都可以通过get方法查询.

        Statement的两个子类:
        PreparedStatement:对于同一条语句的多次执行,Statement每次都要把SQL语句发送给数据
    库,这样做效率明显不高,而如果数据库支持预编译,PreparedStatement可以先把要执行的语句一次发
    给它,然后每次执行而不必发送相同的语句,效率当然提高,当然如果数据库不支持预编译,
    PreparedStatement会象Statement一样工作,只是效率不高而不需要用户工手干预.
        另外PreparedStatement还支持接收参数.在预编译后只要传输不同的参数就可以执行,大大
    提高了性能.
            
        PreparedStatement ps = conn.prepareStatement(\"select * from 表 where 字段=?\");
        ps.setString(1,参数);
        ResultSet rs = ps.executeQuery();
        
        CallableStatement:是PreparedStatement的子类,它只是用来执行存储过程的.
        CallableStatement sc = conn.prepareCall(\"{call query()}\");
        ResultSet rs = cs.executeQuery();
        
        关于更高级的知识我们在JDBC高级应用中介绍. 

    作为基础知识的最后部分,我们来说一说结果集的处理,当然是说对一般结果集的处理.
    至于存储过程返回的多结果集,我们仍然放在高级应用中介绍.
        SQL语句如何执行的是查询操作,那就要返回一个ResultSet对象,要想把查询结果最后
    明白地显示给用户,必须对ResultSet进行处理.ResultSet返回的是一个表中符合条件的记录,对
    ResultSet的处理要逐行处理,而对于每一行的列的处理,则可以按任意顺序(注意,这只是JDBC规
    范的要求,有些JDBC实现时对于列的处理仍然要求用户按顺序处理,但这是极少数的).事实上,虽
    然你可以在处理列的时候可以按任意顺序,但如果你按从左到右的顺序则可以得到较高的性能.

        这儿从底层来讲解一下ResultSet对象,在任何介绍JDBC的书上你是不会获得这样的知
    识的,因为那是数据库厂商的事.ResultSet对象实际维护的是一个二维指针,第一维是指向当前
    行,最初它指向的是结果集的第一行之前,所以如果要访问第一行,就要先next(),以后每一行都
    要先next()才能访问,然后第二维的指针指向列,只要当你去rs.getXXX(列)时,才通过
    Connection再去数据库把真实的数据取出来,否则没有什么机器能真的把要取的数据都放在内
    存中.
        所以,千万要记住,如果Connection已经关闭,那是不可能再从ResultSet中取到数据的.
    有很多人问我,我可不可以取到一个ResultSet把它写到Session中然后关闭Connection,这样就
    不要每次都连结了.我只能告诉你,你的想法非常好,但,是错误的!当然在javax.sql包中JDBC高
    级应用中有CacheRow和WebCacheRow可以把结果集缓存下来,但那和我们自己开一个数据结构把
    ResultSet的行集中所有值一次取出来保存起来没有什么两样.
        访问行中的列,可以按字段名或索引来访问.下面是一个简单的检索结果的程序:

        ResultSet rs = stmt.executeQuery(\"select a1,a2,a3 from table\");
        while(rs.next()){
            int i = rs.getInt(1);
            String a = rs.getString(\"a2\");
            ..............
        }

        对于用来显示的结果集,用while来进行next()是最普通的,如果next()返回false,则
    说明已经没有可用的行了.但有时我们可能连一行都没有,而如果有记录又不知道是多少行,这时
    如果要对有记录和没有记录进行不同的处理,应该用以下流程进行判断:

        if(rs.next()){
            //因为已经先next()了,所经对记录应该用do{}while();来处理
            do{
                int i = rs.getInt(1);
                String a = rs.getString(\"a2\");
            }while(rs.next());
        }
        esle{
            System.out.println(\"没有取得符合条件的记录!\");
        }

        类型转换:
        ResultSet的getXXX方法将努力把结果集中的SQL数据类型转换为JAVA的数据类型,事实
    大多数类型是可以转换的,但仍然有不少糊弄是不能转换的,如你不能将一个SQL的float转换成
    JAVA的DATE,你无法将 VARCHAR \"我们\"转换成JAVA的Int.

        较大的值:
        对于大于Statement中getMaxFieldSize返回值的值,用普通的getBytes()或getString()
    是不能读取的,好在JAVA提供了读取输入浪的方法,对于大对象,我们可以通过rs.getXXXStream()
    来得到一个InputStream,XXX的类型包括Ascii,Binay,Unicode.根据你存储的字段类型来使用不
    同的流类型,一般来说,二进制文件用getBinayStream(),文本文件用getAsciiStyream(),对于
    Unicode字符的文本文件用getUnicodeStream(),相对应的数据库字段类型应该为:Blob,Clob和
    Nlob.

        获取结果集的信息:
        大多数情况下编程人员对数据库结构是了解的,可以知道结果集中各列的情况,但有时并
    不知道结果集中有哪些列,是什么类型.这时可以通过getMetaData()来获取结果集的情况:

        ResulSetMetaData rsmd = rs.getMetaData();
        rsmd.getColumnCount()返回列的个数.
        getColumnLabel(int)返回该int所对应的列的显示标题
        getColumnName(int)返回该int所对应的列的在数据库中的名称.
        getColumnType(int)返回该int所对应的列的在数据库中的数据类型.
        getColumnTypeName(int)返回该int所对应的列的数据类型在数据源中的名称.
        isReadOnly(int)返回该int所对应的列是否只读.
        isNullable(int)返回该int所对应的列是否可以为空
     JDBC初级应用实例(二)[动态访问数据库]

        上面有一位朋友问了,如果在已经连结的情况下,知道当前连结的库的表的情况呢?
    其实只你已经连结了,你就能知道这个库中所以情况而不仅仅上表的情况:

        有时(我到目前只见到过一次),我们对一种新的数据库根本不知道它的结构或者是
    其中的内容,好坏么我们如何来获取数据库的情况呢?
        真实的例子是这样的,我的朋友的公司接到了一个单子,对方使用的数据库是叫什么
    \"titanium\"的,说实话由于本人的孤陋寡闻,在此之前从来不知道还有这种数据库,更别说如何
    访问了,现在朋友要看里面有什么\"东西\",当然是一筹莫展.所以只好找我.
        接到电话后,我先问他是什么平台上跑的,如果连结的,他说是在windows下可以建立
    ODBC数据源,哈哈,就是说可以用java建立Connection了,OK
        只能建立一下Connection,那么就可以得到这个数据库的所有元信息:
        DatabaseMetadata dbmd = conn.getMetadata();然后你可以从这个对象获取以下信
    息:
        getUrl();      //返回与这个数据库的连结的URL,当然是已知的,要不你怎么连上去
        getUserName(); //返回与这个数据库的连结的用户,同上
        isReadOnly();数据库是否为只读
        getDatabaseProduceName();//数据库产品名称
        getDatabaseProduceVersion();//版本号
        getDriverName();//驱动程序
        getDriverVersion();//驱动程序版本

        以上内容没有什么意义

        ResultSet getTables(String catalog, 
                    String schemaPattern,
                    String tableNamePattern,
                    String[] types) 
        可以得到该库中\"表\"的所有情况,这里的表包括表,视图,系统表,临时空间,别名,同义词
        对于各参数:
        String catalog,表的目录,可能为null,\"null\"匹配所有
        String schemaPattern,表的大纲,同上
        String tableNamePattern,表名,同上
        String[] types,表的类型,\"null\"匹配所有,可用的类型为:
        TABLE,VIEW,SYSEM TABLE,GLOBAL TEMPORARY,LOCAL  TEMPORARY,ALIAS,SYNONYM

        例如:
            DatabaseMetaData dbmd = conn.getMetaData();
            ResultSet rs = dbmd.getTables(null,null,null,null);
            ResultSetMetaData rsmd = rs.getMetaData();
            int j = rsmd.getColumnCount();
            for(int i=1;i<=j;i++){
                out.print(rsmd.getColumnLabel(i)+\"\\t\");
            }
            out.println();
            while(rs.next()){
                for(int i=1;i<=j;i++){
                    out.print(rs.getString(i)+\"\\t\");
                }
                out.println();
            }
        对于更详细的表中的列的信息,可以用dbmd(不是rsmd).getColumns(
                String catalog,
                String schemaPattern,
                String tableNamePattern,
                String columnNamePattern
            )
        不仅可以获得rsmd中的信息,还可以获得列的大小,小数位数,精度,缺省值,列在表中
    的位置等相关信息.
        还有两个方法,调用和获取表信息一样,可以获得存储过程和索引的信息:
        ResultSet getProcedures(
                String catalog,
                String schemaPattern,
                String procedurePattern
            );
        ResultSet getIndexINFO(
                String catalog,
                String schemaPattern,
                String table,
                boolean unique,boolean approximate
            ); 
  • 相关阅读:
    QButton
    注入
    SpringBoot热重启配置
    centos7 安装 tomcat
    centos 安装jdk
    spring boot (6) AOP的使用
    spring boot (5) 自定义配置
    spring boot (4) 使用log4 打印日志
    SpringBoot (3)设置支持跨域请求
    spring boot (2) 配置swagger2核心配置 docket
  • 原文地址:https://www.cnblogs.com/zhuor/p/270750.html
Copyright © 2020-2023  润新知