* Use explicit list of columns in select statement.

* Only care and know about columns that are mapped or used internally.
* Fixed bug in size() for grouped collections with loadmode=aggressive.
This commit is contained in:
hns 2003-05-22 11:08:04 +00:00
parent 1f38513443
commit 5c192c437a
3 changed files with 127 additions and 57 deletions

View file

@ -26,10 +26,16 @@ public final class DbColumn {
private final int type; private final int type;
private final Relation relation; private final Relation relation;
private final boolean isId;
private final boolean isPrototype;
private final boolean isName;
private final boolean isMapped;
/** /**
* Constructor * Constructor
*/ */
public DbColumn(String name, int type, Relation rel) { public DbColumn(String name, int type, Relation rel, DbMapping dbmap) {
this.name = name; this.name = name;
this.type = type; this.type = type;
this.relation = rel; this.relation = rel;
@ -37,6 +43,12 @@ public final class DbColumn {
if (relation != null) { if (relation != null) {
relation.setColumnType(type); relation.setColumnType(type);
} }
isId = name.equalsIgnoreCase(dbmap.getIDField());
isPrototype = name.equalsIgnoreCase(dbmap.getPrototypeField());
isName = name.equalsIgnoreCase(dbmap.getNameField());
isMapped = relation != null || isId || isPrototype || isName;
} }
/** /**
@ -59,4 +71,33 @@ public final class DbColumn {
public Relation getRelation() { public Relation getRelation() {
return relation; return relation;
} }
/**
* Returns true if this column serves as ID field for the prototype.
*/
public boolean isIdField() {
return isId;
}
/**
* Returns true if this column serves as prototype field for the prototype.
*/
public boolean isPrototypeField() {
return isPrototype;
}
/**
* Returns true if this column serves as name field for the prototype.
*/
public boolean isNameField() {
return isName;
}
/**
* Returns true if this field is mapped by the prototype's db mapping.
*/
public boolean isMapped() {
return isMapped;
}
} }

View file

@ -24,6 +24,7 @@ import java.sql.*;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -836,15 +837,19 @@ public final class DbMapping implements Updatable {
// ok, we have the meta data, now loop through mapping... // ok, we have the meta data, now loop through mapping...
int ncols = meta.getColumnCount(); int ncols = meta.getColumnCount();
ArrayList list = new ArrayList(ncols);
columns = new DbColumn[ncols];
for (int i = 0; i < ncols; i++) { for (int i = 0; i < ncols; i++) {
String colName = meta.getColumnName(i + 1); String colName = meta.getColumnName(i + 1);
Relation rel = columnNameToRelation(colName); Relation rel = columnNameToRelation(colName);
columns[i] = new DbColumn(colName, meta.getColumnType(i + 1), rel); DbColumn col = new DbColumn(colName, meta.getColumnType(i + 1), rel, this);
if (col.isMapped()) {
list.add(col);
}
} }
columns = new DbColumn[list.size()];
columns = (DbColumn[]) list.toArray(columns);
} }
return columns; return columns;
@ -862,6 +867,7 @@ public final class DbMapping implements Updatable {
*/ */
public DbColumn getColumn(String columnName) public DbColumn getColumn(String columnName)
throws ClassNotFoundException, SQLException { throws ClassNotFoundException, SQLException {
DbColumn col = (DbColumn) columnMap.get(columnName); DbColumn col = (DbColumn) columnMap.get(columnName);
if (col == null) { if (col == null) {
@ -879,10 +885,6 @@ public final class DbMapping implements Updatable {
} }
} }
if (col == null) {
throw new SQLException("Column " + columnName + " not found in " + this);
}
columnMap.put(columnName, col); columnMap.put(columnName, col);
} }
@ -890,12 +892,13 @@ public final class DbMapping implements Updatable {
} }
/** /**
* Get a StringBuffer initialized to the first part of the select statement
* for objects defined by this DbMapping
* *
* @return the StringBuffer containing the first part of the select query
* *
* @return ... * @throws SQLException if the table meta data could not be retrieved
* * @throws ClassNotFoundException if the JDBC driver class was not found
* @throws SQLException ...
* @throws ClassNotFoundException ...
*/ */
public StringBuffer getSelect() throws SQLException, ClassNotFoundException { public StringBuffer getSelect() throws SQLException, ClassNotFoundException {
String sel = selectString; String sel = selectString;
@ -904,7 +907,22 @@ public final class DbMapping implements Updatable {
return new StringBuffer(sel); return new StringBuffer(sel);
} }
StringBuffer s = new StringBuffer("SELECT * FROM "); StringBuffer s = new StringBuffer("SELECT ");
DbColumn[] cols = columns;
if (cols == null) {
cols = getColumns();
}
for (int i = 0; i < cols.length; i++) {
s.append(cols[i].getName());
if (i < cols.length-1) {
s.append(',');
}
}
s.append(" FROM ");
s.append(getTableName()); s.append(getTableName());
s.append(" "); s.append(" ");
@ -974,6 +992,11 @@ public final class DbMapping implements Updatable {
try { try {
DbColumn col = getColumn(columnName); DbColumn col = getColumn(columnName);
// This is not a mapped column. In case of doubt, add quotes.
if (col == null) {
return true;
}
switch (col.getType()) { switch (col.getType()) {
case Types.CHAR: case Types.CHAR:
case Types.VARCHAR: case Types.VARCHAR:

View file

@ -148,45 +148,42 @@ public final class Node implements INode, Serializable {
* Constructor used for nodes being stored in a relational database table. * Constructor used for nodes being stored in a relational database table.
*/ */
public Node(DbMapping dbm, ResultSet rs, DbColumn[] columns, WrappedNodeManager nmgr) public Node(DbMapping dbm, ResultSet rs, DbColumn[] columns, WrappedNodeManager nmgr)
throws SQLException, IOException { throws SQLException, IOException {
this.nmgr = nmgr; this.nmgr = nmgr;
// see what prototype/DbMapping this object should use // see what prototype/DbMapping this object should use
dbmap = dbm; dbmap = dbm;
String protoField = dbmap.getPrototypeField();
if (protoField != null) {
String protoName = rs.getString(protoField);
if (protoName != null) {
dbmap = nmgr.getDbMapping(protoName);
if (dbmap == null) {
// invalid prototype name!
System.err.println("Warning: Invalid prototype name: " + protoName +
" - using default");
dbmap = dbm;
}
}
}
setPrototype(dbmap.getTypeName());
id = rs.getString(dbmap.getIDField());
// checkWriteLock ();
String nameField = dbmap.getNameField();
name = (nameField == null) ? id : rs.getString(nameField);
if ((name == null) || (name.length() == 0)) {
name = dbmap.getTypeName() + " " + id;
}
created = lastmodified = System.currentTimeMillis(); created = lastmodified = System.currentTimeMillis();
for (int i = 0; i < columns.length; i++) { for (int i = 0; i < columns.length; i++) {
// set prototype?
if (columns[i].isPrototypeField()) {
String protoName = rs.getString(i+1);
if (protoName != null) {
dbmap = nmgr.getDbMapping(protoName);
if (dbmap == null) {
// invalid prototype name!
System.err.println("Warning: Invalid prototype name: " + protoName +
" - using default");
dbmap = dbm;
}
}
}
// set id?
if (columns[i].isIdField()) {
id = rs.getString(i+1);
}
// set name?
if (columns[i].isNameField()) {
name = rs.getString(i+1);
}
Relation rel = columns[i].getRelation(); Relation rel = columns[i].getRelation();
if ((rel == null) || if ((rel == null) ||
@ -199,7 +196,7 @@ public final class Node implements INode, Serializable {
switch (columns[i].getType()) { switch (columns[i].getType()) {
case Types.BIT: case Types.BIT:
newprop.setBooleanValue(rs.getBoolean(columns[i].getName())); newprop.setBooleanValue(rs.getBoolean(i+1));
break; break;
@ -207,21 +204,21 @@ public final class Node implements INode, Serializable {
case Types.BIGINT: case Types.BIGINT:
case Types.SMALLINT: case Types.SMALLINT:
case Types.INTEGER: case Types.INTEGER:
newprop.setIntegerValue(rs.getLong(columns[i].getName())); newprop.setIntegerValue(rs.getLong(i+1));
break; break;
case Types.REAL: case Types.REAL:
case Types.FLOAT: case Types.FLOAT:
case Types.DOUBLE: case Types.DOUBLE:
newprop.setFloatValue(rs.getDouble(columns[i].getName())); newprop.setFloatValue(rs.getDouble(i+1));
break; break;
case Types.DECIMAL: case Types.DECIMAL:
case Types.NUMERIC: case Types.NUMERIC:
BigDecimal num = rs.getBigDecimal(columns[i].getName()); BigDecimal num = rs.getBigDecimal(i+1);
if (num == null) { if (num == null) {
break; break;
@ -237,7 +234,7 @@ public final class Node implements INode, Serializable {
case Types.VARBINARY: case Types.VARBINARY:
case Types.BINARY: case Types.BINARY:
newprop.setStringValue(rs.getString(columns[i].getName())); newprop.setStringValue(rs.getString(i+1));
break; break;
@ -245,9 +242,9 @@ public final class Node implements INode, Serializable {
case Types.LONGVARCHAR: case Types.LONGVARCHAR:
try { try {
newprop.setStringValue(rs.getString(columns[i].getName())); newprop.setStringValue(rs.getString(i+1));
} catch (SQLException x) { } catch (SQLException x) {
Reader in = rs.getCharacterStream(columns[i].getName()); Reader in = rs.getCharacterStream(i+1);
char[] buffer = new char[2048]; char[] buffer = new char[2048];
int read = 0; int read = 0;
int r = 0; int r = 0;
@ -272,22 +269,22 @@ public final class Node implements INode, Serializable {
case Types.CHAR: case Types.CHAR:
case Types.VARCHAR: case Types.VARCHAR:
case Types.OTHER: case Types.OTHER:
newprop.setStringValue(rs.getString(columns[i].getName())); newprop.setStringValue(rs.getString(i+1));
break; break;
case Types.DATE: case Types.DATE:
newprop.setDateValue(rs.getDate(columns[i].getName())); newprop.setDateValue(rs.getDate(i+1));
break; break;
case Types.TIME: case Types.TIME:
newprop.setDateValue(rs.getTime(columns[i].getName())); newprop.setDateValue(rs.getTime(i+1));
break; break;
case Types.TIMESTAMP: case Types.TIMESTAMP:
newprop.setDateValue(rs.getTimestamp(columns[i].getName())); newprop.setDateValue(rs.getTimestamp(i+1));
break; break;
@ -298,7 +295,7 @@ public final class Node implements INode, Serializable {
// continue; // continue;
default: default:
newprop.setStringValue(rs.getString(columns[i].getName())); newprop.setStringValue(rs.getString(i+1));
break; break;
} }
@ -326,6 +323,15 @@ public final class Node implements INode, Serializable {
newprop.dirty = false; newprop.dirty = false;
} }
// set the prototype from the dbmap,
// which was possibly modified while reading the resultset
setPrototype(dbmap.getTypeName());
// If name was not set from resultset, create a synthetical name now.
if ((name == null) || (name.length() == 0)) {
name = dbmap.getTypeName() + " " + id;
}
// again set created and lastmodified. This is because // again set created and lastmodified. This is because
// lastmodified has been been updated, and we want both values to // lastmodified has been been updated, and we want both values to
// be identical to show that the node hasn't been changed since // be identical to show that the node hasn't been changed since
@ -1585,7 +1591,7 @@ public final class Node implements INode, Serializable {
// do not fetch subnodes for nodes that haven't been persisted yet or are in // do not fetch subnodes for nodes that haven't been persisted yet or are in
// the process of being persistified - except if "manual" subnoderelation is set. // the process of being persistified - except if "manual" subnoderelation is set.
if (subRel.aggressiveLoading && if (subRel.aggressiveLoading && subRel.getGroup() == null &&
(((state != TRANSIENT) && (state != NEW)) || (((state != TRANSIENT) && (state != NEW)) ||
(subnodeRelation != null))) { (subnodeRelation != null))) {
// we don't want to load *all* nodes if we just want to count them // we don't want to load *all* nodes if we just want to count them