/* * 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.

* @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;