Clean-up of sandbox, rearranging everything.

Added a couple of files that was never commited.
This commit is contained in:
Harald Kuhr
2010-06-07 09:55:35 +02:00
parent 1f60b62626
commit b6ee5ce450
37 changed files with 5735 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import com.twelvemonkeys.lang.SystemUtil;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* A class that holds a JDBC Connection. The class can be configured by a
* properties file. However, the approach is rather lame, and only lets you
* configure one connection...
* <P/>
* Tested with jConnect (Sybase), I-net Sprinta2000 (MS SQL) and Oracle.
* <P/>
* @todo be able to register more drivers, trough properties and runtime
* @todo be able to register more connections, trough properties and runtime
* <P/>
* <STRONG>Example properties file</STRONG></BR>
* # filename: com.twelvemonkeys.sql.DatabaseConnection.properties
* driver=com.inet.tds.TdsDriver
* url=jdbc:inetdae7:127.0.0.1:1433?database\=mydb
* user=scott
* password=tiger
* # What do you expect, really?
* logDebug=true
*
* @author Philippe Béal (phbe@iconmedialab.no)
* @author Harald Kuhr (haraldk@iconmedialab.no)
* @author last modified by $Author: haku $
*
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/DatabaseConnection.java#1 $
*
* @todo Use org.apache.commons.logging instead of proprietary logging.
*
*/
public class DatabaseConnection {
// Default driver
public final static String DEFAULT_DRIVER = "NO_DRIVER";
// Default URL
public final static String DEFAULT_URL = "NO_URL";
protected static String mDriver = null;
protected static String mUrl = null;
// Default debug is true
// private static boolean debug = true;
protected static Properties mConfig = null;
//protected static Log mLog = null;
protected static Log mLog = null;
protected static boolean mInitialized = false;
// Must be like this...
// http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html :-)
private static DatabaseConnection sInstance = new DatabaseConnection();
/**
* Creates the DatabaseConnection.
*/
private DatabaseConnection() {
init();
}
/**
* Gets the single DatabaseConnection instance.
*/
protected static DatabaseConnection getInstance() {
/*
if (sInstance == null) {
sInstance = new DatabaseConnection();
sInstance.init();
}
*/
return sInstance;
}
/**
* Initializes the DatabaseConnection, called from the constructor.
*
* @exception IllegalStateException if an attempt to call init() is made
* after the instance is allready initialized.
*/
protected synchronized void init() {
// Make sure init is executed only once!
if (mInitialized) {
throw new IllegalStateException("init() may only be called once!");
}
mInitialized = true;
try {
mConfig = SystemUtil.loadProperties(DatabaseConnection.class);
}
catch (FileNotFoundException fnf) {
// Ignore
}
catch (IOException ioe) {
//LogFactory.getLog(getClass()).error("Caught IOException: ", ioe);
new Log(this).logError(ioe);
//ioe.printStackTrace();
}
finally {
if (mConfig == null) {
mConfig = new Properties();
}
}
mLog = new Log(this, mConfig);
//mLog = LogFactory.getLog(getClass());
// debug = new Boolean(config.getProperty("debug", "true")).booleanValue();
// config.list(System.out);
mDriver = mConfig.getProperty("driver", DEFAULT_DRIVER);
mUrl = mConfig.getProperty("url", DEFAULT_URL);
}
/**
* Gets the default JDBC Connection. The connection is configured through
* the properties file.
*
* @return the default jdbc Connection
*/
public static Connection getConnection() {
return getConnection(null, null, getInstance().mUrl);
}
/**
* Gets a JDBC Connection with the given parameters. The connection is
* configured through the properties file.
*
* @param pUser the database user name
* @param pPassword the password of the database user
* @param pURL the url to connect to
*
* @return a jdbc Connection
*/
public static Connection getConnection(String pUser,
String pPassword,
String pURL) {
return getInstance().getConnectionInstance(pUser, pPassword, pURL);
}
/**
* Gets a JDBC Connection with the given parameters. The connection is
* configured through the properties file.
*
* @param pUser the database user name
* @param pPassword the password of the database user
* @param pURL the url to connect to
*
* @return a jdbc Connection
*/
protected Connection getConnectionInstance(String pUser,
String pPassword,
String pURL) {
Properties props = (Properties) mConfig.clone();
if (pUser != null) {
props.put("user", pUser);
}
if (pPassword != null) {
props.put("password", pPassword);
}
// props.list(System.out);
try {
// Load & register the JDBC Driver
if (!DEFAULT_DRIVER.equals(mDriver)) {
Class.forName(mDriver).newInstance();
}
Connection conn = DriverManager.getConnection(pURL, props);
if (mLog.getLogDebug()) {
//if (mLog.isDebugEnabled()) {
DatabaseMetaData dma = conn.getMetaData();
mLog.logDebug("Connected to " + dma.getURL());
mLog.logDebug("Driver " + dma.getDriverName());
mLog.logDebug("Version " + dma.getDriverVersion());
//mLog.debug("Connected to " + dma.getURL());
//mLog.debug("Driver " + dma.getDriverName());
//mLog.debug("Version " + dma.getDriverVersion());
}
return conn;
}
catch (Exception e) {
mLog.logError(e.getMessage());
// Get chained excpetions
if (e instanceof SQLException) {
SQLException sqle = (SQLException) e;
while ((sqle = sqle.getNextException()) != null) {
mLog.logWarning(sqle);
}
}
}
return null;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import java.io.Serializable;
/**
* DatabaseProduct
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/DatabaseProduct.java#1 $
*/
public final class DatabaseProduct implements Serializable {
private static final String UNKNOWN_NAME = "Unknown";
private static final String GENERIC_NAME = "Generic";
private static final String CACHE_NAME = "Caché";
private static final String DB2_NAME = "DB2";
private static final String MSSQL_NAME = "MSSQL";
private static final String ORACLE_NAME = "Oracle";
private static final String POSTGRESS_NAME = "PostgreSQL";
private static final String SYBASE_NAME = "Sybase";
/*public*/ static final DatabaseProduct UNKNOWN = new DatabaseProduct(UNKNOWN_NAME);
public static final DatabaseProduct GENERIC = new DatabaseProduct(GENERIC_NAME);
public static final DatabaseProduct CACHE = new DatabaseProduct(CACHE_NAME);
public static final DatabaseProduct DB2 = new DatabaseProduct(DB2_NAME);
public static final DatabaseProduct MSSQL = new DatabaseProduct(MSSQL_NAME);
public static final DatabaseProduct ORACLE = new DatabaseProduct(ORACLE_NAME);
public static final DatabaseProduct POSTGRES = new DatabaseProduct(POSTGRESS_NAME);
public static final DatabaseProduct SYBASE = new DatabaseProduct(SYBASE_NAME);
private static final DatabaseProduct[] VALUES = {
GENERIC, CACHE, DB2, MSSQL, ORACLE, POSTGRES, SYBASE,
};
private static int sNextOrdinal = -1;
private final int mOrdinal = sNextOrdinal++;
private final String mKey;
private DatabaseProduct(String pName) {
mKey = pName;
}
static int enumSize() {
return sNextOrdinal;
}
final int id() {
return mOrdinal;
}
final String key() {
return mKey;
}
public String toString() {
return mKey + " [id=" + mOrdinal+ "]";
}
/**
* Gets the {@code DatabaseProduct} known by the given name.
*
* @param pName
* @return the {@code DatabaseProduct} known by the given name
* @throws IllegalArgumentException if there's no such name
*/
public static DatabaseProduct resolve(String pName) {
if ("ANSI".equalsIgnoreCase(pName) || GENERIC_NAME.equalsIgnoreCase(pName)) {
return GENERIC;
}
else if ("Cache".equalsIgnoreCase(pName) || CACHE_NAME.equalsIgnoreCase(pName)) {
return CACHE;
}
else if (DB2_NAME.equalsIgnoreCase(pName)) {
return DB2;
}
else if (MSSQL_NAME.equalsIgnoreCase(pName)) {
return MSSQL;
}
else if (ORACLE_NAME.equalsIgnoreCase(pName)) {
return ORACLE;
}
else if ("Postgres".equalsIgnoreCase(pName) || POSTGRESS_NAME.equalsIgnoreCase(pName)) {
return POSTGRES;
}
else if (SYBASE_NAME.equalsIgnoreCase(pName)) {
return SYBASE;
}
else {
throw new IllegalArgumentException("Unknown database product \"" + pName
+ "\", try any of the known products, or \"Generic\"");
}
}
private Object readResolve() {
return VALUES[mOrdinal]; // Canonicalize
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import java.util.Hashtable;
/**
* Interface for classes that is to be read from a database, using the
* ObjectReader class.
*
* @author Harald Kuhr (haraldk@iconmedialab.no)
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/DatabaseReadable.java#1 $
*
* @todo Use JDK logging instead of proprietary logging.
*
*/
public interface DatabaseReadable {
/**
* Gets the unique identifier of this DatabaseReadable object.
*
* @return An object that uniqely identifies this DatabaseReadable.
*/
public Object getId();
/**
* Sets the unique identifier of this DatabaseReadable object.
*
* @param id An object that uniqely identifies this DatabaseReadable.
*/
public void setId(Object id);
/**
* Gets the object to database mapping of this DatabaseReadable.
*
* @return A Hashtable cotaining the database mapping for this
* DatabaseReadable.
*/
public Hashtable getMapping();
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
/**
* AbstractHelper
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/JDBCHelper.java#1 $
*/
public abstract class JDBCHelper {
private static JDBCHelper[] sHelpers = new JDBCHelper[DatabaseProduct.enumSize()];
static {
DatabaseProduct product = DatabaseProduct.resolve(System.getProperty("com.twelvemonkeys.sql.databaseProduct", "Generic"));
sHelpers[0] = createInstance(product);
}
private JDBCHelper() {
}
private static JDBCHelper createInstance(DatabaseProduct pProduct) {
// Get database name
// Instantiate helper
if (pProduct == DatabaseProduct.GENERIC) {
return new GenericHelper();
}
else if (pProduct == DatabaseProduct.CACHE) {
return new CacheHelper();
}
else if (pProduct == DatabaseProduct.DB2) {
return new DB2Helper();
}
else if (pProduct == DatabaseProduct.MSSQL) {
return new MSSQLHelper();
}
else if (pProduct == DatabaseProduct.ORACLE) {
return new OracleHelper();
}
else if (pProduct == DatabaseProduct.POSTGRES) {
return new PostgreSQLHelper();
}
else if (pProduct == DatabaseProduct.SYBASE) {
return new SybaseHelper();
}
else {
throw new IllegalArgumentException("Unknown database product, try any of the known products, or \"generic\"");
}
}
public final static JDBCHelper getInstance() {
return sHelpers[0];
}
public final static JDBCHelper getInstance(DatabaseProduct pProuct) {
JDBCHelper helper = sHelpers[pProuct.id()];
if (helper == null) {
// This is ok, iff sHelpers[pProuct] = helper is an atomic op...
synchronized (sHelpers) {
helper = sHelpers[pProuct.id()];
if (helper == null) {
helper = createInstance(pProuct);
sHelpers[pProuct.id()] = helper;
}
}
}
return helper;
}
// Abstract or ANSI SQL implementations of different stuff
public String getDefaultDriverName() {
return "";
}
public String getDefaultURL() {
return "jdbc:{$DRIVER}://localhost:{$PORT}/{$DATABASE}";
}
// Vendor specific concrete implementations
static class GenericHelper extends JDBCHelper {
// Nothing here
}
static class CacheHelper extends JDBCHelper {
public String getDefaultDriverName() {
return "com.intersys.jdbc.CacheDriver";
}
public String getDefaultURL() {
return "jdbc:Cache://localhost:1972/{$DATABASE}";
}
}
static class DB2Helper extends JDBCHelper {
public String getDefaultDriverName() {
return "COM.ibm.db2.jdbc.net.DB2Driver";
}
public String getDefaultURL() {
return "jdbc:db2:{$DATABASE}";
}
}
static class MSSQLHelper extends JDBCHelper {
public String getDefaultDriverName() {
return "com.microsoft.jdbc.sqlserver.SQLServerDriver";
}
public String getDefaultURL() {
return "jdbc:microsoft:sqlserver://localhost:1433;databasename={$DATABASE};SelectMethod=cursor";
}
}
static class OracleHelper extends JDBCHelper {
public String getDefaultDriverName() {
return "oracle.jdbc.driver.OracleDriver";
}
public String getDefaultURL() {
return "jdbc:oracle:thin:@localhost:1521:{$DATABASE}";
}
}
static class PostgreSQLHelper extends JDBCHelper {
public String getDefaultDriverName() {
return "org.postgresql.Driver";
}
public String getDefaultURL() {
return "jdbc:postgresql://localhost/{$DATABASE}";
}
}
static class SybaseHelper extends JDBCHelper {
public String getDefaultDriverName() {
return "com.sybase.jdbc2.jdbc.SybDriver";
}
public String getDefaultURL() {
return "jdbc:sybase:Tds:localhost:4100/";
}
}
}

View File

@@ -0,0 +1,673 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import com.twelvemonkeys.lang.SystemUtil;
import java.io.*;
import java.util.Date;
import java.util.Hashtable;
import java.util.Properties;
/**
* Class used for logging.
* The class currently supports four levels of logging (debug, warning, error
* and info).
* <P>
* The class maintains a cahce of OutputStreams, to avoid more than one stream
* logging to a specific file. The class should also be thread safe, in that no
* more than one instance of the class can log to the same OuputStream.
* <P>
* <STRONG>
* WARNING: The uniqueness of logfiles is based on filenames alone, meaning
* "info.log" and "./info.log" will probably be treated as different files,
* and have different streams attatched to them.
* </STRONG>
* <P>
* <STRONG>
* WARNING: The cached OutputStreams can possibly be in error state or be
* closed without warning. Should be fixed in later versions!
* </STRONG>
*
* @author Harald Kuhr (haraldk@iconmedialab.no)
* @author last modified by $Author: haku $
*
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/Log.java#1 $
*
* @deprecated Use the JDK java.util.logging for logging.
* This class is old and outdated, and is here only for compatibility. It will
* be removed from the library in later releases.
* <P>
* All new code are strongly encouraged to use the org.apache.commons.logging
* package for logging.
*
* @see java.util.logging.Logger
*
*/
class Log {
private static Hashtable streamCache = new Hashtable();
static {
streamCache.put("System.out", System.out);
streamCache.put("System.err", System.err);
}
private static Log globalLog = null;
private String owner = null;
private boolean logDebug = false;
private boolean logWarning = false;
private boolean logError = true; // Log errors!
private boolean logInfo = false;
private PrintStream debugLog = null;
private PrintStream warningLog = null;
private PrintStream errorLog = null;
private PrintStream infoLog = null;
/**
* Init global log
*/
static {
Properties config = null;
try {
config = SystemUtil.loadProperties(Log.class);
}
catch (FileNotFoundException fnf) {
// That's okay.
}
catch (IOException ioe) {
// Not so good
log(System.err, "ERROR", Log.class.getName(), null, ioe);
}
globalLog = new Log(new Log(), config);
// Defaults
if (globalLog.debugLog == null)
globalLog.setDebugLog(System.out);
if (globalLog.warningLog == null)
globalLog.setWarningLog(System.err);
if (globalLog.errorLog == null)
globalLog.setErrorLog(System.err);
if (globalLog.infoLog == null)
globalLog.setInfoLog(System.out);
// Info
globalLog.logDebug("Logging system started.");
log(globalLog.infoLog, "INFO", Log.class.getName(),
"Logging system started.", null);
}
/**
* Internal use only
*/
private Log() {
}
/**
* Creates a log
*/
public Log(Object owner) {
this.owner = owner.getClass().getName();
}
/**
* Creates a log
*/
public Log(Object owner, Properties config) {
this(owner);
if (config == null)
return;
// Set logging levels
logDebug = new Boolean(config.getProperty("logDebug",
"false")).booleanValue();
logWarning = new Boolean(config.getProperty("logWarning",
"false")).booleanValue();
logError = new Boolean(config.getProperty("logError",
"true")).booleanValue();
logInfo = new Boolean(config.getProperty("logInfo",
"true")).booleanValue();
// Set logging streams
String fileName;
try {
if ((fileName = config.getProperty("debugLog")) != null)
setDebugLog(fileName);
if ((fileName = config.getProperty("warningLog")) != null)
setWarningLog(fileName);
if ((fileName = config.getProperty("errorLog")) != null)
setErrorLog(fileName);
if ((fileName = config.getProperty("infoLog")) != null)
setInfoLog(fileName);
}
catch (IOException ioe) {
if (errorLog == null)
setErrorLog(System.err);
logError("Could not create one or more logging streams! ", ioe);
}
}
/**
* Checks if we log debug info
*
* @return True if logging
*/
public boolean getLogDebug() {
return logDebug;
}
/**
* Sets wheter we are to log debug info
*
* @param logDebug Boolean, true if we want to log debug info
*/
public void setLogDebug(boolean logDebug) {
this.logDebug = logDebug;
}
/**
* Checks if we globally log debug info
*
* @return True if global logging
*/
/*
public static boolean getGlobalDebug() {
return globalDebug;
}
*/
/**
* Sets wheter we are to globally log debug info
*
* @param logDebug Boolean, true if we want to globally log debug info
*/
/*
public static void setGlobalDebug(boolean globalDebug) {
Log.globalDebug = globalDebug;
}
/*
/**
* Sets the OutputStream we want to print to
*
* @param os The OutputStream we will use for logging
*/
public void setDebugLog(OutputStream os) {
debugLog = new PrintStream(os, true);
}
/**
* Sets the filename of the File we want to print to. Equivalent to
* setDebugLog(new FileOutputStream(fileName, true))
*
* @param file The File we will use for logging
* @see #setDebugLog(OutputStream)
*/
public void setDebugLog(String fileName) throws IOException {
setDebugLog(getStream(fileName));
}
/**
* Prints debug info to the current debugLog
*
* @param message The message to log
* @see #logDebug(String, Exception)
*/
public void logDebug(String message) {
logDebug(message, null);
}
/**
* Prints debug info to the current debugLog
*
* @param exception An Exception
* @see #logDebug(String, Exception)
*/
public void logDebug(Exception exception) {
logDebug(null, exception);
}
/**
* Prints debug info to the current debugLog
*
* @param message The message to log
* @param exception An Exception
*/
public void logDebug(String message, Exception exception) {
if (!(logDebug || globalLog.logDebug))
return;
if (debugLog != null)
log(debugLog, "DEBUG", owner, message, exception);
else
log(globalLog.debugLog, "DEBUG", owner, message, exception);
}
// WARNING
/**
* Checks if we log warning info
*
* @return True if logging
*/
public boolean getLogWarning() {
return logWarning;
}
/**
* Sets wheter we are to log warning info
*
* @param logWarning Boolean, true if we want to log warning info
*/
public void setLogWarning(boolean logWarning) {
this.logWarning = logWarning;
}
/**
* Checks if we globally log warning info
*
* @return True if global logging
*/
/*
public static boolean getGlobalWarning() {
return globalWarning;
}
*/
/**
* Sets wheter we are to globally log warning info
*
* @param logWarning Boolean, true if we want to globally log warning info
*/
/*
public static void setGlobalWarning(boolean globalWarning) {
Log.globalWarning = globalWarning;
}
*/
/**
* Sets the OutputStream we want to print to
*
* @param os The OutputStream we will use for logging
*/
public void setWarningLog(OutputStream os) {
warningLog = new PrintStream(os, true);
}
/**
* Sets the filename of the File we want to print to. Equivalent to
* setWarningLog(new FileOutputStream(fileName, true))
*
* @param file The File we will use for logging
* @see #setWarningLog(OutputStream)
*/
public void setWarningLog(String fileName) throws IOException {
setWarningLog(getStream(fileName));
}
/**
* Prints warning info to the current warningLog
*
* @param message The message to log
* @see #logWarning(String, Exception)
*/
public void logWarning(String message) {
logWarning(message, null);
}
/**
* Prints warning info to the current warningLog
*
* @param exception An Exception
* @see #logWarning(String, Exception)
*/
public void logWarning(Exception exception) {
logWarning(null, exception);
}
/**
* Prints warning info to the current warningLog
*
* @param message The message to log
* @param exception An Exception
*/
public void logWarning(String message, Exception exception) {
if (!(logWarning || globalLog.logWarning))
return;
if (warningLog != null)
log(warningLog, "WARNING", owner, message, exception);
else
log(globalLog.warningLog, "WARNING", owner, message, exception);
}
// ERROR
/**
* Checks if we log error info
*
* @return True if logging
*/
public boolean getLogError() {
return logError;
}
/**
* Sets wheter we are to log error info
*
* @param logError Boolean, true if we want to log error info
*/
public void setLogError(boolean logError) {
this.logError = logError;
}
/**
* Checks if we globally log error info
*
* @return True if global logging
*/
/*
public static boolean getGlobalError() {
return globalError;
}
*/
/**
* Sets wheter we are to globally log error info
*
* @param logError Boolean, true if we want to globally log error info
*/
/*
public static void setGlobalError(boolean globalError) {
Log.globalError = globalError;
}
*/
/**
* Sets the OutputStream we want to print to
*
* @param os The OutputStream we will use for logging
*/
public void setErrorLog(OutputStream os) {
errorLog = new PrintStream(os, true);
}
/**
* Sets the filename of the File we want to print to. Equivalent to
* setErrorLog(new FileOutputStream(fileName, true))
*
* @param file The File we will use for logging
* @see #setErrorLog(OutputStream)
*/
public void setErrorLog(String fileName) throws IOException {
setErrorLog(getStream(fileName));
}
/**
* Prints error info to the current errorLog
*
* @param message The message to log
* @see #logError(String, Exception)
*/
public void logError(String message) {
logError(message, null);
}
/**
* Prints error info to the current errorLog
*
* @param exception An Exception
* @see #logError(String, Exception)
*/
public void logError(Exception exception) {
logError(null, exception);
}
/**
* Prints error info to the current errorLog
*
* @param message The message to log
* @param exception An Exception
*/
public void logError(String message, Exception exception) {
if (!(logError || globalLog.logError))
return;
if (errorLog != null)
log(errorLog, "ERROR", owner, message, exception);
else
log(globalLog.errorLog, "ERROR", owner, message, exception);
}
// INFO
/**
* Checks if we log info info
*
* @return True if logging
*/
public boolean getLogInfo() {
return logInfo;
}
/**
* Sets wheter we are to log info info
*
* @param logInfo Boolean, true if we want to log info info
*/
public void setLogInfo(boolean logInfo) {
this.logInfo = logInfo;
}
/**
* Checks if we globally log info info
*
* @return True if global logging
*/
/*
public static boolean getGlobalInfo() {
return globalInfo;
}
*/
/**
* Sets wheter we are to globally log info info
*
* @param logInfo Boolean, true if we want to globally log info info
*/
/*
public static void setGlobalInfo(boolean globalInfo) {
Log.globalInfo = globalInfo;
}
*/
/**
* Sets the OutputStream we want to print to
*
* @param os The OutputStream we will use for logging
*/
public void setInfoLog(OutputStream os) {
infoLog = new PrintStream(os, true);
}
/**
* Sets the filename of the File we want to print to. Equivalent to
* setInfoLog(new FileOutputStream(fileName, true))
*
* @param file The File we will use for logging
* @see #setInfoLog(OutputStream)
*/
public void setInfoLog(String fileName) throws IOException {
setInfoLog(getStream(fileName));
}
/**
* Prints info info to the current infoLog
*
* @param message The message to log
* @see #logInfo(String, Exception)
*/
public void logInfo(String message) {
logInfo(message, null);
}
/**
* Prints info info to the current infoLog
*
* @param exception An Exception
* @see #logInfo(String, Exception)
*/
public void logInfo(Exception exception) {
logInfo(null, exception);
}
/**
* Prints info info to the current infoLog
*
* @param message The message to log
* @param exception An Exception
*/
public void logInfo(String message, Exception exception) {
if (!(logInfo || globalLog.logInfo))
return;
if (infoLog != null)
log(infoLog, "INFO", owner, message, exception);
else
log(globalLog.infoLog, "INFO", owner, message, exception);
}
// LOG
/**
* Internal method to get a named stream
*/
private static OutputStream getStream(String name) throws IOException {
OutputStream os = null;
synchronized (streamCache) {
if ((os = (OutputStream) streamCache.get(name)) != null)
return os;
os = new FileOutputStream(name, true);
streamCache.put(name, os);
}
return os;
}
/**
* Internal log method
*/
private static void log(PrintStream ps, String header,
String owner, String message, Exception ex) {
// Only allow one instance to print to the given stream.
synchronized (ps) {
// Create output stream for logging
LogStream logStream = new LogStream(ps);
logStream.time = new Date(System.currentTimeMillis());
logStream.header = header;
logStream.owner = owner;
if (message != null)
logStream.println(message);
if (ex != null) {
logStream.println(ex.getMessage());
ex.printStackTrace(logStream);
}
}
}
}
/**
* Utility class for logging.
*
* Minimal overloading of PrintStream
*/
class LogStream extends PrintStream {
Date time = null;
String header = null;
String owner = null;
public LogStream(OutputStream ps) {
super(ps);
}
public void println(Object o) {
if (o == null)
println("null");
else
println(o.toString());
}
public void println(String str) {
super.println("*** " + header + " (" + time + ", " + time.getTime()
+ ") " + owner + ": " + str);
}
}

View File

@@ -0,0 +1,276 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import java.lang.reflect.*;
import java.util.*;
import java.sql.SQLException;
import java.sql.Connection;
/*
Det vi trenger er en mapping mellom
- abstrakt navn/klasse/type/identifikator (tilsv. repository)
- java klasse
- selve mappingen av db kolonne/java property
I tillegg en mapping mellom alle objektene som brukes i VM'en, og deres id'er
*/
/**
* Under construction.
*
* @author Harald Kuhr (haraldk@iconmedialab.no),
* @version 0.5
*/
public abstract class ObjectManager {
private ObjectReader mObjectReader = null;
private WeakHashMap mLiveObjects = new WeakHashMap(); // object/id
private Hashtable mTypes = new Hashtable(); // type name/java class
private Hashtable mMappings = new Hashtable(); // type name/mapping
/**
* Creates an Object Manager with the default JDBC connection
*/
public ObjectManager() {
this(DatabaseConnection.getConnection());
}
/**
* Creates an Object Manager with the given JDBC connection
*/
public ObjectManager(Connection pConnection) {
mObjectReader = new ObjectReader(pConnection);
}
/**
* Gets the property/column mapping for a given type
*/
protected Hashtable getMapping(String pType) {
return (Hashtable) mMappings.get(pType);
}
/**
* Gets the class for a type
*
* @return The class for a type. If the type is not found, this method will
* throw an excpetion, and will never return null.
*/
protected Class getType(String pType) {
Class cl = (Class) mTypes.get(pType);
if (cl == null) {
// throw new NoSuchTypeException();
}
return cl;
}
/**
* Gets a java object of the class for a given type.
*/
protected Object getObject(String pType)
/*throws XxxException*/ {
// Get class
Class cl = getType(pType);
// Return the new instance (requires empty public constructor)
try {
return cl.newInstance();
}
catch (Exception e) {
// throw new XxxException(e);
throw new RuntimeException(e.getMessage());
}
// Can't happen
//return null;
}
/**
* Gets a DatabaseReadable object that can be used for looking up the
* object properties from the database.
*/
protected DatabaseReadable getDatabaseReadable(String pType) {
return new DatabaseObject(getObject(pType), getMapping(pType));
}
/**
* Reads the object of the given type and with the given id from the
* database
*/
// interface
public Object getObject(String pType, Object pId)
throws SQLException {
// Create DatabaseReadable and set id
DatabaseObject dbObject = (DatabaseObject) getDatabaseReadable(pType);
dbObject.setId(pId);
// Read it
dbObject = (DatabaseObject) mObjectReader.readObject(dbObject);
// Return it
return dbObject.getObject();
}
/**
* Reads the objects of the given type and with the given ids from the
* database
*/
// interface
public Object[] getObjects(String pType, Object[] pIds)
throws SQLException {
// Create Vector to hold the result
Vector result = new Vector(pIds.length);
// Loop through Id's and fetch one at a time (no good performance...)
for (int i = 0; i < pIds.length; i++) {
// Create DBObject, set id and read it
DatabaseObject dbObject =
(DatabaseObject) getDatabaseReadable(pType);
dbObject.setId(pIds[i]);
dbObject = (DatabaseObject) mObjectReader.readObject(dbObject);
// Add to result if not null
if (dbObject != null) {
result.add(dbObject.getObject());
}
}
// Create array of correct type, length equal to Vector
Class cl = getType(pType);
Object[] arr = (Object[]) Array.newInstance(cl, result.size());
// Return the vector as an array
return result.toArray(arr);
}
/**
* Reads the objects of the given type and with the given properties from
* the database
*/
// interface
public Object[] getObjects(String pType, Hashtable pWhere)
throws SQLException {
return mObjectReader.readObjects(getDatabaseReadable(pType), pWhere);
}
/**
* Reads all objects of the given type from the database
*/
// interface
public Object[] getObjects(String pType)
throws SQLException {
return mObjectReader.readObjects(getDatabaseReadable(pType));
}
// interface
public Object addObject(Object pObject) {
// get id...
return pObject;
}
// interface
public Object updateObject(Object pObject) {
// get id...
return pObject;
}
// interface
public abstract Object deleteObject(String pType, Object pId);
// interface
public abstract Object deleteObject(Object pObject);
// interface
public abstract Object createObject(String pType, Object pId);
// interface
public abstract Object createObject(String pType);
}
/**
* Utility class for reading Objects from the database
*/
class DatabaseObject implements DatabaseReadable {
Hashtable mMapping = null;
Object mId = null;
Object mObject = null;
public DatabaseObject(Object pObject, Hashtable pMapping) {
setObject(pObject);
setMapping(pMapping);
}
public Object getId() {
return mId;
}
public void setId(Object pId) {
mId = pId;
}
public void setObject(Object pObject) {
mObject = pObject;
}
public Object getObject() {
return mObject;
}
public void setMapping(Hashtable pMapping) {
mMapping = pMapping;
}
public Hashtable getMapping() {
return mMapping;
}
}

View File

@@ -0,0 +1,663 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import com.twelvemonkeys.lang.*;
import java.lang.reflect.*;
// Single-type import, to avoid util.Date/sql.Date confusion
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
/**
* A class for mapping JDBC ResultSet rows to Java objects.
*
* @see ObjectReader
*
* @author Harald Kuhr (haraldk@iconmedialab.no)
* @author last modified by $Author: haku $
*
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/ObjectMapper.java#1 $
*
* @todo Use JDK logging instead of proprietary logging.
*/
public class ObjectMapper {
final static String DIRECTMAP = "direct";
final static String OBJECTMAP = "object";
final static String COLLECTIONMAP = "collection";
final static String OBJCOLLMAP = "objectcollection";
Class mInstanceClass = null;
Hashtable mMethods = null;
Hashtable mColumnMap = null;
Hashtable mPropertiesMap = null;
Hashtable mJoins = null;
private Hashtable mTables = null;
private Vector mColumns = null;
Hashtable mForeignKeys = null;
Hashtable mPrimaryKeys = null;
Hashtable mMapTypes = null;
Hashtable mClasses = null;
String mPrimaryKey = null;
String mForeignKey = null;
String mIdentityJoin = null;
Log mLog = null;
/**
* Creates a new ObjectMapper for a DatabaseReadable
*
* @param obj An object of type DatabaseReadable
*/
/*
public ObjectMapper(DatabaseReadable obj) {
this(obj.getClass(), obj.getMapping());
}
*/
/**
* Creates a new ObjectMapper for any object, given a mapping
*
* @param objClass The class of the object(s) created by this OM
* @param mapping an Hashtable containing the mapping information
* for this OM
*/
public ObjectMapper(Class pObjClass, Hashtable pMapping) {
mLog = new Log(this);
mInstanceClass = pObjClass;
mJoins = new Hashtable();
mPropertiesMap = new Hashtable();
mColumnMap = new Hashtable();
mClasses = new Hashtable();
mMapTypes = new Hashtable();
mForeignKeys = new Hashtable();
mPrimaryKeys = new Hashtable();
// Unpack and store mapping information
for (Enumeration keys = pMapping.keys(); keys.hasMoreElements();) {
String key = (String) keys.nextElement();
String value = (String) pMapping.get(key);
int dotIdx = key.indexOf(".");
if (dotIdx >= 0) {
if (key.equals(".primaryKey")) {
// Primary key
mPrimaryKey = (String) pMapping.get(value);
}
else if (key.equals(".foreignKey")) {
// Foreign key
mForeignKey = (String) pMapping.get(value);
}
else if (key.equals(".join")) {
// Identity join
mIdentityJoin = (String) pMapping.get(key);
}
else if (key.endsWith(".primaryKey")) {
// Primary key in joining table
mPrimaryKeys.put(key.substring(0, dotIdx), value);
}
else if (key.endsWith(".foreignKey")) {
// Foreign key
mForeignKeys.put(key.substring(0, dotIdx), value);
}
else if (key.endsWith(".join")) {
// Joins
mJoins.put(key.substring(0, dotIdx), value);
}
else if (key.endsWith(".mapType")) {
// Maptypes
value = value.toLowerCase();
if (value.equals(DIRECTMAP) || value.equals(OBJECTMAP) ||
value.equals(COLLECTIONMAP) ||
value.equals(OBJCOLLMAP)) {
mMapTypes.put(key.substring(0, dotIdx), value);
}
else {
mLog.logError("Illegal mapType: \"" + value + "\"! "
+ "Legal types are: direct, object, "
+ "collection and objectCollection.");
}
}
else if (key.endsWith(".class")) {
// Classes
try {
mClasses.put(key.substring(0, dotIdx),
Class.forName(value));
}
catch (ClassNotFoundException e) {
mLog.logError(e);
//e.printStackTrace();
}
}
else if (key.endsWith(".collection")) {
// TODO!!
}
}
else {
// Property to column mappings
mPropertiesMap.put(key, value);
mColumnMap.put(value.substring(value.lastIndexOf(".") + 1),
key);
}
}
mMethods = new Hashtable();
Method[] methods = mInstanceClass.getMethods();
for (int i = 0; i < methods.length; i++) {
// Two methods CAN have same name...
mMethods.put(methods[i].getName(), methods[i]);
}
}
public void setPrimaryKey(String pPrimaryKey) {
mPrimaryKey = pPrimaryKey;
}
/**
* Gets the name of the property, that acts as the unique identifier for
* this ObjectMappers type.
*
* @return The name of the primary key property
*/
public String getPrimaryKey() {
return mPrimaryKey;
}
public String getForeignKey() {
return mForeignKey;
}
/**
* Gets the join, that is needed to find this ObjectMappers type.
*
* @return The name of the primary key property
*/
public String getIdentityJoin() {
return mIdentityJoin;
}
Hashtable getPropertyMapping(String pProperty) {
Hashtable mapping = new Hashtable();
if (pProperty != null) {
// Property
if (mPropertiesMap.containsKey(pProperty))
mapping.put("object", mPropertiesMap.get(pProperty));
// Primary key
if (mPrimaryKeys.containsKey(pProperty)) {
mapping.put(".primaryKey", "id");
mapping.put("id", mPrimaryKeys.get(pProperty));
}
//Foreign key
if (mForeignKeys.containsKey(pProperty))
mapping.put(".foreignKey", mPropertiesMap.get(mForeignKeys.get(pProperty)));
// Join
if (mJoins.containsKey(pProperty))
mapping.put(".join", mJoins.get(pProperty));
// mapType
mapping.put(".mapType", "object");
}
return mapping;
}
/**
* Gets the column for a given property.
*
* @param property The property
* @return The name of the matching database column, on the form
* table.column
*/
public String getColumn(String pProperty) {
if (mPropertiesMap == null || pProperty == null)
return null;
return (String) mPropertiesMap.get(pProperty);
}
/**
* Gets the table name for a given property.
*
* @param property The property
* @return The name of the matching database table.
*/
public String getTable(String pProperty) {
String table = getColumn(pProperty);
if (table != null) {
int dotIdx = 0;
if ((dotIdx = table.lastIndexOf(".")) >= 0)
table = table.substring(0, dotIdx);
else
return null;
}
return table;
}
/**
* Gets the property for a given database column. If the column incudes
* table qualifier, the table qualifier is removed.
*
* @param column The name of the column
* @return The name of the mathcing property
*/
public String getProperty(String pColumn) {
if (mColumnMap == null || pColumn == null)
return null;
String property = (String) mColumnMap.get(pColumn);
int dotIdx = 0;
if (property == null && (dotIdx = pColumn.lastIndexOf(".")) >= 0)
property = (String) mColumnMap.get(pColumn.substring(dotIdx + 1));
return property;
}
/**
* Maps each row of the given result set to an object ot this OM's type.
*
* @param rs The ResultSet to process (map to objects)
* @return An array of objects (of this OM's class). If there are no rows
* in the ResultSet, an empty (zero-length) array will be returned.
*/
public synchronized Object[] mapObjects(ResultSet pRSet) throws SQLException {
Vector result = new Vector();
ResultSetMetaData meta = pRSet.getMetaData();
int cols = meta.getColumnCount();
// Get colum names
String[] colNames = new String[cols];
for (int i = 0; i < cols; i++) {
colNames[i] = meta.getColumnName(i + 1); // JDBC cols start at 1...
/*
System.out.println(meta.getColumnLabel(i + 1));
System.out.println(meta.getColumnName(i + 1));
System.out.println(meta.getColumnType(i + 1));
System.out.println(meta.getColumnTypeName(i + 1));
// System.out.println(meta.getTableName(i + 1));
// System.out.println(meta.getCatalogName(i + 1));
// System.out.println(meta.getSchemaName(i + 1));
// Last three NOT IMPLEMENTED!!
*/
}
// Loop through rows in resultset
while (pRSet.next()) {
Object obj = null;
try {
obj = mInstanceClass.newInstance(); // Asserts empty constructor!
}
catch (IllegalAccessException iae) {
mLog.logError(iae);
// iae.printStackTrace();
}
catch (InstantiationException ie) {
mLog.logError(ie);
// ie.printStackTrace();
}
// Read each colum from this row into object
for (int i = 0; i < cols; i++) {
String property = (String) mColumnMap.get(colNames[i]);
if (property != null) {
// This column is mapped to a property
mapColumnProperty(pRSet, i + 1, property, obj);
}
}
// Add object to the result Vector
result.addElement(obj);
}
return result.toArray((Object[]) Array.newInstance(mInstanceClass,
result.size()));
}
/**
* Maps a ResultSet column (from the current ResultSet row) to a named
* property of an object, using reflection.
*
* @param rs The JDBC ResultSet
* @param index The column index to get the value from
* @param property The name of the property to set the value of
* @param obj The object to set the property to
*/
void mapColumnProperty(ResultSet pRSet, int pIndex, String pProperty,
Object pObj) {
if (pRSet == null || pProperty == null || pObj == null)
throw new IllegalArgumentException("ResultSet, Property or Object"
+ " arguments cannot be null!");
if (pIndex <= 0)
throw new IllegalArgumentException("Index parameter must be > 0!");
String methodName = "set" + StringUtil.capitalize(pProperty);
Method setMethod = (Method) mMethods.get(methodName);
if (setMethod == null) {
// No setMethod for this property
mLog.logError("No set method for property \""
+ pProperty + "\" in " + pObj.getClass() + "!");
return;
}
// System.err.println("DEBUG: setMethod=" + setMethod);
Method getMethod = null;
String type = "";
try {
Class[] cl = {Integer.TYPE};
type = setMethod.getParameterTypes()[0].getName();
type = type.substring(type.lastIndexOf(".") + 1);
// There is no getInteger, use getInt instead
if (type.equals("Integer")) {
type = "int";
}
// System.err.println("DEBUG: type=" + type);
getMethod = pRSet.getClass().
getMethod("get" + StringUtil.capitalize(type), cl);
}
catch (Exception e) {
mLog.logError("Can't find method \"get"
+ StringUtil.capitalize(type) + "(int)\" "
+ "(for class " + StringUtil.capitalize(type)
+ ") in ResultSet", e);
return;
}
try {
// Get the data from the DB
// System.err.println("DEBUG: " + getMethod.getName() + "(" + (i + 1) + ")");
Object[] colIdx = {new Integer(pIndex)};
Object[] arg = {getMethod.invoke(pRSet, colIdx)};
// Set it to the object
// System.err.println("DEBUG: " + setMethod.getName() + "(" + arg[0] + ")");
setMethod.invoke(pObj, arg);
}
catch (InvocationTargetException ite) {
mLog.logError(ite);
// ite.printStackTrace();
}
catch (IllegalAccessException iae) {
mLog.logError(iae);
// iae.printStackTrace();
}
}
/**
* Creates a SQL query string to get the primary keys for this
* ObjectMapper.
*/
String buildIdentitySQL(String[] pKeys) {
mTables = new Hashtable();
mColumns = new Vector();
// Get columns to select
mColumns.addElement(getPrimaryKey());
// Get tables to select (and join) from and their joins
tableJoins(null, false);
for (int i = 0; i < pKeys.length; i++) {
tableJoins(getColumn(pKeys[i]), true);
}
// All data read, build SQL query string
return "SELECT " + getPrimaryKey() + " " + buildFromClause()
+ buildWhereClause();
}
/**
* Creates a SQL query string to get objects for this ObjectMapper.
*/
public String buildSQL() {
mTables = new Hashtable();
mColumns = new Vector();
String key = null;
for (Enumeration keys = mPropertiesMap.keys(); keys.hasMoreElements();) {
key = (String) keys.nextElement();
// Get columns to select
String column = (String) mPropertiesMap.get(key);
mColumns.addElement(column);
tableJoins(column, false);
}
// All data read, build SQL query string
return buildSelectClause() + buildFromClause()
+ buildWhereClause();
}
/**
* Builds a SQL SELECT clause from the columns Vector
*/
private String buildSelectClause() {
StringBuilder sqlBuf = new StringBuilder();
sqlBuf.append("SELECT ");
String column = null;
for (Enumeration select = mColumns.elements(); select.hasMoreElements();) {
column = (String) select.nextElement();
/*
String subColumn = column.substring(column.indexOf(".") + 1);
// System.err.println("DEBUG: col=" + subColumn);
String mapType = (String) mMapTypes.get(mColumnMap.get(subColumn));
*/
String mapType = (String) mMapTypes.get(getProperty(column));
if (mapType == null || mapType.equals(DIRECTMAP)) {
sqlBuf.append(column);
sqlBuf.append(select.hasMoreElements() ? ", " : " ");
}
}
return sqlBuf.toString();
}
/**
* Builds a SQL FROM clause from the tables/joins Hashtable
*/
private String buildFromClause() {
StringBuilder sqlBuf = new StringBuilder();
sqlBuf.append("FROM ");
String table = null;
String schema = null;
for (Enumeration from = mTables.keys(); from.hasMoreElements();) {
table = (String) from.nextElement();
/*
schema = (String) schemas.get(table);
if (schema != null)
sqlBuf.append(schema + ".");
*/
sqlBuf.append(table);
sqlBuf.append(from.hasMoreElements() ? ", " : " ");
}
return sqlBuf.toString();
}
/**
* Builds a SQL WHERE clause from the tables/joins Hashtable
*
* @return Currently, this metod will return "WHERE 1 = 1", if no other
* WHERE conditions are specified. This can be considered a hack.
*/
private String buildWhereClause() {
StringBuilder sqlBuf = new StringBuilder();
String join = null;
boolean first = true;
for (Enumeration where = mTables.elements(); where.hasMoreElements();) {
join = (String) where.nextElement();
if (join.length() > 0) {
if (first) {
// Skip " AND " in first iteration
first = false;
}
else {
sqlBuf.append(" AND ");
}
}
sqlBuf.append(join);
}
if (sqlBuf.length() > 0)
return "WHERE " + sqlBuf.toString();
return "WHERE 1 = 1"; // Hacky...
}
/**
* Finds tables used in mappings and joins and adds them to the tables
* Hashtable, with the table name as key, and the join as value.
*/
private void tableJoins(String pColumn, boolean pWhereJoin) {
String join = null;
String table = null;
if (pColumn == null) {
// Identity
join = getIdentityJoin();
table = getTable(getProperty(getPrimaryKey()));
}
else {
// Normal
int dotIndex = -1;
if ((dotIndex = pColumn.lastIndexOf(".")) <= 0) {
// No table qualifier
return;
}
// Get table qualifier.
table = pColumn.substring(0, dotIndex);
// Don't care about the tables that are not supposed to be selected from
String property = (String) getProperty(pColumn);
if (property != null) {
String mapType = (String) mMapTypes.get(property);
if (!pWhereJoin && mapType != null && !mapType.equals(DIRECTMAP)) {
return;
}
join = (String) mJoins.get(property);
}
}
// If table is not in the tables Hash, add it, and check for joins.
if (mTables.get(table) == null) {
if (join != null) {
mTables.put(table, join);
StringTokenizer tok = new StringTokenizer(join, "= ");
String next = null;
while(tok.hasMoreElements()) {
next = tok.nextToken();
// Don't care about SQL keywords
if (next.equals("AND") || next.equals("OR")
|| next.equals("NOT") || next.equals("IN")) {
continue;
}
// Check for new tables and joins in this join clause.
tableJoins(next, false);
}
}
else {
// No joins for this table.
join = "";
mTables.put(table, join);
}
}
}
}

View File

@@ -0,0 +1,879 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.SystemUtil;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
/**
* Class used for reading table data from a database through JDBC, and map
* the data to Java classes.
*
* @see ObjectMapper
*
* @author Harald Kuhr (haraldk@iconmedialab.no)
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/sql/ObjectReader.java#1 $
*
* @todo Use JDK logging instead of proprietary logging.
*
*/
public class ObjectReader {
/**
* Main method, for testing purposes only.
*/
public final static void main(String[] pArgs) throws SQLException {
/*
System.err.println("Testing only!");
// Get default connection
ObjectReader obr = new ObjectReader(DatabaseConnection.getConnection());
com.twelvemonkeys.usedcars.DBCar car = new com.twelvemonkeys.usedcars.DBCar(new Integer(1));
com.twelvemonkeys.usedcars.DBDealer dealer = new com.twelvemonkeys.usedcars.DBDealer("NO4537");
System.out.println(obr.readObject(dealer));
com.twelvemonkeys.usedcars.Dealer[] dealers = (com.twelvemonkeys.usedcars.Dealer[]) obr.readObjects(dealer);
for (int i = 0; i < dealers.length; i++) {
System.out.println(dealers[i]);
}
System.out.println("------------------------------------------------------------------------------\n"
+ "Total: " + dealers.length + " dealers in database\n");
Hashtable where = new Hashtable();
where.put("zipCode", "0655");
dealers = (com.twelvemonkeys.usedcars.Dealer[]) obr.readObjects(dealer, where);
for (int i = 0; i < dealers.length; i++) {
System.out.println(dealers[i]);
}
System.out.println("------------------------------------------------------------------------------\n"
+ "Total: " + dealers.length + " dealers matching query: "
+ where + "\n");
com.twelvemonkeys.usedcars.Car[] cars = null;
cars = (com.twelvemonkeys.usedcars.Car[]) obr.readObjects(car);
for (int i = 0; i < cars.length; i++) {
System.out.println(cars[i]);
}
System.out.println("------------------------------------------------------------------------------\n"
+ "Total: " + cars.length + " cars in database\n");
where = new Hashtable();
where.put("year", new Integer(1995));
cars = (com.twelvemonkeys.usedcars.Car[]) obr.readObjects(car, where);
for (int i = 0; i < cars.length; i++) {
System.out.println(cars[i]);
}
System.out.println("------------------------------------------------------------------------------\n"
+ "Total: " + cars.length + " cars matching query: "
+ where + " \n");
where = new Hashtable();
where.put("publishers", "Bilguiden");
cars = (com.twelvemonkeys.usedcars.Car[]) obr.readObjects(car, where);
for (int i = 0; i < cars.length; i++) {
System.out.println(cars[i]);
}
System.out.println("------------------------------------------------------------------------------\n"
+ "Total: " + cars.length + " cars matching query: "
+ where + "\n");
System.out.println("==============================================================================\n"
+ getStats());
*/
}
protected Log mLog = null;
protected Properties mConfig = null;
/**
* The connection used for all database operations executed by this
* ObjectReader.
*/
Connection mConnection = null;
/**
* The cache for this ObjectReader.
* Probably a source for memory leaks, as it has no size limitations.
*/
private Hashtable mCache = new Hashtable();
/**
* Creates a new ObjectReader, using the given JDBC Connection. The
* Connection will be used for all database reads by this ObjectReader.
*
* @param connection A JDBC Connection
*/
public ObjectReader(Connection pConnection) {
mConnection = pConnection;
try {
mConfig = SystemUtil.loadProperties(getClass());
}
catch (FileNotFoundException fnf) {
// Just go with defaults
}
catch (IOException ioe) {
new Log(this).logError(ioe);
}
mLog = new Log(this, mConfig);
}
/**
* Gets a string containing the stats for this ObjectReader.
*
* @return A string to display the stats.
*/
public static String getStats() {
long total = sCacheHit + sCacheMiss + sCacheUn;
double hit = ((double) sCacheHit / (double) total) * 100.0;
double miss = ((double) sCacheMiss / (double) total) * 100.0;
double un = ((double) sCacheUn / (double) total) * 100.0;
// Default locale
java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
return "Total: " + total + " reads. "
+ "Cache hits: " + sCacheHit + " (" + nf.format(hit) + "%), "
+ "Cache misses: " + sCacheMiss + " (" + nf.format(miss) + "%), "
+ "Unattempted: " + sCacheUn + " (" + nf.format(un) + "%) ";
}
/**
* Get an array containing Objects of type objClass, with the
* identity values for the given class set.
*/
private Object[] readIdentities(Class pObjClass, Hashtable pMapping,
Hashtable pWhere, ObjectMapper pOM)
throws SQLException {
sCacheUn++;
// Build SQL query string
if (pWhere == null)
pWhere = new Hashtable();
String[] keys = new String[pWhere.size()];
int i = 0;
for (Enumeration en = pWhere.keys(); en.hasMoreElements(); i++) {
keys[i] = (String) en.nextElement();
}
// Get SQL for reading identity column
String sql = pOM.buildIdentitySQL(keys)
+ buildWhereClause(keys, pMapping);
// Log?
mLog.logDebug(sql + " (" + pWhere + ")");
// Prepare statement and set values
PreparedStatement statement = mConnection.prepareStatement(sql);
for (int j = 0; j < keys.length; j++) {
Object key = pWhere.get(keys[j]);
if (key instanceof Integer)
statement.setInt(j + 1, ((Integer) key).intValue());
else if (key instanceof BigDecimal)
statement.setBigDecimal(j + 1, (BigDecimal) key);
else
statement.setString(j + 1, key.toString());
}
// Execute query
ResultSet rs = null;
try {
rs = statement.executeQuery();
}
catch (SQLException e) {
mLog.logError(sql + " (" + pWhere + ")", e);
throw e;
}
Vector result = new Vector();
// Map query to objects
while (rs.next()) {
Object obj = null;
try {
obj = pObjClass.newInstance();
}
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (InstantiationException ie) {
ie.printStackTrace();
}
// Map it
pOM.mapColumnProperty(rs, 1,
pOM.getProperty(pOM.getPrimaryKey()), obj);
result.addElement(obj);
}
// Return array of identifiers
return result.toArray((Object[]) Array.newInstance(pObjClass,
result.size()));
}
/**
* Reads one object implementing the DatabaseReadable interface from the
* database.
*
* @param readable A DatabaseReadable object
* @return The Object read, or null in no object is found
*/
public Object readObject(DatabaseReadable pReadable) throws SQLException {
return readObject(pReadable.getId(), pReadable.getClass(),
pReadable.getMapping());
}
/**
* Reads the object with the given id from the database, using the given
* mapping.
*
* @param id An object uniquely identifying the object to read
* @param objClass The clas
* @return The Object read, or null in no object is found
*/
public Object readObject(Object pId, Class pObjClass, Hashtable pMapping)
throws SQLException {
return readObject(pId, pObjClass, pMapping, null);
}
/**
* Reads all the objects of the given type from the
* database. The object must implement the DatabaseReadable interface.
*
* @return An array of Objects, or an zero-length array if none was found
*/
public Object[] readObjects(DatabaseReadable pReadable)
throws SQLException {
return readObjects(pReadable.getClass(),
pReadable.getMapping(), null);
}
/**
* Sets the property value to an object using reflection
*
* @param obj The object to get a property from
* @param property The name of the property
* @param value The property value
*
*/
private void setPropertyValue(Object pObj, String pProperty,
Object pValue) {
Method m = null;
Class[] cl = {pValue.getClass()};
try {
//Util.setPropertyValue(pObj, pProperty, pValue);
// Find method
m = pObj.getClass().
getMethod("set" + StringUtil.capitalize(pProperty), cl);
// Invoke it
Object[] args = {pValue};
m.invoke(pObj, args);
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (InvocationTargetException ite) {
ite.printStackTrace();
}
}
/**
* Gets the property value from an object using reflection
*
* @param obj The object to get a property from
* @param property The name of the property
*
* @return The property value as an Object
*/
private Object getPropertyValue(Object pObj, String pProperty) {
Method m = null;
Class[] cl = new Class[0];
try {
//return Util.getPropertyValue(pObj, pProperty);
// Find method
m = pObj.getClass().
getMethod("get" + StringUtil.capitalize(pProperty),
new Class[0]);
// Invoke it
Object result = m.invoke(pObj, new Object[0]);
return result;
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (InvocationTargetException ite) {
ite.printStackTrace();
}
return null;
}
/**
* Reads and sets the child properties of the given parent object.
*
* @param parent The object to set the child obects to.
* @param om The ObjectMapper of the parent object.
*/
private void setChildObjects(Object pParent, ObjectMapper pOM)
throws SQLException {
if (pOM == null) {
throw new NullPointerException("ObjectMapper in readChildObjects "
+ "cannot be null!!");
}
for (Enumeration keys = pOM.mMapTypes.keys(); keys.hasMoreElements();) {
String property = (String) keys.nextElement();
String mapType = (String) pOM.mMapTypes.get(property);
if (property.length() <= 0 || mapType == null) {
continue;
}
// Get the id of the parent
Object id = getPropertyValue(pParent,
pOM.getProperty(pOM.getPrimaryKey()));
if (mapType.equals(ObjectMapper.OBJECTMAP)) {
// OBJECT Mapping
// Get the class for this property
Class objectClass = (Class) pOM.mClasses.get(property);
DatabaseReadable dbr = null;
try {
dbr = (DatabaseReadable) objectClass.newInstance();
}
catch (Exception e) {
mLog.logError(e);
}
/*
Properties mapping = readMapping(objectClass);
*/
// Get property mapping for child object
if (pOM.mJoins.containsKey(property))
// mapping.setProperty(".join", (String) pOM.joins.get(property));
dbr.getMapping().put(".join", pOM.mJoins.get(property));
// Find id and put in where hash
Hashtable where = new Hashtable();
// String foreignKey = mapping.getProperty(".foreignKey");
String foreignKey = (String)
dbr.getMapping().get(".foreignKey");
if (foreignKey != null) {
where.put(".foreignKey", id);
}
Object[] child = readObjects(dbr, where);
// Object[] child = readObjects(objectClass, mapping, where);
if (child.length < 1)
throw new SQLException("No child object with foreign key "
+ foreignKey + "=" + id);
else if (child.length != 1)
throw new SQLException("More than one object with foreign "
+ "key " + foreignKey + "=" + id);
// Set child object to the parent
setPropertyValue(pParent, property, child[0]);
}
else if (mapType.equals(ObjectMapper.COLLECTIONMAP)) {
// COLLECTION Mapping
// Get property mapping for child object
Hashtable mapping = pOM.getPropertyMapping(property);
// Find id and put in where hash
Hashtable where = new Hashtable();
String foreignKey = (String) mapping.get(".foreignKey");
if (foreignKey != null) {
where.put(".foreignKey", id);
}
DBObject dbr = new DBObject();
dbr.mapping = mapping; // ugh...
// Read the objects
Object[] objs = readObjects(dbr, where);
// Put the objects in a hash
Hashtable children = new Hashtable();
for (int i = 0; i < objs.length; i++) {
children.put(((DBObject) objs[i]).getId(),
((DBObject) objs[i]).getObject());
}
// Set child properties to parent object
setPropertyValue(pParent, property, children);
}
}
}
/**
* Reads all objects from the database, using the given mapping.
*
* @param objClass The class of the objects to read
* @param mapping The hashtable containing the object mapping
*
* @return An array of Objects, or an zero-length array if none was found
*/
public Object[] readObjects(Class pObjClass, Hashtable pMapping)
throws SQLException {
return readObjects(pObjClass, pMapping, null);
}
/**
* Builds extra SQL WHERE clause
*
* @param keys An array of ID names
* @param mapping The hashtable containing the object mapping
*
* @return A string containing valid SQL
*/
private String buildWhereClause(String[] pKeys, Hashtable pMapping) {
StringBuilder sqlBuf = new StringBuilder();
for (int i = 0; i < pKeys.length; i++) {
String column = (String) pMapping.get(pKeys[i]);
sqlBuf.append(" AND ");
sqlBuf.append(column);
sqlBuf.append(" = ?");
}
return sqlBuf.toString();
}
private String buildIdInClause(Object[] pIds, Hashtable pMapping) {
StringBuilder sqlBuf = new StringBuilder();
if (pIds != null && pIds.length > 0) {
sqlBuf.append(" AND ");
sqlBuf.append(pMapping.get(".primaryKey"));
sqlBuf.append(" IN (");
for (int i = 0; i < pIds.length; i++) {
sqlBuf.append(pIds[i]); // SETTE INN '?' ???
sqlBuf.append(", ");
}
sqlBuf.append(")");
}
return sqlBuf.toString();
}
/**
* Reads all objects from the database, using the given mapping.
*
* @param readable A DatabaseReadable object
* @param mapping The hashtable containing the object mapping
*
* @return An array of Objects, or an zero-length array if none was found
*/
public Object[] readObjects(DatabaseReadable pReadable, Hashtable pWhere)
throws SQLException {
return readObjects(pReadable.getClass(),
pReadable.getMapping(), pWhere);
}
/**
* Reads the object with the given id from the database, using the given
* mapping.
* This is the most general form of readObject().
*
* @param id An object uniquely identifying the object to read
* @param objClass The class of the object to read
* @param mapping The hashtable containing the object mapping
* @param where An hashtable containing extra criteria for the read
*
* @return An array of Objects, or an zero-length array if none was found
*/
public Object readObject(Object pId, Class pObjClass,
Hashtable pMapping, Hashtable pWhere)
throws SQLException {
ObjectMapper om = new ObjectMapper(pObjClass, pMapping);
return readObject0(pId, pObjClass, om, pWhere);
}
public Object readObjects(Object[] pIds, Class pObjClass,
Hashtable pMapping, Hashtable pWhere)
throws SQLException {
ObjectMapper om = new ObjectMapper(pObjClass, pMapping);
return readObjects0(pIds, pObjClass, om, pWhere);
}
/**
* Reads all objects from the database, using the given mapping.
* This is the most general form of readObjects().
*
* @param objClass The class of the objects to read
* @param mapping The hashtable containing the object mapping
* @param where An hashtable containing extra criteria for the read
*
* @return An array of Objects, or an zero-length array if none was found
*/
public Object[] readObjects(Class pObjClass, Hashtable pMapping,
Hashtable pWhere) throws SQLException {
return readObjects0(pObjClass, pMapping, pWhere);
}
// readObjects implementation
private Object[] readObjects0(Class pObjClass, Hashtable pMapping,
Hashtable pWhere) throws SQLException {
ObjectMapper om = new ObjectMapper(pObjClass, pMapping);
Object[] ids = readIdentities(pObjClass, pMapping, pWhere, om);
Object[] result = readObjects0(ids, pObjClass, om, pWhere);
return result;
}
private Object[] readObjects0(Object[] pIds, Class pObjClass,
ObjectMapper pOM, Hashtable pWhere)
throws SQLException {
Object[] result = new Object[pIds.length];
// Read each object from ID
for (int i = 0; i < pIds.length; i++) {
// TODO: For better cahce efficiency/performance:
// - Read as many objects from cache as possible
// - Read all others in ONE query, and add to cache
/*
sCacheUn++;
// Build SQL query string
if (pWhere == null)
pWhere = new Hashtable();
String[] keys = new String[pWhere.size()];
int i = 0;
for (Enumeration en = pWhere.keys(); en.hasMoreElements(); i++) {
keys[i] = (String) en.nextElement();
}
// Get SQL for reading identity column
String sql = pOM.buildSelectClause() + pOM.buildFromClause() +
+ buildWhereClause(keys, pMapping) + buildIdInClause(pIds, pMapping);
// Log?
mLog.logDebug(sql + " (" + pWhere + ")");
// Log?
mLog.logDebug(sql + " (" + pWhere + ")");
PreparedStatement statement = null;
// Execute query, and map columns/properties
try {
statement = mConnection.prepareStatement(sql);
// Set keys
for (int j = 0; j < keys.length; j++) {
Object value = pWhere.get(keys[j]);
if (value instanceof Integer)
statement.setInt(j + 1, ((Integer) value).intValue());
else if (value instanceof BigDecimal)
statement.setBigDecimal(j + 1, (BigDecimal) value);
else
statement.setString(j + 1, value.toString());
}
// Set ids
for (int j = 0; j < pIds.length; j++) {
Object id = pIds[i];
if (id instanceof Integer)
statement.setInt(j + 1, ((Integer) id).intValue());
else if (id instanceof BigDecimal)
statement.setBigDecimal(j + 1, (BigDecimal) id);
else
statement.setString(j + 1, id.toString());
}
ResultSet rs = statement.executeQuery();
Object[] result = pOM.mapObjects(rs);
// Set child objects and return
for (int i = 0; i < result.length; i++) {
// FOR THIS TO REALLY GET EFFECTIVE, WE NEED TO SET ALL
// CHILDREN IN ONE GO!
setChildObjects(result[i], pOM);
mContent.put(pOM.getPrimaryKey() + "=" + pId, result[0]);
}
// Return result
return result[0];
}
*/
Object id = getPropertyValue(result[i],
pOM.getProperty(pOM.getPrimaryKey()));
result[i] = readObject0(id, pObjClass, pOM, null);
}
return result;
}
// readObject implementation, used for ALL database reads
static long sCacheHit;
static long sCacheMiss;
static long sCacheUn;
private Object readObject0(Object pId, Class pObjClass, ObjectMapper pOM,
Hashtable pWhere) throws SQLException {
if (pId == null && pWhere == null)
throw new IllegalArgumentException("Either id or where argument"
+ "must be non-null!");
// First check if object exists in cache
if (pId != null) {
Object o = mCache.get(pOM.getPrimaryKey() + "=" + pId);
if (o != null) {
sCacheHit++;
return o;
}
sCacheMiss++;
}
else {
sCacheUn++;
}
// Create where hash
if (pWhere == null)
pWhere = new Hashtable();
// Make sure the ID is in the where hash
if (pId != null)
pWhere.put(pOM.getProperty(pOM.getPrimaryKey()), pId);
String[] keys = new String[pWhere.size()];
Enumeration en = pWhere.keys();
for (int i = 0; en.hasMoreElements(); i++) {
keys[i] = (String) en.nextElement();
}
// Get SQL query string
String sql = pOM.buildSQL() + buildWhereClause(keys, pOM.mPropertiesMap);
// Log?
mLog.logDebug(sql + " (" + pWhere + ")");
PreparedStatement statement = null;
// Execute query, and map columns/properties
try {
statement = mConnection.prepareStatement(sql);
for (int j = 0; j < keys.length; j++) {
Object value = pWhere.get(keys[j]);
if (value instanceof Integer)
statement.setInt(j + 1, ((Integer) value).intValue());
else if (value instanceof BigDecimal)
statement.setBigDecimal(j + 1, (BigDecimal) value);
else
statement.setString(j + 1, value.toString());
}
ResultSet rs = statement.executeQuery();
Object[] result = pOM.mapObjects(rs);
// Set child objects and return
if (result.length == 1) {
setChildObjects(result[0], pOM);
mCache.put(pOM.getPrimaryKey() + "=" + pId, result[0]);
// Return result
return result[0];
}
// More than 1 is an error...
else if (result.length > 1) {
throw new SQLException("More than one object with primary key "
+ pOM.getPrimaryKey() + "="
+ pWhere.get(pOM.getProperty(pOM.getPrimaryKey())) + "!");
}
}
catch (SQLException e) {
mLog.logError(sql + " (" + pWhere + ")", e);
throw e;
}
finally {
try {
statement.close();
}
catch (SQLException e) {
mLog.logError(e);
}
}
return null;
}
/**
* Utility method for reading a property mapping from a properties-file
*
*/
public static Properties loadMapping(Class pClass) {
try {
return SystemUtil.loadProperties(pClass);
}
catch (FileNotFoundException fnf) {
// System.err... err...
System.err.println("ERROR: " + fnf.getMessage());
}
catch (IOException ioe) {
ioe.printStackTrace();
}
return new Properties();
}
/**
* @deprecated Use loadMapping(Class) instead
* @see #loadMapping(Class)
*/
public static Properties readMapping(Class pClass) {
return loadMapping(pClass);
}
}
/**
* Utility class
*/
class DBObject implements DatabaseReadable {
Object id;
Object o;
static Hashtable mapping; // WHOA, STATIC!?!?
public DBObject() {
}
public void setId(Object id) {
this.id = id;
}
public Object getId() {
return id;
}
public void setObject(Object o) {
this.o = o;
}
public Object getObject() {
return o;
}
public Hashtable getMapping() {
return mapping;
}
}

View File

@@ -0,0 +1,396 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.sql;
import com.twelvemonkeys.lang.StringUtil;
import java.sql.*;
import java.io.*;
import java.util.Properties;
/**
* A class used to test a JDBC database connection. It can also be used as a
* really simple form of command line SQL interface, that passes all command
* line parameters to the database as plain SQL, and returns all rows to
* Sytem.out. <EM>Be aware that the wildcard character (*) is intercepted by
* the console, so you have to quote your string, or escape the wildcard
* character, otherwise you may get unpredictable results.</EM>
* <P/>
* <STRONG>Exmaple use</STRONG>
* <BR/>
* <PRE>
* $ java -cp lib\jconn2.jar;build com.twelvemonkeys.sql.SQLUtil
* -d com.sybase.jdbc2.jdbc.SybDriver -u "jdbc:sybase:Tds:10.248.136.42:6100"
* -l scott -p tiger "SELECT * FROM emp"</PRE>
* <EM>Make sure sure to include the path to your JDBC driver in the java class
* path!</EM>
*
* @author Philippe Béal (phbe@iconmedialab.no)
* @author Harald Kuhr (haraldk@iconmedialab.no)
* @author last modified by $author: WMHAKUR $
* @version $id: $
* @see DatabaseConnection
*/
public class SQLUtil {
/**
* Method main
*
* @param pArgs
* @throws SQLException
*
* @todo Refactor the long and ugly main method...
* Consider: - extract method parserArgs(String[])::Properties (how do we
* get the rest of the arguments? getProperty("_ARGV")?
* Make the Properties/Map an argument and return int with last
* option index?
* - extract method getStatementReader(Properties)
*/
public static void main(String[] pArgs) throws SQLException, IOException {
String user = null;
String password = null;
String url = null;
String driver = null;
String configFileName = null;
String scriptFileName = null;
String scriptSQLDelim = "go";
int argIdx = 0;
boolean errArgs = false;
while ((argIdx < pArgs.length) && (pArgs[argIdx].charAt(0) == '-') && (pArgs[argIdx].length() >= 2)) {
if ((pArgs[argIdx].charAt(1) == 'l') || pArgs[argIdx].equals("--login")) {
argIdx++;
user = pArgs[argIdx++];
}
else if ((pArgs[argIdx].charAt(1) == 'p') || pArgs[argIdx].equals("--password")) {
argIdx++;
password = pArgs[argIdx++];
}
else if ((pArgs[argIdx].charAt(1) == 'u') || pArgs[argIdx].equals("--url")) {
argIdx++;
url = pArgs[argIdx++];
}
else if ((pArgs[argIdx].charAt(1) == 'd') || pArgs[argIdx].equals("--driver")) {
argIdx++;
driver = pArgs[argIdx++];
}
else if ((pArgs[argIdx].charAt(1) == 'c') || pArgs[argIdx].equals("--config")) {
argIdx++;
configFileName = pArgs[argIdx++];
}
else if ((pArgs[argIdx].charAt(1) == 's') || pArgs[argIdx].equals("--script")) {
argIdx++;
scriptFileName = pArgs[argIdx++];
}
else if ((pArgs[argIdx].charAt(1) == 'h') || pArgs[argIdx].equals("--help")) {
argIdx++;
errArgs = true;
}
else {
System.err.println("Unknown option \"" + pArgs[argIdx++] + "\"");
}
}
if (errArgs || (scriptFileName == null && (pArgs.length < (argIdx + 1)))) {
System.err.println("Usage: SQLUtil [--help|-h] [--login|-l <login-name>] [--password|-p <password>] [--driver|-d <jdbc-driver-class>] [--url|-u <connect url>] [--config|-c <config-file>] [--script|-s <script-file>] <sql statement> ");
System.exit(5);
}
// If config file, read config and use as defaults
// NOTE: Command line options override!
if (!StringUtil.isEmpty(configFileName)) {
Properties config = new Properties();
File configFile = new File(configFileName);
if (!configFile.exists()) {
System.err.println("Config file " + configFile.getAbsolutePath() + " does not exist.");
System.exit(10);
}
InputStream in = new FileInputStream(configFile);
try {
config.load(in);
}
finally {
in.close();
}
if (driver == null) {
driver = config.getProperty("driver");
}
if (url == null) {
url = config.getProperty("url");
}
if (user == null) {
user = config.getProperty("login");
}
if (password == null) {
password = config.getProperty("password");
}
}
// Register JDBC driver
if (driver != null) {
registerDriver(driver);
}
Connection conn = null;
try {
// Use default connection from DatabaseConnection.properties
conn = DatabaseConnection.getConnection(user, password, url);
if (conn == null) {
System.err.println("No connection.");
System.exit(10);
}
BufferedReader reader;
if (scriptFileName != null) {
// Read SQL from file
File file = new File(scriptFileName);
if (!file.exists()) {
System.err.println("Script file " + file.getAbsolutePath() + " does not exist.");
System.exit(10);
}
reader = new BufferedReader(new FileReader(file));
}
else {
// Create SQL statement from command line params
StringBuilder sql = new StringBuilder();
for (int i = argIdx; i < pArgs.length; i++) {
sql.append(pArgs[i]).append(" ");
}
reader = new BufferedReader(new StringReader(sql.toString()));
}
//reader.mark(10000000);
//for (int i = 0; i < 5; i++) {
StringBuilder sql = new StringBuilder();
while (true) {
// Read next line
String line = reader.readLine();
if (line == null) {
// End of file, execute and quit
String str = sql.toString();
if (!StringUtil.isEmpty(str)) {
executeSQL(str, conn);
}
break;
}
else if (line.trim().endsWith(scriptSQLDelim)) {
// End of statement, execute and continue
sql.append(line.substring(0, line.lastIndexOf(scriptSQLDelim)));
executeSQL(sql.toString(), conn);
sql.setLength(0);
}
else {
sql.append(line).append(" ");
}
}
//reader.reset();
//}
}
finally {
// Close the connection
if (conn != null) {
conn.close();
}
}
}
private static void executeSQL(String pSQL, Connection pConn) throws SQLException {
System.out.println("Executing: " + pSQL);
Statement stmt = null;
try {
// NOTE: Experimental
//stmt = pConn.prepareCall(pSQL);
//boolean results = ((CallableStatement) stmt).execute();
// Create statement and execute
stmt = pConn.createStatement();
boolean results = stmt.execute(pSQL);
int updateCount = -1;
SQLWarning warning = stmt.getWarnings();
while (warning != null) {
System.out.println("Warning: " + warning.getMessage());
warning = warning.getNextWarning();
}
// More result sets to process?
while (results || (updateCount = stmt.getUpdateCount()) != -1) {
// INSERT, UPDATE or DELETE statement (no result set).
if (!results && (updateCount >= 0)) {
System.out.println("Operation successfull. " + updateCount + " row" + ((updateCount != 1) ? "s" : "") + " affected.");
System.out.println();
}
// SELECT statement or stored procedure
else {
processResultSet(stmt.getResultSet());
}
// More results?
results = stmt.getMoreResults();
}
}
catch (SQLException sqle) {
System.err.println("Error: " + sqle.getMessage());
while ((sqle = sqle.getNextException()) != null) {
System.err.println(" " + sqle);
}
}
finally {
// Close the statement
if (stmt != null) {
stmt.close();
}
}
}
// TODO: Create interface ResultSetProcessor
// -- processWarnings(SQLWarning pWarnings);
// -- processMetaData(ResultSetMetaData pMetas); ??
// -- processResultSet(ResultSet pResult);
// TODO: Add parameter pResultSetProcessor to method
// TODO: Extract contents of this method to class Default/CLIRSP
// TODO: Create new class JTableRSP that creates (?) and populates a JTable
// or a TableModel (?)
private static void processResultSet(ResultSet pResultSet) throws SQLException {
try {
// Get meta data
ResultSetMetaData meta = pResultSet.getMetaData();
// Print any warnings that might have occured
SQLWarning warning = pResultSet.getWarnings();
while (warning != null) {
System.out.println("Warning: " + warning.getMessage());
warning = warning.getNextWarning();
}
// Get the number of columns in the result set
int numCols = meta.getColumnCount();
for (int i = 1; i <= numCols; i++) {
boolean prepend = isNumeric(meta.getColumnType(i));
String label = maybePad(meta.getColumnLabel(i), meta.getColumnDisplaySize(i), " ", prepend);
System.out.print(label + "\t");
}
System.out.println();
for (int i = 1; i <= numCols; i++) {
boolean prepend = isNumeric(meta.getColumnType(i));
String label = maybePad("(" + meta.getColumnTypeName(i) + "/" + meta.getColumnClassName(i) + ")", meta.getColumnDisplaySize(i), " ", prepend);
System.out.print(label + "\t");
}
System.out.println();
for (int i = 1; i <= numCols; i++) {
String label = maybePad("", meta.getColumnDisplaySize(i), "-", false);
System.out.print(label + "\t");
}
System.out.println();
while (pResultSet.next()) {
for (int i = 1; i <= numCols; i++) {
boolean prepend = isNumeric(meta.getColumnType(i));
String value = maybePad(String.valueOf(pResultSet.getString(i)), meta.getColumnDisplaySize(i), " ", prepend);
System.out.print(value + "\t");
//System.out.print(pResultSet.getString(i) + "\t");
}
System.out.println();
}
System.out.println();
}
catch (SQLException sqle) {
System.err.println("Error: " + sqle.getMessage());
while ((sqle = sqle.getNextException()) != null) {
System.err.println(" " + sqle);
}
throw sqle;
}
finally {
if (pResultSet != null) {
pResultSet.close();
}
}
}
private static String maybePad(String pString, int pColumnDisplaySize, String pPad, boolean pPrepend) {
String padded;
if (pColumnDisplaySize < 100) {
padded = StringUtil.pad(pString, pColumnDisplaySize, pPad, pPrepend);
}
else {
padded = StringUtil.pad(pString, 100, pPad, pPrepend);
}
return padded;
}
private static boolean isNumeric(int pColumnType) {
return (pColumnType == Types.INTEGER || pColumnType == Types.DECIMAL
|| pColumnType == Types.TINYINT || pColumnType == Types.BIGINT
|| pColumnType == Types.DOUBLE || pColumnType == Types.FLOAT
|| pColumnType == Types.NUMERIC || pColumnType == Types.REAL
|| pColumnType == Types.SMALLINT);
}
public static boolean isDriverAvailable(String pDriver) {
//ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
Class.forName(pDriver, false, null); // null means the caller's ClassLoader
return true;
}
catch (ClassNotFoundException ignore) {
// Ignore
}
return false;
}
public static void registerDriver(String pDriver) {
// Register JDBC driver
try {
Class.forName(pDriver).newInstance();
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Driver class not found: " + e.getMessage(), e);
//System.err.println("Driver class not found: " + e.getMessage());
//System.exit(5);
}
catch (InstantiationException e) {
throw new RuntimeException("Driver class could not be instantiated: " + e.getMessage(), e);
//System.err.println("Driver class could not be instantiated: " + e.getMessage());
//System.exit(5);
}
catch (IllegalAccessException e) {
throw new RuntimeException("Driver class could not be instantiated: " + e.getMessage(), e);
//System.err.println("Driver class could not be instantiated: " + e.getMessage());
//System.exit(5);
}
}
}

View File

@@ -0,0 +1,12 @@
<HTML>
<BODY>
Provides classes for database access through JDBC.
The package contains warious mechanisms to et connections, read (currently) and write (future) objects from a database, etc.
@see java.sql
@see com.twelvemonkeys.sql.ObjectReader
@see com.twelvemonkeys.sql.DatabaseConnection
</BODY>
</HTML>