在我的情况下,我正在查询数据库以获取特定的回复(在这种情况下,基于用户名的注册信息)。
//Build SQL String and Query Database.
if(formValid){
try {
SQL = "SELECT * FROM users WHERE username=? AND email=?";
Collections.addAll(fields, username, email);
results = services.DataService.getData(SQL, fields);
if (!results.next()){
errMessages.add("User account not found.");
} else {
user = new User();
user.fillUser(results); //Is it ok to pass ResultSet Around?
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
services.DataService.closeDataObjects(); //Does this close the ResultSet I passed to fillUser?
}
}
因此,一旦我查询数据库,如果找到结果,我创建一个新的User对象并用我从数据库收到的数据填充它。我曾经在我将结果集拉入的方法中直接执行所有这些操作,但我意识到我在整个项目中都进行了大量的冗余编码,因此我将其全部移动到一个存在于实际User bean中的中心方法中。
public void fillUser(ResultSet data) throws SQLException{
setUserId(data.getInt("id"));
setFirstName(data.getString("first_name"));
setLastName(data.getString("last_name"));
setUsername(data.getString("username"));
setType(data.getString("type"));
setEmail(data.getString("email"));
}
我已经做了一些测试,并且从我可以确定的,因为我在查询的finally块中关闭原始结果集,我传递给fillUser方法的结果集也被关闭。或者我错了,我是否认真泄露数据?这实际上是我第二次传递结果集(因此它的两个实例),因为我用来查询我的数据库的块是
public static ResultSet getData(String SQL, ArrayList fields) throws SQLException {
try{
connection = Database.getConnection();
preparedStatement = connection.prepareStatement(SQL);
for(int i=0; i<fields.size(); i++){
Integer num = i + 1;
Object item = fields.get(i);
if(item instanceof String){
preparedStatement.setString(num, (String) item); //Array item is String.
} else if (item instanceof Integer){
preparedStatement.setInt(num, (Integer) item); //Array item is Integer.
}
}
resultSet = preparedStatement.executeQuery();
return resultSet;
}finally{
}
}
所有这些代码段都存在于不同的类中,并在整个项目中多次重复使用。可以像这样传递结果集,还是应该尝试其他方法?我的目标是减少代码冗余,但我不确定我是否以合法的方式处理它。
答案 0 :(得分:6)
从技术上讲,传递结果集是可以的,只要您没有序列化并将其传递给不同的JVM,并且您的JDBC连接和语句仍处于打开状态。
然而,拥有数据库访问层可能是一个更好的软件工程师和编程实践,它以Java编码方式返回结果集(在您的示例中为User
列表)。这样,你的代码会更清晰,你不必担心ResultSet是否已经打开,或者你必须将它滚动到顶部,你可以命名...
答案 1 :(得分:3)
正如我前面的每个人都说过传递结果集一个坏主意。如果您使用的是c3p0之类的连接池库,则可以安全地使用CachedRowSet及其实现CachedRowSetImpl。使用此功能,您可以关闭连接。它只会在需要时使用连接。这是来自java doc的片段:
CachedRowSet对象是一个断开连接的行集,这意味着它只是短暂地使用了与其数据源的连接。它在读取数据时连接到其数据源,以便用行填充自身,并在将更改传播回其底层数据源时再次连接。剩下的时间,CachedRowSet对象被断开连接,包括在修改其数据时。断开连接会使RowSet对象更加精简,因此更容易传递给另一个组件。例如,可以将断开连接的RowSet对象序列化并通过线路传递给瘦客户端,例如个人数字助理(PDA)。
以下是查询和返回ResultSet的代码段:
public ResultSet getContent(String queryStr) {
Connection conn = null;
Statement stmt = null;
ResultSet resultSet = null;
CachedRowSetImpl crs = null;
try {
Connection conn = dataSource.getConnection();
stmt = conn.createStatement();
resultSet = stmt.executeQuery(queryStr);
crs = new CachedRowSetImpl();
crs.populate(resultSet);
} catch (SQLException e) {
throw new IllegalStateException("Unable to execute query: " + queryStr, e);
}finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
LOGGER.error("Ignored", e);
}
}
return crs;
}
以下是使用c3p0创建数据源的代码段:
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("<driver class>"); //loads the jdbc driver
} catch (PropertyVetoException e) {
e.printStackTrace();
return;
}
cpds.setJdbcUrl("jdbc:<url>");
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
javax.sql.DataSource dataSource = cpds;