// Copyright (c) 2001 SPY internetworking <dustin@spy.net>
package net.spy.db;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import net.spy.util.SpyConfig;
import net.spy.util.SpyUtil;
/**
* Super class for all stored procedure calls.
*/
public abstract class DBSP extends SpyCacheDB implements DBSPLike {
// Size for buffer for to stringing
private static final int TOSTRING_SB_SIZE=128;
// BigDecimal scale for doing data conversions
private static final int BD_SCALE=4;
// The set of parameters available to this DBSP (defined in the subclass)
private final LinkedHashMap<String, Parameter> parameters=
new LinkedHashMap<String, Parameter>();
// The set of arguments provided to this DBSP at runtime
private final LinkedHashMap<String, Argument> arguments=
new LinkedHashMap<String, Argument>();
// SP name
private String spname=null;
// Caching info
private long cachetime=0;
// timeout
private int timeout=0;
// Max rows
private int maxRows=0;
private boolean debug=false;
// The query
private String query=null;
// My prepared statement
private PreparedStatement pst=null;
// cursor name if cursors are enabled
private String cursorName=null;
/**
* Get a new DBSP object with a given config.
*/
public DBSP(SpyConfig conf) throws SQLException {
super(conf);
initsp();
}
/**
* Get a new DBSP object using the given Connection
*/
public DBSP(Connection conn) throws SQLException {
super(conn);
initsp();
}
// Initialize hashtables
private void initsp() {
// Inherit debug flag from the logger.
debug=getLogger().isDebugEnabled();
}
/**
* Execute the query.
*/
public ResultSet executeQuery() throws SQLException {
prepare();
ResultSet rs=null;
rs=pst.executeQuery();
if (debug && !(this instanceof DBCP)) {
getLogger().debug("Returned: ");
ResultSetMetaData rsmd=rs.getMetaData();
String cols="";
for (int x=1; x<=rsmd.getColumnCount(); x++) {
if (x>1) {
cols+=", ";
}
cols+=rsmd.getColumnName(x)+"="+rsmd.getColumnTypeName(x);
}
getLogger().debug(cols);
}
return(rs);
}
/**
* Execute a query for update only.
*/
public int executeUpdate() throws SQLException {
int rv=0;
prepare();
rv=pst.executeUpdate();
return(rv);
}
/**
* Get the next result set.
*
* @return null if there are no more result sets.
*/
public ResultSet nextResults() throws SQLException {
ResultSet rs=null;
// Skip over the updates
pst.getUpdateCount();
if(pst.getMoreResults()) {
rs=pst.getResultSet();
}
return(rs);
}
/**
* Get the warnings.
*/
public SQLWarning getWarnings() throws SQLException {
return(pst.getWarnings());
}
/**
* Set the cursor name to be handed to the underlying Statement.
*
* @param name cursor name (or null to disable cursor handling)
*/
public void setCursorName(String name) throws SQLException {
cursorName=name;
if(pst != null) {
getLogger().debug("Setting the pst cursor name to %s", cursorName);
pst.setCursorName(cursorName);
}
}
/**
* Set the number of seconds the results of this SP will be valid.
* This option uses {@link SpyCacheDB#prepareStatement(String,long)}
* instead of the native driver's version.
*
* @param time time (in seconds) to keep the results around
*/
public void setCacheTime(long time) {
this.cachetime=time;
}
/**
* Get the cache time configured for this SP.
*
* @return the time (in seconds) the results will be valid
*/
public long getCacheTime() {
return(cachetime);
}
/**
* Set the prepared statement on which this DBSP will operate.
*
* @param to the prepared statement
*/
protected void setPreparedStatement(PreparedStatement to)
throws SQLException {
if(this.pst != null) {
getLogger().warn("Discarding old prepared statement %s", this.pst);
}
this.pst=to;
if(debug) {
getLogger().debug("Setting timeout to: %s", timeout);
}
pst.setQueryTimeout(timeout);
pst.setMaxRows(maxRows);
// Set the cursor name if there is one.
if(cursorName != null) {
getLogger().debug("Setting the pst cursor name to %s", cursorName);
}
}
/**
* Get the prepared statement on which this DBSP will operate.
*/
protected PreparedStatement getPreparedStatement() {
return(pst);
}
/**
* Define a parameter.
*
* @param p the parameter
* @throws SQLException if the parameter has already been added
*/
protected void addParameter(Parameter p) throws SQLException {
if(parameters.containsKey(p.getName())) {
throw new SQLException(
"parameter ``" + p + "'' already provided.");
}
// Save it.
parameters.put(p.getName(), p);
}
/**
* Define a field to be required.
*
* @param name the name of the field
* @param type the type
* @throws SQLException if the type has already been added
* @see java.sql.Types
*/
protected void setRequired(String name, int type) throws SQLException {
addParameter(new Parameter(Parameter.REQUIRED, type, name));
}
/**
* Define a field to be optional.
*
* @param name the name of the field
* @param type the type
* @throws SQLException if the type has already been added
* @see java.sql.Types
*/
protected void setOptional(String name, int type) throws SQLException {
addParameter(new Parameter(Parameter.OPTIONAL, type, name));
}
/**
* Define a field to be output.
*
* @param name the name of the field
* @param type the type
* @throws SQLException if the type has already been added
* @see java.sql.Types
*/
protected void setOutput(String name, int type) throws SQLException {
addParameter(new Parameter(Parameter.OUTPUT, type, name));
// I'm not sure how best to do this, but this has to be something
// other than a null. If it's null, than the Argument will make
// the javaType into a Types.NULL instead of what I'm trying to set
// it to. Then I just get pissed and life sucks. I guess we could
// make the Argument have a "getSetJavaType()" method? Anyhow, for
// now I'm just going to make an object.
setArg(name, "", type);
}
/**
* Set the named argument to the value contained in the given object
* for the given type.
*
* @param which which variable to set
* @param what what value to set it to
* @param type type of variable this is
*/
protected void setArg(String which, Object what, int type)
throws SQLException {
arguments.put(which, new Argument(type, which, what));
}
/**
* Get the arguments in parameter order.
*
* @return an unmodifiable list of {@link Argument} objects
*/
public Collection<Argument> getArguments() {
ArrayList<Argument> al=new ArrayList<Argument>(arguments.size());
for(Parameter p : getParameters()) {
Argument arg=arguments.get(p.getName());
if(arg==null && p.getParamType()==Parameter.REQUIRED) {
throw new NullPointerException("Missing argument for " + p);
}
if(arg!=null) {
al.add(arg);
}
}
return(Collections.unmodifiableList(al));
}
/**
* Get a Collection of all of the parameters in the order in which they were
* defined.
*
* @return a Collection of {@link Parameter} objects.
*/
protected Collection<Parameter> getParams() {
return(parameters.values());
}
/**
* Reset all arguments.
*/
protected void resetArgs() {
arguments.clear();
}
/**
* Set the timeout for this query. See JDCB Statement documentation.
*/
public void setQueryTimeout(int to) {
this.timeout=to;
}
/**
* Set the maximum number of rows that should be returned from this
* query. See JDBC Statement documentation.
*/
public void setMaxRows(int to) {
this.maxRows=to;
}
/**
* Get the timeout for this query
*/
public int getQueryTimeout() {
return(this.timeout);
}
/**
* Set the stored procedure to the given value.
*/
protected void setSPName(String to) {
this.spname=to;
}
/**
* Get the SP name.
*
* @return the name of the SP to call, or null if not applicable
*/
protected String getSPName() {
return(spname);
}
/**
* Verify that the arguments, as given, are acceptable.
*/
protected void checkArgs() throws SQLException {
if(debug) {
getLogger().debug("Checking");
getLogger().debug("Parameters: %s", parameters);
getLogger().debug("Args: %s", arguments);
}
// Now, verify all of the arguments we have are correctly typed.
for(Argument arg : arguments.values()) {
// Find the matching parameter.
Parameter p=parameters.get(arg.getName());
// Verify the argument exists
if(p==null) {
throw new SQLException("Invalid argument " + arg);
}
int ptype=p.getJavaType();
int atype=arg.getJavaType();
if (atype==Types.NULL) {
atype=((DBNull)arg.getValue()).getType();
}
// Check the type
if(ptype!=atype) {
throw new SQLException("Invalid type for arg " + arg
+ " type was "
+ atype + " ("
+ TypeNames.getTypeName(atype) + ")"
+ " should be "
+ ptype
+ " (" + TypeNames.getTypeName(ptype)
+ ")"
);
}
}
// Next, verify all of the required arguments are there.
for(Parameter p : parameters.values()) {
if(p.getParamType() == Parameter.REQUIRED) {
if(arguments.get(p.getName()) == null) {
throw new SQLException("Required argument "
+ p + " missing.");
}
}
}
// Check complete. :)
}
/**
* Prepare the statement for execution.
*/
protected void prepare() throws SQLException {
if(pst == null) {
// Make sure all the arguments are there.
checkArgs();
// Get ready to build our query.
StringBuilder querySb=new StringBuilder(TOSTRING_SB_SIZE);
querySb.append("exec ");
querySb.append(spname);
querySb.append(" ");
int nargs=0;
for(Argument arg : getArguments()) {
querySb.append("\t@");
querySb.append(arg.getName());
querySb.append("=?,\n");
nargs++;
}
// Remove the last comma if we had params
if(nargs>0) {
querySb.delete(querySb.length()-2, querySb.length());
}
String tmpQuery=querySb.toString().trim();
if (debug) {
getLogger().debug("Query: %s", tmpQuery);
}
// Fill in the arguments.
setQuery(tmpQuery);
}
applyArgs(getArguments());
}
/**
* Set the value of debug for this instance.
*
* @param db the debug value to set
*/
protected void setDebug(boolean db) {
this.debug=db;
}
/**
* Find out if debug is enabled.
*
* @return true if debug is enabled
*/
protected boolean isDebugEnabled() {
return(debug);
}
/**
* Set the SQL query to call
*/
protected void setQuery(String to) throws SQLException {
this.query=to;
// Get a prepared statement, varies whether it's cachable or not.
PreparedStatement tmpPst=null;
if(getCacheTime()>0) {
tmpPst=prepareStatement(to, getCacheTime());
} else {
tmpPst=prepareStatement(to);
}
setPreparedStatement(tmpPst);
}
/**
* Get the query this DBSP will be calling.
*/
protected String getQuery() {
return(query);
}
/**
* Fill in the arguments (with types) for the given list of parameters.
*
* @param v the list of Parameters we need to add, in order
*/
protected void applyArgs(Collection<Argument> v) throws SQLException {
// Use this iterator for the now positional arguments
int i=1;
for(Argument arg : v) {
Object o=arg.getValue();
int type=arg.getJavaType();
if(isDebugEnabled()) {
getLogger().debug("arg[%d] = %s", i, arg);
}
try {
switch(type) {
case Types.BIT:
pst.setBoolean(i, ((Boolean)o).booleanValue());
break;
case Types.DATE:
pst.setDate(i, (java.sql.Date)o);
break;
case Types.DOUBLE:
pst.setDouble(i, ((Number)o).doubleValue());
break;
case Types.FLOAT:
pst.setFloat(i, ((Number)o).floatValue());
break;
case Types.INTEGER:
pst.setInt(i, ((Number)o).intValue());
break;
case Types.BIGINT:
pst.setLong(i, ((Number)o).longValue());
break;
case Types.NUMERIC:
case Types.DECIMAL:
BigDecimal bd=((BigDecimal)o).setScale(BD_SCALE,
BigDecimal.ROUND_HALF_UP);
pst.setBigDecimal(i, bd);
break;
case Types.SMALLINT:
case Types.TINYINT:
pst.setShort(i, (short)((Number)o).intValue());
break;
case Types.NULL:
pst.setNull(i, ((DBNull)o).getType());
break;
case Types.OTHER:
pst.setObject(i, o);
break;
case Types.CLOB:
case Types.VARCHAR:
pst.setString(i, (String)o);
break;
case Types.TIME:
pst.setTime(i, (Time)o);
break;
case Types.TIMESTAMP:
pst.setTimestamp(i, (Timestamp)o);
break;
default:
throw new SQLException("Whoops, type "
+ TypeNames.getTypeName(type) + "(" + type + ")"
+ " seems to have been overlooked.");
}
} catch(SQLException se) {
getLogger().warn("SQLException while applying %s"
+ " in prepared statement for type %s %s", arg,
TypeNames.getTypeName(type), o, se);
throw se;
} catch (Exception applyException) {
String msg="Problem setting " + arg
+ " in prepared statement for type "
+ TypeNames.getTypeName(type) + " "
+ String.valueOf(o) + " : " + applyException;
getLogger().warn(msg, applyException);
SQLException se=new SQLException(msg);
se.initCause(applyException);
throw se;
}
i++;
}
}
/**
* Set field <i>which</i> to the value a1 of the type java.math.BigDecimal
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,java.math.BigDecimal a1)
throws SQLException {
setArg(which, a1, Types.DECIMAL);
}
/**
* Set field <i>which</i> to the value a1 of the type boolean
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,boolean a1)
throws SQLException {
setArg(which, Boolean.valueOf(a1), Types.BIT);
}
/**
* Set field <i>which</i> to the value of b.
*
* @param which the field to set
* @param b the value to set
* @throws SQLException if there's an error setting this argument
*/
public void set(String which, Boolean b) throws SQLException {
setArg(which, b, Types.BIT);
}
/**
* Set field <i>which</i> to the value a1 of the type java.sql.Date
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,java.sql.Date a1)
throws SQLException {
setArg(which, a1, Types.DATE);
}
/**
* Set field <i>which</i> to the value a1 of the type double
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,double a1)
throws SQLException {
setArg(which, new Double(a1), Types.DOUBLE);
}
/**
* Set field <i>which</i> to the value a1 of the type float
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,float a1)
throws SQLException {
setArg(which, new Float(a1), Types.FLOAT);
}
/**
* Set field <i>which</i> to the value a1 of the type float
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which, Float a1)
throws SQLException {
setArg(which, a1, Types.FLOAT);
}
/**
* Set field <i>which</i> to the value a1 of the type java.lang.Integer
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,Integer a1)
throws SQLException {
if (debug) {
getLogger().debug("Adding Integer->INTEGER for "+which);
}
setArg(which, a1, Types.INTEGER);
}
/**
* Set field <i>which</i> to the value a1 of the type int
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,int a1)
throws SQLException {
setArg(which, new Integer(a1), Types.INTEGER);
}
/**
* Set field <i>which</i> to the value a1 of the type long
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,long a1)
throws SQLException {
setArg(which, new Long(a1), Types.BIGINT);
}
/**
* Set field <i>which</i> to a null of the given type.
* Do not call this directly, instead, if you have an empty String,
* for example, consider:
* <pre><code>
* set("mycol", (String)null);
* </code></pre>
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
protected void setNull(String which,int a1)
throws SQLException {
// This one works a bit different because we have to store the
// original type
setArg(which, new Integer(a1), Types.NULL);
}
/**
* Set field <i>which</i> to the value a1 of the type java.lang.Object
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
/*
public void set(String which,java.lang.Object a1)
throws SQLException {
getLogger().debug("Setting OTHER for "+which);
setArg(which, a1, Types.OTHER);
}
*/
/**
* Set field <i>which</i> to the value a1 of the type short
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void setTinyInt(String which,short a1)
throws SQLException {
setArg(which, new Integer(a1), Types.SMALLINT);
}
/**
* Set a parameter to the tinyint value b of the type byte.
*/
public void set(String which, byte b) throws SQLException {
setArg(which, new Integer(b), Types.TINYINT);
}
/**
* Set a parameter to a tinyint value.
*/
public void set(String which, Byte b) throws SQLException {
setArg(which, new Integer(b.intValue()), Types.TINYINT);
}
/**
* Set field <i>which</i> to the value a1 of the type short
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,short a1)
throws SQLException {
setArg(which, new Integer(a1), Types.SMALLINT);
}
/**
* Set the field <i>which</i> to the value s of the type short.
*
* @param which the field to set (by name)
* @param s the new value
* @throws SQLException if there's a problem setting this value
*/
public void set(String which, Short s) throws SQLException {
setArg(which, new Integer(s.intValue()), Types.SMALLINT);
}
/**
* Set field <i>which</i> to the value a1 of the type java.lang.String
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,java.lang.String a1)
throws SQLException {
if (debug) {
getLogger().debug("Adding String->VARCHAR for "+which);
}
setArg(which, a1, Types.VARCHAR);
}
/**
* Set field <i>which</i> to the value a1 of the type Time
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,Time a1)
throws SQLException {
setArg(which, a1, Types.TIME);
}
/**
* Set field <i>which</i> to the value a1 of the type Timestamp
*
* @param which which field to set
* @param a1 the value to set
*
* @exception SQLException if there's an error setting this argument.
*/
public void set(String which,Timestamp a1)
throws SQLException {
setArg(which, a1, Types.TIMESTAMP);
}
public void set(String which,Object obj)
throws SQLException {
setArg(which, obj, Types.OTHER);
}
/**
* Get a Collection containing all parameters.
*
* @return an umodifiable Collection of {@link Parameter} objects.
*/
public Collection<Parameter> getParameters() {
return(parameters.values());
}
/**
* Get a Collection of parameters of a specific type.
*
* @param type the Parameter type.
* @return a list of {@link Parameter}s of the specified type.
*/
public Collection<Parameter> getParameters(int type) {
ArrayList<Parameter> al=new ArrayList<Parameter>();
for(Parameter p : getParameters()) {
if(p.getJavaType() == type) {
al.add(p);
}
}
return(al);
}
/**
* Get the {@link java.sql.Types} type of the given parameter.
*
* @return the type, or -1 if there's no such variable
*/
public int getType(String var) {
int rv=-1;
Parameter p=parameters.get(var);
if(p!=null) {
rv=p.getJavaType();
}
return(rv);
}
/**
* Get the {@link Parameter} type.
*
* @param var the name of the parameter
* @return the {@link Parameter} type, or -1 if there's no such
* parameter
*/
public int getParameterType(String var) {
int rv=-1;
Parameter p=parameters.get(var);
if(p!=null) {
rv=p.getParamType();
}
return(rv);
}
/**
* Close this statement and whatever the superclass wants to do.
*/
@Override
public void close() {
if(pst!=null) {
try {
pst.close();
} catch(SQLException e) {
getLogger().warn("Problem closing prepared statement", e);
}
pst=null;
}
super.close();
}
/**
* Set a field in the DBSP after coercing the String value to the
* required value for the given field. This should <i>rarely</i> be
* used, but is useful for accessing DBSPs through web forms.
*
* @param var the field to set
* @param value the String representation of the value to set
* @throws SQLException if there's a problem with coersion
*/
public void setCoerced(String var, String value) throws SQLException {
int type=getType(var);
if(value == null) {
// if the value is null, send in a null
DBNull n=new DBNull(type);
set(var, n);
} else {
// Value is not null, parse it and call the proper set method
switch(type) {
case Types.BIT:
set(var, SpyUtil.getBoolean(value).booleanValue());
break;
case Types.DOUBLE:
set(var, new Double(value).doubleValue());
break;
case Types.FLOAT:
set(var, new Float(value).floatValue());
break;
case Types.INTEGER:
set(var, Integer.parseInt(value));
break;
case Types.BIGINT:
set(var, Long.parseLong(value));
break;
case Types.NUMERIC:
case Types.DECIMAL:
set(var, new BigDecimal(value));
break;
case Types.SMALLINT:
case Types.TINYINT:
set(var, (short)Integer.parseInt(value));
break;
case Types.OTHER:
set(var, value);
break;
case Types.VARCHAR:
set(var, value);
break;
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
throw new SQLException("Date types not currently handled");
default:
throw new SQLException(
"No known type for " + var + ", you sure it's valid?.");
}
}
}
/**
* Objects with names.
*/
public abstract class NamedObject extends Object {
private String name=null;
/**
* Get an instance of a named object with the given name.
*
* @param nm the name
*/
protected NamedObject(String nm) {
super();
if(nm==null) {
throw new NullPointerException("name not given");
}
this.name=nm;
}
/**
* Get the name of this object.
*/
public String getName() {
return(name);
}
/**
* String me.
*/
@Override
public String toString() {
return("{" + getClass().getName() + " " + getName() + "}");
}
/**
* Get the hash code of the name of this object.
*
* @return getName().hashCode()
*/
@Override
public int hashCode() {
return(name.hashCode());
}
/**
* Test for equality.
*
* @param o object to test against
* @return true if <code>o</code> is a NamedObject object with the
* same name
*/
@Override
public boolean equals(Object o) {
boolean rv=false;
if(o instanceof NamedObject) {
NamedObject no=(NamedObject)o;
rv=name.equals(no.name);
}
return(rv);
}
}
/**
* Parameters for DBSPs.
*/
public class Parameter extends NamedObject {
/**
* Parameter type indicating this is a required parameter.
*/
public static final int REQUIRED=1;
/**
* Parameter type indicating this is an optional parameter.
*/
public static final int OPTIONAL=2;
/**
* Parameter type indicating this is an output parameter.
*/
public static final int OUTPUT=2;
private int paramType=0;
private int javaType=0;
/**
* Construct a new Parameter.
*
* @param pType the parameter type (REQUIRED, etc...)
* @param jType the {@link java.sql.Types} type
* @param name name of the parametr
*/
public Parameter(int pType, int jType, String name) {
super(name);
this.paramType=pType;
this.javaType=jType;
validateParamType();
}
private void validateParamType() {
if((paramType != REQUIRED)
&& (paramType != OPTIONAL)
&& (paramType != OUTPUT)) {
throw new IllegalArgumentException(paramType
+ " is not a valid " + getClass().getName()
+ " type.");
}
} // validateParamType
/**
* Get the type of this parameter.
*/
public int getParamType() {
return(paramType);
}
/**
* Get the {@link java.sql.Types} type of this parameter.
*/
public int getJavaType() {
return(javaType);
}
@Override
public String toString() {
return "{Param: type="
+ TypeNames.getTypeName(javaType)
+ "(" + javaType + "), name="
+ getName() + ", required=" + paramType + "}";
}
}
/**
* Arguments for DBSPs.
*/
public class Argument extends NamedObject {
private int javaType=0;
private Object value=null;
/**
* Construct a new Argument.
*
* @param jType the {@link java.sql.Types} type
* @param name name of the parametr
*/
public Argument(int jType, String name, Object v) {
super(name);
this.javaType=jType;
this.value=v;
}
/**
* Get the {@link java.sql.Types} type of this parameter.
*/
public int getJavaType() {
return(value == null ? Types.NULL : javaType);
}
/**
* Get the value of this argument.
* @return the object passed in at construct time unless it was
* null, in which case it will be an appropriate
* {@link DBNull} object
*/
public Object getValue() {
Object rv=value;
if(rv==null) {
rv=new DBNull(javaType);
}
return(rv);
}
@Override
public String toString() {
String valName=(value == null ? valName="NULL" : "'" + value + "'");
return "{Arg: type=" + javaType
+ "(" + TypeNames.getTypeName(javaType)
+ "), name=" + getName() + ", value=" + valName + "}";
}
}
}