helma/modules/helma/Database.js
2012-03-27 11:46:35 +02:00

341 lines
12 KiB
JavaScript

/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2006 Helma Software. All Rights Reserved.
*
* $RCSfile: Database.js,v $
* $Author$
* $Revision$
* $Date$
*/
/**
* @fileoverview Properties and methods of the helma.Database prototype.
* <br /><br />
* To use this optional module, its repository needs to be added to the
* application, for example by calling app.addRepository('modules/helma/Database.js')
*/
if (!global.helma) {
global.helma = {};
}
/**
* Constructor for Database objects, providing access through relational
* databases through JDBC. It is usually simpler to use one of the factory
* methods {@link #createInstance} or {@link #getInstance}.
* @class <p>This class provides access to a relational database through JDBC.
* There are two convenient ways to create instances of this class.</p>
*
* <p>The first is to use {@link #getInstance helma.Database.getInstance()}
* to obtain a connection to a DB that is defined in the application's
* db.properties and managed by Helma. The second way is to define and create
* a database connection locally using
* {@link #createInstance helma.Database.createInstance()} and passing it
* all necessary parameters.</p>
*
* <p>This class provides two ways of interaction:
* The {@link #query} method allows to issue SQL queries, returning a result set.
* The {@link #execute} provides a way to issue SQL statements that do not
* return a result set.</p>
*
* <p>Database connections allocated by this class are be managed and eventually
* disposed by Helma.</p>
*
* @param {DbSource} source instance of a helma.objectmodel.db.DbSource
* @constructor
*/
helma.Database = function(source) {
var Types = java.sql.Types;
var DbSource = Packages.helma.objectmodel.db.DbSource;
if (typeof(source) == "string")
source = app.getDbSource(source);
if (!(source instanceof DbSource))
throw "helma.Database requires a helma.objectmodel.db.DbSource argument";
/**
* Get the java.sql.Connection for this Database instance. This can be used
* to operate on the connection directly, without going through the helma.Database
* class.
* @return {java.sql.Connection} the JDBC connection
*/
this.getConnection = function() {
return source.getConnection();
};
/**
* Returns the lower case name of the underlying database product.
* @return {String} the name of the DB product
*/
this.getProductName = function() {
return source.getConnection().getMetaData().getDatabaseProductName().toLowerCase();
};
/**
* Returns true if this is an Oracle database.
* @return {boolean} true if this is an Oracle database.
*/
this.isOracle = function() {
return source.isOracle();
};
/**
* Returns true if this is a MySQL database.
* @return {boolean} true if this is an MySQL database.
*/
this.isMySql = function() {
return source.isMySQL();
};
/**
* Returns true if this is a PostgreSQL database.
* @return {boolean} true if this is a PostgreSQL database.
*/
this.isPostgreSql = function() {
return source.isPostgreSQL();
};
/**
* Executes the given SQL statement. The result set is returned
* as JavaScript Array containing a JavaScript Object for each result.
* @param {String} sql an SQL query statement
* @return {Array} an Array containing the result set
*/
this.query = function(sql) {
var isLogSqlEnabled = (getProperty("logSQL", "false").toLowerCase() == "true");
var logTimeStart = isLogSqlEnabled ? java.lang.System.currentTimeMillis() : 0;
var connection = source.getConnection();
connection.setReadOnly(true);
var statement = connection.createStatement();
var resultSet = statement.executeQuery(sql);
var metaData = resultSet.getMetaData();
var max = metaData.getColumnCount();
var types = [];
for (var i=1; i <= max; i++) {
types[i] = metaData.getColumnType(i);
}
var result = [];
while (resultSet.next()) {
var row = {}
for (var i=1; i<=max; i+=1) {
switch (types[i]) {
case Types.BIT:
case Types.BOOLEAN:
row[metaData.getColumnLabel(i)] = resultSet.getBoolean(i);
break;
case Types.TINYINT:
case Types.BIGINT:
case Types.SMALLINT:
case Types.INTEGER:
row[metaData.getColumnLabel(i)] = resultSet.getLong(i);
break;
case Types.REAL:
case Types.FLOAT:
case Types.DOUBLE:
case Types.DECIMAL:
case Types.NUMERIC:
row[metaData.getColumnLabel(i)] = resultSet.getDouble(i);
break;
case Types.VARBINARY:
case Types.BINARY:
case Types.LONGVARBINARY:
case Types.LONGVARCHAR:
case Types.CHAR:
case Types.VARCHAR:
case Types.CLOB:
case Types.OTHER:
row[metaData.getColumnLabel(i)] = resultSet.getString(i);
break;
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
row[metaData.getColumnLabel(i)] = resultSet.getTimestamp(i);
break;
case Types.NULL:
row[metaData.getColumnLabel(i)] = null;
break;
default:
row[metaData.getColumnLabel(i)] = resultSet.getString(i);
break;
}
}
result[result.length] = row;
}
var logTimeStop = isLogSqlEnabled ? java.lang.System.currentTimeMillis() : 0;
if (isLogSqlEnabled) {
var tableName = metaData.getColumnCount() > 0 ? metaData.getTableName(1) : null;
app.getLogger("helma." + app.name + ".sql").info("SQL DIRECT_QUERY " + (tableName || "-") + " " + (logTimeStop - logTimeStart) + ": " + sql);
}
try {
statement.close();
resultSet.close();
} catch (error) {
// ignore
}
return result;
};
/**
* Executes the given SQL statement, which may be an INSERT, UPDATE,
* or DELETE statement or an SQL statement that returns nothing,
* such as an SQL data definition statement. The return value is an integer that
* indicates the number of rows that were affected by the statement.
* @param {String} sql an SQL statement
* @return {int} either the row count for INSERT, UPDATE or
* DELETE statements, or 0 for SQL statements that return nothing
*/
this.execute = function(sql) {
var isLogSqlEnabled = (getProperty("logSQL", "false").toLowerCase() == "true");
var logTimeStart = isLogSqlEnabled ? java.lang.System.currentTimeMillis() : 0;
var connection = source.getConnection();
connection.setReadOnly(false);
var statement = connection.createStatement();
var result;
try {
result = statement.executeUpdate(sql);
} finally {
try {
statement.close();
} catch (error) {
// ignore
}
}
var logTimeStop = isLogSqlEnabled ? java.lang.System.currentTimeMillis() : 0;
if (isLogSqlEnabled) {
app.getLogger("helma." + app.name + ".sql").info("SQL DIRECT_EXECUTE - " + (logTimeStop - logTimeStart) + ": " + sql);
}
return result;
};
/**
* Return the name of the Helma DbSource object.
* @return {String} the DbSource name
*/
this.getName = function() {
return source.getName();
};
/**
* Return the name of the JDBC driver used by this Database instance.
* @return {String} the JDBC driver name
*/
this.getDriverName = function() {
return source.getDriverName();
};
/**
* @ignore
*/
this.toString = function() {
return "[helma.Database " + this.getName() + "]";
};
for (var i in this)
this.dontEnum(i);
return this;
};
/**
* Create a new Database instance using the given parameters.
* <p>Some of the parameters support shortcuts for known database products.
* The <code>url</code> parameter recognizes the values "mysql", "oracle" and
* "postgresql". For those databases, it is also possible to pass just
* <code>hostname</code> or <code>hostname:port</code> as <code>url</code>
* parameters instead of the full JDBC URL.</p>
* @param {String} driver the class name of the JDBC driver. As
* shortcuts, the values "mysql", "oracle" and "postgresql" are
* recognized.
* @param {String} url the JDBC URL.
* @param {String} name the name of the database to use
* @param {String} user the the username
* @param {String} password the password
* @return {helma.Database} a helma.Database instance
*/
helma.Database.createInstance = function(driver, url, name, user, password) {
var DbSource = Packages.helma.objectmodel.db.DbSource;
if (!driver || !url || !name)
throw("Insufficient arguments to create helma.db.Connection");
if (typeof password != "string")
password = "";
var MYSQL = "mysql";
var ORACLE = "oracle";
var POSTGRESQL = "postgresql";
var JDBC = "jdbc:";
var DRIVER_MYSQL = "com.mysql.jdbc.Driver";
var DRIVER_ORACLE = "oracle.jdbc.driver.OracleDriver";
var DRIVER_POSTGRESQL = "org.postgresql.Driver";
if (driver == MYSQL) {
driver = DRIVER_MYSQL;
if (url.indexOf(JDBC) != 0)
url = "jdbc:mysql://" + url + "/" + name;
} else if (driver == ORACLE) {
driver = DRIVER_ORACLE;
if (url.indexOf(JDBC) != 0)
url = "jdbc:oracle:thin:@" + url + ":" + name;
} else if (driver == POSTGRESQL) {
driver = DRIVER_POSTGRESQL;
if (url.indexOf(JDBC) != 0)
url = "jdbc:postgresql://" + url + "/" + name;
}
var props = new Packages.helma.util.ResourceProperties();
props.put(name + ".url", url);
props.put(name + ".driver", driver);
if (user) {
props.put(name + ".user", user)
}
if (password) {
props.put(name + ".password", password);
}
return new helma.Database(new DbSource(name, props));
}
/**
* Get a Database instance using the Database source defined in the
* application's db.properties file with the given name.
* @param {String} name the name of the DB source as defined in db.properties
* @return {helma.Database} a helma.Database instance
*/
helma.Database.getInstance = function(name) {
return new helma.Database(app.getDbSource(name));
}
/**
* @ignore
*/
helma.Database.toString = function() {
return "[helma.Database]";
};
/**
* @ignore
*/
helma.Database.example = function() {
var type = "mysql";
var host = "localhost";
var user = "root";
var pw = "";
var name = "mysql";
var db = new helma.Database(type, host, user, pw, name);
var result = db.query("select count(*) from db");
res.write(result.toSource());
return;
};
helma.lib = "Database";
helma.dontEnum(helma.lib);
for (var i in helma[helma.lib])
helma[helma.lib].dontEnum(i);
for (var i in helma[helma.lib].prototype)
helma[helma.lib].prototype.dontEnum(i);
delete helma.lib;