/*
* 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.
*
* 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
This class provides access to a relational database through JDBC. * There are two convenient ways to create instances of this class.
* *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.
* *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.
* *Database connections allocated by this class are be managed and eventually * disposed by Helma.
* * @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. *Some of the parameters support shortcuts for known database products.
* The url
parameter recognizes the values "mysql", "oracle" and
* "postgresql". For those databases, it is also possible to pass just
* hostname
or hostname:port
as url
* parameters instead of the full JDBC URL.