各位好,我在优化数据库操作模块,请问大家是如何优化的?说说各自的经验好么?谢谢!

iStringTheory 2003-10-16 05:45:35
先说说我的系统:
我把一些常用的数据库操作诸如返回ResultSet、执行查询等等操作封装到一个DataOpt类,类的部分代码如下:
///////////////////////////////////////////////////////////////////////
public class DataOpt {
/*** 私有变量声明 ***/
private ResultSet resultSet=null;
private Statement statement=null;
private Connection conn=null;
//----------------------------------------------------------------------
/**
* 在构造函数中连接数据库
* @param dbType 数据库类型
* @param userId 用户名
* @param userPwd 口令
*/
public DataOpt() {
try {
conn = ConnectionManager.getConnection();//从连接池中获取连接
ConnectionManager.getStats();
}
catch (Exception ex) {
System.err.println("不能连接数据库!" + ex.getMessage());
}
}

//----------------------------------------------------------------------
/**
* 执行返回记录集的查询
* @param sqlString SQL查询字符串
* @return 返回ResultSet对象
*/
public ResultSet executeQuery(String sqlString) {
statement = null;
resultSet = null;
try {
statement = conn.createStatement();
resultSet = statement.executeQuery(sqlString);
}
catch (SQLException ex) {
System.err.println("executeQuery()出错:" + ex.getMessage());
}
finally{
try {
if (statement != null) {
statement.close();
statement=null;
}
}
catch (SQLException ex1) {
System.err.println("executeQuery() error!");
}
}
return resultSet;
}
///////////////////////////////////////////////////////////////////////

我现在比较困扰的问题是,每次操作数据库都要实例化这个类,是不是很浪费资源?能否在一个class里面只实例化一次这个数据库操作类?如果不行,应该如何优化?
我看到有的代码里面将public ResultSet executeQuery(String sqlString) 写成public synchronized ResultSet executeQuery(String sqlString) ,请问有必要么?谢谢
...全文
109 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
wellsoon 2003-10-22
  • 打赏
  • 举报
回复
//直接返回ResultSet和把结果存放至ArrayList再返回,后者有何优点呢?

swinging(山不在高) 也说了:

When a Statement object is closed, its current ResultSet object, if one exists, is also closed.

所以除非在外部对数据读取处理了之后,才能够关闭数据库连接,
引用swinging(山不在高)的话:“我才想到你的关闭操作是在这个类外部操作的,”

这样操作起来也很不方便的。

我是看 jdon上面一篇文章的例子是用的ArrayList的方式来返回,

我在外部的操作使用非常方便,就可以完全不关心数据库里的查询函数的具体实现了,
(请看看我顶楼的回复 )
也完全不用去关心数据库的连接和断开连接的问题,外部的操作,仅仅只提交一个SQL语句,然后就可以在返回的ArrayList对得到的结果进行读取操作了。
TonyTonyQ 2003-10-21
  • 打赏
  • 举报
回复
to wellsoon(wellsoon)
请教:
直接返回ResultSet和把结果存放至ArrayList再返回,后者有何优点呢?
xmqds 2003-10-21
  • 打赏
  • 举报
回复
采用连接池
最好不要用JDBC-ODBC桥!
wellsoon 2003-10-21
  • 打赏
  • 举报
回复
我改的别人的:

public java.util.ArrayList executeQuery(String sqlQueryStmt) {
System.out.println("SQL String is " + sqlQueryStmt);
java.util.ArrayList rows = new java.util.ArrayList();
java.sql.Connection conn = null;
java.sql.Statement stmt = null;
java.sql.ResultSet rs = null;
try {
conn = this.getConnection(10000);
stmt = conn.createStatement();
rs = stmt.executeQuery(sqlQueryStmt);
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
HashMap row = new HashMap();
for (int i = 1; i <= columnCount; i++) {
String cname = rsmd.getColumnName(i);
row.put(cname, rs.getObject(i));
}
rows.add(row);
}
return rows;
}
catch (Exception e) {
System.out.println("查询数据库出错,SQL语句为:" + sqlQueryStmt + "\n错误信息为:" +
e.getMessage());
return null;
}
finally {
try {
rs.close();
stmt.close();
//关闭连接,返回连接
this.freeConnection(conn);
conn = null;
}
catch (Exception e) {
System.out.println("释放连接出错,错误信息为:" + e.getMessage());
// return rows;
}
}
}

xiaohaiz 2003-10-21
  • 打赏
  • 举报
回复
优化只针对性能瓶颈做,先找到你的性能瓶颈。
TonyTonyQ 2003-10-21
  • 打赏
  • 举报
回复
有意思!
iStringTheory 2003-10-21
  • 打赏
  • 举报
回复
to swinging(山不在高)
连接肯定是关闭了的,由于使用了连接池,你没看到这个类有个dispose方法么?里面就是将用完的Connection重新放进连接池的,如下:
/**
* 清理对象
* @throws SQLException
*/
public void dispose() {
try {
ConnectionManager.returnConnection(conn);
/*System.out.println("成功关闭Statement对象并归还数据库连接!连接数:" +
ConnectionManager.getInstanceNum());*/
ConnectionManager.getStats();
}
catch (SQLException ex) {
System.err.println("连接池错误!" + ex.getMessage());
}
}

iStringTheory 2003-10-21
  • 打赏
  • 举报
回复
非常感谢各位的关注,尤其是 swinging(山不在高) 朋友提出了很多中肯的意见(我已经给您发了短消息,不知收到否?),今天5:30前结贴!谢谢!
swinging 2003-10-21
  • 打赏
  • 举报
回复
唉,我还以为我眼睛出问题了。

再次查找了下,你的public void dispose()方法没有在类中用到,
我才想到你的关闭操作是在这个类外部操作的,
如果是那样,那么你这个类只能算一个工具类,实现一些通用的操作方法而已,
外部需要重新包装DAO。

就你实现的共用的方法来说:
第一、使用STATEMENT,效率很低,不是说编程效率,而是实际数据库访问对SQL的解析和执行,ORACLE的对SQL语句的执行机制决定了的,应该使用PREPAREDSTATEMENT,特别是带参数的语句,不论带的参数是int还是string还是其它什么的。(我只熟悉ORACLE,所以我也就ORACLE说,其它数据库不了解具体ORACLE执行SQL的过程说起来很长,就不细解释了)
第二、看到的一个execProc(String procName, String params)方法使用参数传递,当时参数传递的方式很差,居然用一个STRING,然后在方法中分析STRING来处理,绝对是下下策。而且只支持STRING和INT的设置,还没有返回值,可用性很低,至少要有返回值处理,因为数据库端可能会有业务逻辑错误通过返回值返回,而具有返回操作结果记录的存储过程实在经常用。
第三、异常都被CATCH了,而且有些还什么处理都不作,实在很糟糕这样做。
第四、setClob方法,该方法不管三七二十一,就在处理完成后把AUTOCOMMIT设置为TRUE,
如果外部正在进行事务处理怎么办?(这里,原来我以为你是准备在外部控制事务的,现在看来我的想法有点错误,估计对于事务,你会重新new出CONNECTION来处理)
第五、一些小细节,比如在方法的开头有:
statement = null;
resultSet = null;
这么做基本上没有什么意义,因为你在下面直接就又给相应变量重新赋值了,所以这么做没有必要,可以说完全是白做。

最后,我还是不理解:
public ResultSet executeQuery(String sqlString)
这个方法真的可以返回一个可以使用的ResultSet吗?当然,看样子你早用过了,估计是可以,不过JDK API中对RESULTSET和STATEMENT的说明中,明确指出:
When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
不知道你用的是什么JDBC,你依赖的是具体JDBC的不规范实现,这样做感觉很不好。
swinging 2003-10-20
  • 打赏
  • 举报
回复
另外指出的是:每次操作数据库都要实例化这个类,是不是很浪费资源?
这个问题,
每次操作都实例化并不浪费资源,
而且这是必须的,因为有并发访问存在。

你知道访问数据库中最大的资源是什么吗?是数据库连接,connection
你不关闭连接,才是真正的资源浪费,你指望垃圾收集帮你收集?
那是不可能的,先不说垃圾收集什么时候开始不确定,就是收集了,也不能替你关闭连接。
如果你使用finalize()方法来关闭,结果是一样的,
因为垃圾收集并不是那么及时的,所以放在finalize()中只能说比你完全不CLOSE好那么一点,不过也就半斤八两。
swinging 2003-10-20
  • 打赏
  • 举报
回复
to 楼主,不是我说你,
整个类我没有看到CLOSE CONNECTION的语句,
单凭这一点,
这个类就没有价值。

具体就不细讲了,可以看下这个帖子的讨论:
http://expert.csdn.net/Expert/topic/2336/2336848.xml?temp=.6394464
里面有我的观点。
iStringTheory 2003-10-18
  • 打赏
  • 举报
回复
没人了?
iStringTheory 2003-10-17
  • 打赏
  • 举报
回复
swinging(山不在高) :
谢谢您的回复!
首先说明,这里的代码只是部分代码。然后说明一下,这个类工作正常。
我贴一下这个类的完整代码,大家再帮忙看看,谢谢!
package com.strong.data;

import java.io.*;
import java.sql.*;
import java.util.Vector;
import java.io.*;
import com.strong.utils.*;

/**
* <p>Title: 数据库操作类</p>
* <p>Description: 数据库操作函数</p>
* <p>Copyright: Copyright (c) 2003</p>
* <p>Company: 江西思创数码科技公司</p>
* @author 姜晓东
* @version 1.0
*/
public class DataOpt {
/*** 私有变量声明 ***/
private ResultSet resultSet=null;
private Statement statement=null;
private Connection conn=null;
//----------------------------------------------------------------------
/**
* 在构造函数中连接数据库
* @param dbType 数据库类型
* @param userId 用户名
* @param userPwd 口令
*/
public DataOpt() {
try {
conn = ConnectionManager.getConnection();
ConnectionManager.getStats();
}
catch (Exception ex) {
System.err.println("不能连接数据库!" + ex.getMessage());
}
}

//----------------------------------------------------------------------
/**
* 执行返回记录集的查询
* @param sqlString SQL查询字符串
* @return 返回ResultSet对象
*/
public ResultSet executeQuery(String sqlString) {
statement = null;
resultSet = null;
try {
statement = conn.createStatement();
resultSet = statement.executeQuery(sqlString);
}
catch (SQLException ex) {
System.err.println("executeQuery()出错:" + ex.getMessage());
}
finally{
try {
if (statement != null) {
statement.close();
statement=null;
}
}
catch (SQLException ex1) {
System.err.println("executeQuery() error!");
}
}
return resultSet;
}

//----------------------------------------------------------------------
/**
* 执行插入、更新、删除查询
* @param sqlString
*/
public void executeUpdate(String sqlString) {
statement = null;
try {
statement = conn.createStatement();
statement.executeUpdate(sqlString);
}
catch (SQLException ex) {
System.err.println("executeUpdate()出错" + ex.getMessage());
}
finally{
try {
if (statement != null) {
statement.close();
statement=null;
}
}
catch (SQLException ex1) {
System.err.print("DataOpt.executeUpdate() error!\n"+ex1.getMessage());
}
}
}

/**
* 向CLOB写入数据
* @param querySql CLOB字段插入empty_clob()
* @param tableName
* @param keyId
* @param clobField
* @param validateField
* @param validateValue
* @throws SQLException
* @throws IOException
*/
public void setClob(String querySql, String tableName, String keyId,
String clobField, String clobData, String validateField,
String validateValue) throws SQLException, IOException {
try {
resultSet = null;
conn.setAutoCommit(false);
this.executeUpdate(querySql); //执行插入操作,CLOB字段插入empty_clob()

resultSet = this.executeQuery("SELECT " + keyId + " FROM " + tableName +
" WHERE " + validateField + "='" +
validateValue + "'");
int curId = (resultSet.next()) ? resultSet.getInt(keyId) : 1;

resultSet = this.executeQuery("SELECT " + clobField + " FROM " +
tableName + " WHERE " + keyId + "=" + curId +
" FOR UPDATE");
oracle.sql.CLOB clobText = null;
clobText = (resultSet.next()) ?
(oracle.sql.CLOB) resultSet.getClob(clobField) : null;
Writer writer = clobText.getCharacterOutputStream();
writer.write(clobData);
writer.flush();
writer.close();
conn.commit();
}
catch (SQLException ex) {
System.out.println(ex.getMessage());
}
finally {
try {
resultSet.close();
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}
conn.setAutoCommit(true);
}
}

/**
* 读取CLOB字段数据
* @param tableName
* @param keyId
* @param clobField
* @param opId
* @return
*/
public String getClob(String tableName, String keyId, String clobField,
String opId) {
String returnString = "";
try {
StringBuffer querySql = new StringBuffer();
querySql.append( "SELECT " ).append( clobField ).append( " FROM " ).append( tableName ).append( " WHERE ") .append(
keyId ).append( "=" ).append( opId);
resultSet = this.executeQuery(querySql.toString());
if (resultSet.next()) {
oracle.jdbc.driver.OracleResultSet oracleResultSet =
(oracle.jdbc.driver.OracleResultSet) resultSet;
oracle.sql.CLOB clob = (oracle.sql.CLOB) oracleResultSet.getClob(1);

if (! (clob == null || clob.length() == 0)) {
returnString = clob.getSubString( (long) 1, (int) clob.length());
returnString = StringKit.unFilterHTML(returnString);
}
}
}
catch (SQLException ex) {
System.out.println(ex.getMessage());
}
finally{
try{
resultSet.close();
}catch(SQLException ex){}
}
return returnString;
}

/**
* 执行存储过程
* @param procName
* @param params
*/
public void execProc(String procName, String params) {
//构造命令字符串///////////////////////////////////////
String tmp[] = params.split(",");
StringBuffer cmd = new StringBuffer();
StringBuffer tmp2 = new StringBuffer();
StringBuffer tmp3 = new StringBuffer(); //类型
//String tmp3 = ""; //值
cmd.append("{call " + procName + "(");

for (int i = 0; i < tmp.length; i++) {
cmd .append( "?,");
/////////////////////////////////////////
String[] tmp1 = tmp[i].split("#");
tmp2 .append( tmp1[0] ).append( ",");
tmp3 .append( tmp1[1] ).append( ",");
}
cmd.substring(0, cmd.length() - 1);
cmd .append( ")}");
tmp2.toString().substring(0, tmp2.length() - 1);
tmp3.toString().substring(0, tmp3.length() - 1);
/////////////////////////////////////////////
String[] _tmp2 = tmp2.toString().split(","); //拆分为数组
String[] _tmp3 = tmp3.toString().split(",");
CallableStatement cstmt = null;
try {
cstmt = conn.prepareCall(cmd.toString());
for (int i = 0; i < _tmp2.length; i++) {
if (_tmp2[i].equals("int")) {
cstmt.setInt(i + 1, Integer.parseInt(_tmp3[i]));
}
else if (_tmp2[i].equals("var")) {
cstmt.setString(i + 1, _tmp3[i]);
}
}
cstmt.execute();
cstmt.close();
}
catch (SQLException ex) {}
catch (NumberFormatException ex) {}
}

//----------------------------------------------------------------------
/**
* 清理对象
* @throws SQLException
*/
public void dispose() {
try {
ConnectionManager.returnConnection(conn);
/*System.out.println("成功关闭Statement对象并归还数据库连接!连接数:" +
ConnectionManager.getInstanceNum());*/
ConnectionManager.getStats();
}
catch (SQLException ex) {
System.err.println("连接池错误!" + ex.getMessage());
}
}
}
iStringTheory 2003-10-17
  • 打赏
  • 举报
回复
caeserwliu(白云) :
是的!建立连接以及执行executeQuery需要很大的系统开销,请问DataSouce能带来什么好处呢?
caeserwliu 2003-10-17
  • 打赏
  • 举报
回复
重新实例化这个类不是问题,问题是建立连接时的开销,建议使用DataSource,而且最好是pool的,将DataSource放在服务器的环境中,让服务器来维护
iStringTheory 2003-10-17
  • 打赏
  • 举报
回复
zenzuguocsdn(软虫):
晕!您引述的是《JAVA与模式》P228页内容(附后),能否按您的理解进行说明呢?

附:
例子二
问:我的一个系统需要管理与数据库的连接。学习了单例模式后,我发现可以使用一
个单例类包装一个Connection 对象,并在finalize()方法中关闭这个Connection 对象。这样
的话,在这个单例类的实例没有被人引用时,这个finalize()对象就会被调用,因此,
Connection 对象就会被释放。这多妙啊。
答:这样做是不恰当的。除非有单一实例的需求,不然不要使用单例模式。在这里
Connection 对象可以同时有几个实例共存,不需要是单一实例。
单例模式有很多的错误使用案例都与此例子相似,它们都是试图使用单例模式管理共
享资源的生命周期,这是不恰当的。
zenzuguocsdn 2003-10-17
  • 打赏
  • 举报
回复
这样做是不恰当的。除非有单一实例的需求,不然不要使用单例模式。在这里
Connection 对象可以同时有几个实例共存,不需要是单一实例。
iStringTheory 2003-10-17
  • 打赏
  • 举报
回复
songbo_pp(皮皮) :
您好!
非常感谢您的回复!

希望大家讨论一下这里能不能、该不该使用单例模式?谢谢!
beming 2003-10-17
  • 打赏
  • 举报
回复
对哦...

gz
songbo_pp 2003-10-17
  • 打赏
  • 举报
回复
1.你的方法都不是线程安全的,在多线程下容易爆发空指针异常。

2.可以用单例模式来设计你的数据访问类,以确保任何地方调用只有一个实例。

public class DataOpt {

private static DataOpt instance = null;

private DataOpt(){
...
}

public static DataOpt getIntance(){
if (instance == null){
instance = new DataOpt();
}
return instance();
}

...

}
加载更多回复(5)

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧