Java之路(5)--差不多该开始操作数据库了吧

来源:互联网 发布:淘宝刀具怎么快递的 编辑:程序博客网 时间:2024/04/29 17:57

笑着 胖胖兰原创,转载请注明。

    bluesmile979@hotmail.com

 http://blog.csdn.net/bluesmile979/archive/2008/10/21/3118083.aspx

    前面几篇文章讲的东西的理解消化,加上对应的基础内容学习,如果没有基础的话大概需要3个月左右。心急吃不了热豆腐。不要着急。如果前面的都学好了。那么差不多要接触一个比较重要的东西--数据库操作了。笑着在这一篇文章里要谈的,是关于数据库操作的一些注意事项。

 

   首先来看一下Java中最基本的数据库连结程序。

    Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();  

String url="jdbc:oracle:thin:@localhost:1521:dbName";

String user="bluesmile";

String password="xiaozhe";

Connection conn= DriverManager.getConnection(url,user,password);  

Statement stmt=conn.createStatement(***,***);  

String sql="select * from test";  

ResultSet rs=stmt.executeQuery(sql);  

while(rs.next()) {  

      String s = rs.getString(1);

      System.out.println("您取到的数据为:" + s);

}    

rs.close();  

stmt.close();  

conn.close();  

 

当然,如果就用这样的程序来做数据库连结操作,那时有很多问题的。这里我们只是通过这一段程序来看一下数据库操作的基本流程。具体使用这样的一段程序有什么问题,就是笑着这一篇文章要谈的。

 

那么先来看一下数据库操作的基本流程。

1. 取得数据库连接对象Connection

2. 取得SQL语句执行对象

3. 创建SQL语句并执行

4. 从返回结果ResultSet中取得需要的数据

5. 关闭被打开的资源。

以上,是数据库操作的最最基本的流程,最少最少的,我们也要实现上面流程中的操作。问题,也是就是从如何实现这些基本流程开始的。

 

1. 关于如何取得数据库连结对象Connection

a.      关于针对连接对象的参数设定

通常我们采用的方法是DriverManager.getConnection(url,user,password);来取得Connection对象。在这里很多参数都是采用的默认的。比如oracle数据库对应有两个对性能影响比较大的参数defaultRowPreFetchdefaultBatchValue,前一个设定每次从数据库读取的数据条数,后一个用来设定SQL执行对象Statement的批处理执行语句条数。这两参数的默认值笑着记得都是1,这无疑增加了我们跟数据库打交道的次数。所以明确设定比较好。针对Connection的设定根据数据库不同会有所不同,具体可以取研究对应的实现了Connection接口的实际针对该数据库的连接对象,比如Oracle对应的OraclConnection(记不清楚了,好像叫做这个名字)。不同的数据库提供的JDBC Driver里面都会有这样的具体的Connection实现类的。这些具体的实现类会提供参数的设定接口。这里只能够提个醒,具体哪些参数,如何设定,需要的时候要心里有数,自己去研究一下。

 

b.      关于数据库连结池

创建Connection对象一般来说开销是比较大的。每次连接都创建新的对象显然不是一个很好的做法。幸运的是大多数数据库应用服务器都提供了数据库连结池。笑着在这里要强调的是,我们可能没有必要去做连结池的具体操作,设定。但是我们必须要明白它的存在,我们的脑海里面要有连结池,缓冲池这样的概念,也许大多数情况下,数据库连结池不要要我们做什么,甚至不需要我们知道他的存在,但是,实际程序中有很多影响效率的地方,池,也许会帮你解决一些问题。

所谓池的策略,简单来说,当你请求一个资源,池会看看池中有没有资源,有的话把这个资源分发给请求,没有的话,生成新的资源同时把该资源放到池中。当请求归还的时候,池会判断池中是否满了,如果满了,那么释放该资源。其中关键的,就是生成新资源,判断是否释放资源,以及如何维护池中资源的策略,算法。

 

c.       关于设定AutoCommit

Java里面数据库的AutoCommit的默认设定是true。什么意思呢?就是我每执行一个SQL语句就会向数据库提交一次,数据库就要实际产生一次动作,这比批量的向数据库提交SQL语句的效率要差一些。尤其在批量处理insert,update语句的时候。效率差别还是很明显的。而且,你无法在某一句sql语句失败的时候rollback,所以,要适当考虑设定autocommitfalse,自己主动在适当的时候commit。因为Java中默认为true,可能很多人会忘记考虑这个事情。至少,为false的时候你的数据在你明确commit之前不会被保存到数据库中,那么要不要自动commit至少我们不会忘。不知道Java里面为什么默认值设定为了true了。

 

2. 关于SQL语句执行对象

Java中存在Statement,PreparedStatement,CallableStatement三种SQL语句连接对象,一般来说,比较常用的是PreparedStatement,下面简单说说他们的区别。

 

来看一下最简单的SQL语句,select * from table where a=b

 

大家考虑一下,数据库查询的时候,数据库软件要如何判断我们要做什么呢。

恩,是的,数据库软件需要解析我们传给他的这个SQL语句的字符串,看看select后面是什么,from后面是什么,where 后面是什么。就是说数据库软件要解析Key:Value。那么这个解析过程是要花时间的,越复杂的SQL语句需要花费的时间就越多。

 

是的,这就是StatementPreparedStatement之间的区别,Statement要针对我们传入的每一条SQL语句进行解析,而PreparedStatement采用?这个符号来匹配变化的值,那么当inserupdateSQL语句中大部分内容相同的时候,采用?这个符号来匹配少量变化的值,数据库软件就只需要对这个SQL语句解析一次,然后等待接受PreparedStatement后续传入的可变值部分。所以,我们常用的是PreparedStatement

 

至于CallableStatement,是调用存储过程的,需要在数据库服务期端的存储过程相配合。因为使用了服务期端的存储过程,那么效率自然就高一些。笑着没用过,不好说什么。但是有一点,存储过程执行本身,因为它就是在数据库服务器端运行,数据库对其也有相当的优化,本身执行速度那肯定是快的。但是Java调用存储过程,这个调用过程本身也是要花时间的。所以到底快不快是不一定的。

另外,是用SQL执行对象的addBatch,executBatch方法回比直接使用executeQuery的效率好一些。

附:常用用法代示例

a.      Statement

Stmt.addBatch(“insert…..”);

Stmt.addBatch(“update….”);

Stmt.executeBatch();

b.      PreparedStatement

               pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ?);
                 pstmt.setString(1,”a”);
                 pstmt.addBatch();
                 pstmt.setString(1,”b”);
                 pstmt.addBatch();
                 pstmt.executeBatch();

3. 关于创建的SQL语句

SQL语句本身是支持相当多的逻辑判断,数据筛选的,要适当的使用SQL语句本身的功能。比如Select * From table 然后再通过Java程序筛选需要的数据,那就不如直接使用Select * From table Where a=b让数据库来帮忙筛选。

 

基本原则,我们要减少跟数据库之间的通信量。这根IO是同样的道理,程序根数据库,IO打交道,无论是建立连接,还是数据通信,都是需要花比较多的时间的,所以我们要尽量减少这样的操作

 

4. 关于ResultSet

a.      参数设定,ResultSet里面有fetchSize,FetchDirection这样的参数,fetchSize参考Connection时候的说明,意思差不多。fecthDirection是设定游标运行方向的。简单来说就是数据结构中的链表,单向链表游标就只能一直向前,双向链表可以向前也可以向后移动,但是同时自然要增加一些控制用的东西,影响效率。也就是说没有必要的话就是用一直向前这种就可以了,这也是Java默认的设定。

 

b.      关于从ResultSet中取值

 

Java里提供两种方法,一种是使用字段位置Index作为参数,一种是使用字段名称ColName作为参数,那么自然是用Index会快一些,但是笑着测试这个效率影响不是很大,用Index不方便程序阅读。到底用什么办法还是大家自己根据具体情况判断吧。用Index的话注意把注释加加好。笑着还是倾向这种方法的。

 

5. 关闭资源。

本来关于关闭资源没啥好说的。但是看到网上大量的演示程序给出的代码稍微有些问题,所以还是提一下。基本原则就是你必须要保证你的资源被关闭,被释放掉了。本文开始的程序那样做的话,中间任何语句产生异常的话,这一份资源就不会被释放掉了,那么你的CPU占用率达到100%指日可待。正确用法如下,不多解释了。

try{

} catch (){

} finally{

    if(rs != null){

   try(){

    rs.close();

} catch() {

}

}

    // 以下略

}

6. 再说说基本原则

前面提到了一个基本原则,我们要减少跟数据库之间的通信量。这根IO是同样的道理,程序根数据库,IO打交道,无论是建立连接,还是数据通信,都是需要花比较多的时间的,所以我们要尽量减少这样的操作。

 

这里数据库也好,IO也好,为了减少建立连接,数据通信的次数的具体的办法。都是尽量把有用的,常用的数据保存在内存中,类似池,缓冲的概念,但是什么事情都有个度。过犹不及,用IO打个比方。一般属性文件内容比较少,程序中经常要使用,我们可以一次性的读取到内存中。但是并不一定所有的文件都适合一次性全都读到内存中。比如文件内容很多的时候。一方面内存开销太大了,空间方面我们也要考虑。而且过多的没有用的信息放在内存里,也是没有价值的。就是说虽然可以空间换时间,也要我们在掌握了基本原则的基础之上,根据具体情况来作出不同的判断。万事都没有绝对的。

 

7.