Skip to content
Browse files

Merge branch 'master' of git://github.com/grob/ringo-sqlstore

  • Loading branch information...
2 parents 4f7162e + 8526062 commit 0620f534ea80efb535636f605e26057d8fc11fcb @robi42 committed Aug 23, 2010
View
3 README.md
@@ -9,9 +9,10 @@ Ringo SQLstore is a lightweight ORM/storage implementation for [RingoJS]. It use
* Simple one-to-one and one-to-many mappings (see below)
* Connection pooling
-Currently supported databases are H2, MySQL (5.x) and Oracle (XE).
+Currently supported databases are H2, MySQL (5.x), Oracle (XE) and PostgreSQL (8.x).
SQLstore is heavily inspired by
+
* [ringo-hibernate] by Robert Thurnher
* [Helma] (the predecessor of RingoJS)
* [Hibernate] Project
View
2 lib/ringo/storage/sql/cache.js
@@ -47,14 +47,14 @@ var Cache = function(size) {
}
if (!newTable.containsKey(key)) {
newTable.put(key, value);
+ log.debug("Put", key, " into cache");
if (newTable.size() >= threshold) {
log.info("Rotating cache tables at " + newTable.size() + " objects, purging "
+ oldTable.size() + " objects");
oldTable = newTable;
newTable = new java.util.HashMap(capacity, loadFactor);
}
}
- log.debug("Put", key, " into cache");
return;
});
View
69 lib/ringo/storage/sql/connectionpool.js
@@ -6,39 +6,87 @@ export("ConnectionPool");
* Creates a new connection pool instance
* @class Instances of this class represent a connection pool
* @param {Object} props The connection properties to use
- * @param {Number} maxConnections Optional maximum connections (defaults to 10)
+ * @param {Number} maxConnections Optional maximum connections
* @returns A newly created connection pool
* @constructor
*/
var ConnectionPool = function(props, maxConnections) {
+ var driver = null;
var connections = [];
+ var connectionProps = (function() {
+ var properties = new java.util.Properties();
+ for (var name in props) {
+ if (name === "username") {
+ properties.put("user", props[name]);
+ } else {
+ properties.put(name, props[name]);
+ }
+ }
+ return properties;
+ })();
/**
+ * Returns the driver used by this connection pool
+ * @returns The driver
+ * @type java.sql.Driver
+ */
+ this.getDriver = function() {
+ if (driver === null) {
+ var classLoader = java.lang.Thread.currentThread().getContextClassLoader();
+ var drvr = java.lang.Class.forName(props.driver, true, classLoader).newInstance();
+ if (!drvr.acceptsURL(props.url)) {
+ throw new Error("Invalid URL " + props.url + " for driver " + props.driver);
+ }
+ driver = drvr;
+ }
+ return driver;
+ };
+
+ /**
* Searches for an unused connection and returns it.
* @returns An unused connect
* @type Connection
*/
- this.getConnection = function() {
+ this.getConnection = sync(function() {
var conn = null;
for each (var conn in connections) {
- if (conn.isValid()) {
- conn.lease();
+ if (conn.isValid() && conn.lease()) {
return conn;
}
}
// no inactive connection found, create a new one until we reach
- // the maxConnections limit
- if (connections.length < maxConnections) {
+ // the maxConnections limit (if defined)
+ if (!maxConnections || connections.length < maxConnections) {
log.debug("Creating database connection no.", connections.length, "(URL:", props.url, ", Username:", props.username + ")");
- conn = new Connection(this, java.sql.DriverManager.getConnection(props.url, props.username,
- props.password));
+ conn = new Connection(this, this.getDriver().connect(props.url, connectionProps));
conn.lease();
connections.push(conn);
return conn;
} else {
throw new Error("Maximum connection limit " + maxConnections + " reached");
}
+ });
+
+ /**
+ * Returns the number of connections in this pool
+ * @returns The number of connections in this pool
+ * @type Number
+ */
+ this.size = function() {
+ return connections.length;
+ };
+
+ /**
+ * Loops over all connections, closes them and empties the internal
+ * connection store.
+ */
+ this.closeConnections = function() {
+ for each (var conn in connections) {
+ conn.getConnection().close();
+ }
+ connections.length = 0;
+ return;
};
return this;
@@ -104,10 +152,13 @@ var Connection = function(pool, conn) {
};
/**
- * Marks this connection as not-in-use, but doesn't close it
+ * Marks this connection as not-in-use, but doesn't close it. In addition
+ * the wrapped connection is put back into writable autocommit mode.
*/
this.close = function() {
inUse = false;
+ conn.setReadOnly(false);
+ conn.setAutoCommit(true);
return;
};
View
94 lib/ringo/storage/sql/databases/postgresql.js
@@ -0,0 +1,94 @@
+var types = require("../types");
+var BaseDialect = require("../basedialect").BaseDialect;
+
+/**
+ * Database dialect for PostgreSQL databases
+ * @class Database dialect for PostgreSQL databases
+ * @returns A newly created PostgreSQL database dialect instance
+ * @constructor
+ */
+var Dialect = function() {
+ this.registerColumnType("integer", new types.ColumnType(java.sql.Types.INTEGER, "int4"));
+ this.registerColumnType("long", new types.ColumnType(java.sql.Types.BIGINT, "int8"));
+ this.registerColumnType("short", new types.ColumnType(java.sql.Types.SMALLINT, "int2"));
+ this.registerColumnType("float", new types.ColumnType(java.sql.Types.FLOAT, "float4"));
+ this.registerColumnType("double", new types.ColumnType(java.sql.Types.DOUBLE, "float8"));
+ this.registerColumnType("character", new types.ColumnType(java.sql.Types.CHAR, "character", {
+ "length": 1
+ }));
+ this.registerColumnType("string", new types.ColumnType(java.sql.Types.VARCHAR, "varchar", {
+ "length": 4000
+ }));
+ this.registerColumnType("byte", new types.ColumnType(java.sql.Types.TINYINT, "int2"));
+ this.registerColumnType("boolean", new types.ColumnType(java.sql.Types.BIT, "bool"));
+ this.registerColumnType("date", new types.ColumnType(java.sql.Types.DATE, "date"));
+ this.registerColumnType("time", new types.ColumnType(java.sql.Types.TIME, "time"));
+ this.registerColumnType("timestamp", new types.ColumnType(java.sql.Types.TIMESTAMP, "timestamp"));
+ this.registerColumnType("binary", new types.ColumnType(java.sql.Types.BINARY, "bytea"));
+ this.registerColumnType("text", new types.ColumnType(java.sql.Types.LONGVARCHAR, "text"));
+
+ return this;
+}
+// extend BaseDialect
+Dialect.prototype = new BaseDialect();
+
+/** @ignore */
+Dialect.prototype.toString = function() {
+ return "[Dialect PostgreSQL]";
+};
+
+/**
+ * Returns true
+ * @returns True
+ * @type Boolean
+ */
+Dialect.prototype.hasSequenceSupport = function() {
+ return true;
+};
+
+/**
+ * Returns the SQL statement for retrieving the next value of a sequence
+ * @param {String} sequenceName The name of the sequence
+ * @returns The SQL statement
+ * @type String
+ */
+Dialect.prototype.getSqlNextSequenceValue = function(sequenceName) {
+ return "SELECT nextval('" + sequenceName + "')";
+};
+
+/**
+ * Extends the SQL statement passed as argument with a limit restriction
+ * @param {String} sql The SQL statement to add the limit restriction to
+ * @param {Limit} limit The limit
+ * @returns The SQL statement
+ * @type String
+ */
+Dialect.prototype.getSqlLimit = function(sql, limit) {
+ return sql + " LIMIT " + limit;
+};
+
+/**
+ * Extends the SQL statement passed as argument with an offset restriction
+ * @param {String} sql The SQL statement to add the offset restriction to
+ * @param {Number} offset The offset
+ * @returns The SQL statement
+ * @type String
+ */
+Dialect.prototype.getSqlOffset = function(sql, offset) {
+ return sql + " LIMIT ALL OFFSET " + offset;
+};
+
+/**
+ * Extends the SQL statement passed as argument with a range restriction
+ * @param {String} sql The SQL statement to add the range restriction to
+ * @param {Number} offset The offset
+ * @param {Limit} limit The limit
+ * @returns The SQL statement
+ * @type String
+ */
+Dialect.prototype.getSqlRange = function(sql, offset, limit) {
+ return sql + " LIMIT " + limit + " OFFSET " + offset;
+};
+
+
+exports = new Dialect();
View
66 lib/ringo/storage/sql/store.js
@@ -108,14 +108,18 @@ var Store = function(props, maxConnections) {
};
// create the table if it doesn't exist already
var conn = this.getConnection();
- var schemaName = m.schemaName || this.dialect.getDefaultSchema(conn);
- if (sqlUtils.tableExists(conn, m.tableName, schemaName) === false) {
- this.createTable(conn, this.dialect, m);
- if (m.id.hasSequence() && this.dialect.hasSequenceSupport()) {
- sqlUtils.createSequence(conn, this.dialect, m.schemaName, m.id.sequence);
+ try {
+ var schemaName = m.schemaName || this.dialect.getDefaultSchema(conn);
+ if (sqlUtils.tableExists(conn, m.tableName, schemaName) === false) {
+ this.createTable(conn, this.dialect, m);
+ if (m.id.hasSequence() && this.dialect.hasSequenceSupport()) {
+ sqlUtils.createSequence(conn, this.dialect, m.schemaName, m.id.sequence);
+ }
+ } else {
+ // TODO: update table
}
- } else {
- // TODO: update table
+ } finally {
+ sqlUtils.close(conn);
}
}
return ctor;
@@ -143,6 +147,14 @@ var Store = function(props, maxConnections) {
this.createTransaction = function() {
return new Transaction(this);
};
+
+ /**
+ * Closes all connections this store has opened
+ */
+ this.closeConnections = function() {
+ connectionPool.closeConnections();
+ return;
+ };
return this;
};
@@ -179,6 +191,8 @@ Store.prototype.determineDialect = function() {
throw new Error("Unsupported MySQL version " + majorVersion);
case "Oracle":
return require("./databases/oracle");
+ case "PostgreSQL":
+ return require("./databases/postgresql");
default:
throw new Error("Unsupported database " + productName);
}
@@ -267,16 +281,13 @@ Store.prototype.executeUpdate = function(sql, columns, values, transaction) {
var statement = null;
try {
if (transaction != null) {
- log.debug("Using transaction mode");
conn = transaction.getConnection();
- conn.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
- conn.setAutoCommit(false);
+ log.debug("Using transaction mode, connection:", conn);
} else {
- log.debug("Using autocommit mode");
+ log.debug("Using autocommit mode, connection: ", conn);
conn = this.getConnection();
- conn.setAutoCommit(true);
+ conn.setReadOnly(false);
}
- conn.setReadOnly(false);
statement = conn.prepareStatement(sql);
columns.forEach(function(column, idx) {
var value = values[idx];
@@ -291,7 +302,7 @@ Store.prototype.executeUpdate = function(sql, columns, values, transaction) {
throw e;
} finally {
sqlUtils.close(statement);
- if (transaction == undefined) {
+ if (conn.getAutoCommit()) {
sqlUtils.close(conn);
}
}
@@ -301,10 +312,11 @@ Store.prototype.executeUpdate = function(sql, columns, values, transaction) {
* Generates a new id for the given type, by either using a defined sequence
* or incrementing the max-ID from the table for the given type.
* @param {String} type The type to return the next unused id for
+ * @param {String} transaction Optional transaction
* @returns The next unused id
* @type Number
*/
-Store.prototype.generateId = function(type) {
+Store.prototype.generateId = function(type, transaction) {
var mapping = this.getEntityMapping(type);
var sqlBuf = new java.lang.StringBuffer();
var offset = 0;
@@ -322,7 +334,7 @@ Store.prototype.generateId = function(type) {
var statement = null;
var resultSet = null;
- var conn = this.getConnection();
+ var conn = (transaction || this).getConnection();
log.debug("Generating ID:", sqlBuf.toString());
try {
statement = conn.createStatement();
@@ -333,7 +345,9 @@ Store.prototype.generateId = function(type) {
} finally {
sqlUtils.close(resultSet);
sqlUtils.close(statement);
- sqlUtils.close(conn);
+ if (conn.getAutoCommit()) {
+ sqlUtils.close(conn);
+ }
}
};
@@ -418,16 +432,13 @@ Store.prototype.remove = function(key, transaction) {
var statement = null;
try {
if (transaction != null) {
- log.debug("Using transaction mode");
conn = transaction.getConnection();
- conn.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
- conn.setAutoCommit(false);
+ log.debug("Using transaction mode, connection:", conn);
} else {
- log.debug("Using autocommit mode");
conn = this.getConnection();
- conn.setAutoCommit(true);
+ log.debug("Using autocommit mode, connection:", conn);
+ conn.setReadOnly(false);
}
- conn.setReadOnly(false);
statement = conn.prepareStatement(sqlBuf.toString());
this.dialect.getType("integer").set(statement, key.id, 1);
var result = statement.executeUpdate();
@@ -440,7 +451,7 @@ Store.prototype.remove = function(key, transaction) {
return result;
} finally {
sqlUtils.close(statement);
- if (transaction == undefined) {
+ if (conn.getAutoCommit()) {
sqlUtils.close(conn);
}
}
@@ -496,7 +507,7 @@ Store.prototype.insert = function(entity, transaction) {
sqlBuf.append(mapping.getQualifiedTableName(this.dialect)).append(" (");
// id column
- var nextId = this.generateId(entity._key.type);
+ var nextId = this.generateId(entity._key.type, transaction);
sqlBuf.append(this.dialect.quote(mapping.id.column));
valuesBuf.append("?");
columns.push(mapping.id);
@@ -618,10 +629,7 @@ Store.prototype.getProperties = function(store, entity) {
value = new Collection(propMapping.name, query);
} else if (propMapping.isObjectMapping() && value != null) {
// load the mapped entity from database
- var type = propMapping.entity;
- if (this.isEntityExisting(type, value) === true) {
- value = this.get(type, value);
- }
+ value = this.get(propMapping.entity, value);
}
props[propMapping.name] = value;
}
View
59 lib/ringo/storage/sql/transaction.js
@@ -15,18 +15,55 @@ var Transaction = function(store) {
var deleted = [];
/**
+ * Resets this transaction.
+ * @private
+ */
+ var reset = function() {
+ if (connection !== null) {
+ connection.setTransactionIsolation(java.sql.Connection.TRANSACTION_READ_COMMITTED);
+ connection.close();
+ connection = null;
+ }
+ inserted.length = 0;
+ updated.length = 0;
+ deleted.length = 0;
+ return;
+ };
+
+ /**
* Returns the connection of this transaction
* @returns The connection
* @type java.sql.Connection
*/
this.getConnection = function() {
if (connection === null) {
connection = store.getConnection();
+ connection.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
+ connection.setReadOnly(false);
+ connection.setAutoCommit(false);
}
return connection;
};
/**
+ * Commits all changes made in this transaction
+ */
+ this.commit = function() {
+ connection.commit();
+ reset();
+ return;
+ };
+
+ /**
+ * Rolls back all changes made in this transaction
+ */
+ this.rollback = function() {
+ connection.rollback();
+ reset();
+ return;
+ };
+
+ /**
* Returns true if this transaction is dirty
* @returns True if this transaction is dirty
* @type Boolean
@@ -74,25 +111,3 @@ Transaction.prototype.toString = function() {
this.updated.length + " updated, " +
this.deleted.length + " deleted)]";
};
-
-/**
- * Commits all changes made in this transaction
- */
-Transaction.prototype.commit = function() {
- this.getConnection().commit();
- this.inserted.length = 0;
- this.updated.length = 0;
- this.deleted.length = 0;
- return;
-};
-
-/**
- * Rolls back all changes made in this transaction
- */
-Transaction.prototype.rollback = function() {
- this.getConnection().rollback();
- this.inserted.length = 0;
- this.updated.length = 0;
- this.deleted.length = 0;
- return;
-};
View
17 lib/ringo/storage/sql/util.js
@@ -70,12 +70,13 @@ function createTable(conn, dialect, schemaName, tableName, columns, primaryKey)
var statement = null;
try {
- conn.setReadOnly(false);
+ if (conn.isReadOnly()) {
+ conn.setReadOnly(false);
+ }
statement = conn.createStatement();
statement.executeUpdate(sqlBuf.toString());
} finally {
close(statement);
- close(conn);
}
return;
};
@@ -97,12 +98,13 @@ function createSequence(conn, dialect, schemaName, sequenceName) {
log.debug("Creating sequence: " + sqlBuf.toString());
var statement = null;
try {
- conn.setReadOnly(false);
+ if (conn.isReadOnly()) {
+ conn.setReadOnly(false);
+ }
statement = conn.createStatement();
statement.executeUpdate(sqlBuf.toString());
} finally {
close(statement);
- close(conn);
}
return
};
@@ -195,7 +197,6 @@ function getTables(conn, schemaName, tableName) {
return result;
} finally {
close(tableMetaData);
- close(conn);
}
};
@@ -220,7 +221,6 @@ function tableExists(conn, tableName, schemaName) {
}
} finally {
close(tableMetaData);
- close(conn);
}
return result;
};
@@ -264,12 +264,13 @@ function drop(conn, dialect, typeName, objectName, schemaName) {
log.debug("Dropping:", sqlBuf.toString());
var statement = null;
try {
- conn.setReadOnly(false);
+ if (conn.isReadOnly()) {
+ conn.setReadOnly(false);
+ }
statement = conn.createStatement();
statement.execute(sqlBuf.toString());
} finally {
close(statement);
- close(conn);
}
return;
}
View
5 package.json
@@ -7,7 +7,8 @@
"contributors": [],
"jars": [
"jars/h2-1.2.138.jar",
- "jars/mysql-connector-java-5.1.6-bin.jar",
- "jars/ojdbc6.jar"
+ "jars/mysql.jar",
+ "jars/ojdbc6.jar",
+ "jars/postgresql.jar"
]
}
View
2 test/all.js
@@ -1,7 +1,9 @@
+exports.testConnectionPool = require("./connectionpool_test");
exports.testCache = require("./cache_test");
exports.testH2 = require("./h2_test");
exports.testMysql = require("./mysql_test");
exports.testOracle = require("./oracle_test");
+exports.testOracle = require("./postgresql_test");
//start the test runner if we're called directly from command line
if (require.main == module.id) {
View
2 test/cache_test.js
@@ -35,7 +35,7 @@ exports.testCache = function() {
exports.testRotation = function() {
var cache = new Cache();
- for (var i=0; i<1001; i+=1) {
+ for (var i=1; i<=1001; i+=1) {
cache.put("obj" + i, i);
}
assert.strictEqual(cache.size(), 501);
View
38 test/connectionpool_test.js
@@ -0,0 +1,38 @@
+var assert = require("assert");
+var ConnectionPool = require("ringo/storage/sql/connectionpool").ConnectionPool;
+var dbProps = {
+ "url": "jdbc:h2:mem:test",
+ "driver": "org.h2.Driver"
+};
+
+exports.testGetConnection = function() {
+ var pool = new ConnectionPool(dbProps);
+ // first connection
+ var conn1 = pool.getConnection();
+ assert.strictEqual(pool.size(), 1);
+ assert.isNotNull(conn1);
+ assert.isTrue(conn1.isInUse());
+ assert.isTrue(conn1.isValid());
+ conn1.close();
+ assert.isFalse(conn1.isInUse());
+ assert.strictEqual(pool.size(), 1);
+
+ // retrieve connection again - must be above connection, since it has been closed
+ var conn2 = pool.getConnection();
+ assert.strictEqual(conn1, conn2);
+ assert.strictEqual(pool.size(), 1);
+
+ // retrieve new connection
+ var conn3 = pool.getConnection();
+ assert.strictEqual(pool.size(), 2);
+ assert.notStrictEqual(conn1, conn3);
+ assert.notStrictEqual(conn2, conn3);
+ assert.isFalse(conn1.getConnection().equals(conn3.getConnection()));
+ assert.isFalse(conn2.getConnection().equals(conn3.getConnection()));
+ return;
+};
+
+//start the test runner if we're called directly from command line
+if (require.main == module.id) {
+ require('test').run(exports);
+}
View
12 test/h2_test.js
@@ -1,8 +1,12 @@
-exports = require("./store_test");
-exports.setDbProps({
- "url": "jdbc:h2:mem:test",
+var dbProps = {
+ "url": "jdbc:h2:mem:test;MVCC=TRUE",
"driver": "org.h2.Driver"
-});
+};
+
+exports.testTransaction = require("./transaction_test");
+exports.testTransaction.setDbProps(dbProps);
+exports.testStore = require("./store_test");
+exports.testStore.setDbProps(dbProps);
//start the test runner if we're called directly from command line
if (require.main == module.id) {
View
10 test/mysql_test.js
@@ -1,10 +1,14 @@
-exports = require("./store_test");
-exports.setDbProps({
+var dbProps = {
"url": "jdbc:mysql://localhost/test",
"driver": "com.mysql.jdbc.Driver",
"username": "test",
"password": "test"
-});
+};
+
+exports.testTransaction = require("./transaction_test");
+exports.testTransaction.setDbProps(dbProps);
+exports.testStore = require("./store_test");
+exports.testStore.setDbProps(dbProps);
//start the test runner if we're called directly from command line
if (require.main == module.id) {
View
12 test/oracle_test.js
@@ -1,10 +1,14 @@
-exports = require("./store_test");
-exports.setDbProps({
- "url": "jdbc:oracle:thin:@192.168.1.212:1524:XE",
+var dbProps = {
+ "url": "jdbc:oracle:thin:@192.168.1.215:1524:XE",
"driver": "oracle.jdbc.driver.OracleDriver",
"username": "test",
"password": "test"
-});
+};
+
+exports.testTransaction = require("./transaction_test");
+exports.testTransaction.setDbProps(dbProps);
+exports.testStore = require("./store_test");
+exports.testStore.setDbProps(dbProps);
//start the test runner if we're called directly from command line
if (require.main == module.id) {
View
16 test/postgresql_test.js
@@ -0,0 +1,16 @@
+var dbProps = {
+ "url": "jdbc:postgresql://192.168.1.215/test",
+ "driver": "org.postgresql.Driver",
+ "username": "test",
+ "password": "test"
+};
+
+exports.testTransaction = require("./transaction_test");
+exports.testTransaction.setDbProps(dbProps);
+exports.testStore = require("./store_test");
+exports.testStore.setDbProps(dbProps);
+
+//start the test runner if we're called directly from command line
+if (require.main == module.id) {
+ require('test').run(exports);
+}
View
6 test/store_test.js
@@ -92,7 +92,6 @@ const MAPPING_AUTHOR = {
};
function populate(store) {
- var transaction = store.createTransaction();
var authors = [];
for (var i=1; i<=5; i+=1) {
var author = new Author({
@@ -101,6 +100,7 @@ function populate(store) {
author.save();
authors.push(author);
}
+ var transaction = store.createTransaction();
for (var i=0; i<10; i+=1) {
var nr = i + 1;
var props = {
@@ -144,11 +144,13 @@ exports.tearDown = function() {
var schemaName = ctor.mapping.schemaName || store.dialect.getDefaultSchema(conn);
if (sqlUtils.tableExists(conn, ctor.mapping.tableName, schemaName)) {
sqlUtils.dropTable(conn, store.dialect, ctor.mapping.tableName, schemaName);
- if (store.dialect.hasSequenceSupport()) {
+ if (ctor.mapping.id.hasSequence() && store.dialect.hasSequenceSupport()) {
sqlUtils.dropSequence(conn, store.dialect, ctor.mapping.id.sequence, schemaName);
}
}
});
+ conn.close();
+ store.closeConnections();
return;
};
View
88 test/transaction_test.js
@@ -0,0 +1,88 @@
+var assert = require("assert");
+var Store = require("ringo/storage/sql/store").Store;
+var sqlUtils = require("ringo/storage/sql/util");
+
+var store = null;
+var Author = null;
+
+const MAPPING_AUTHOR = {
+ "table": "author",
+ "id": {
+ "column": "author_id"
+ },
+ "properties": {
+ "name": {
+ "type": "string",
+ "column": "author_name",
+ "nullable": false
+ }
+ }
+};
+
+var dbProps = {
+ "url": "jdbc:h2:mem:test;MVCC=TRUE",
+ "driver": "org.h2.Driver"
+};
+
+exports.setDbProps = function(props) {
+ dbProps = props;
+};
+
+exports.setUp = function() {
+ store = new Store(dbProps);
+ Author = store.defineEntity("Author", MAPPING_AUTHOR);
+ return;
+};
+
+exports.tearDown = function() {
+ var conn = store.getConnection();
+ var schemaName = Author.mapping.schemaName || store.dialect.getDefaultSchema(conn);
+ if (sqlUtils.tableExists(conn, Author.mapping.tableName, schemaName)) {
+ sqlUtils.dropTable(conn, store.dialect, Author.mapping.tableName, schemaName);
+ if (Author.mapping.id.hasSequence() && store.dialect.hasSequenceSupport()) {
+ sqlUtils.dropSequence(conn, store.dialect, Author.mapping.id.sequence, schemaName);
+ }
+ }
+ store.closeConnections();
+ store = null;
+ Author = null;
+ return;
+};
+
+exports.testTransaction = function() {
+ var transaction = store.createTransaction();
+ assert.isFalse(transaction.isDirty());
+
+ var authors = [];
+ // insert some test objects
+ for (var i=0; i<5; i+=1) {
+ var author = new Author({
+ "name": "Author " + (i + 1)
+ });
+ author.save(transaction);
+ authors.push(author);
+ }
+ assert.strictEqual(transaction.inserted.length, authors.length);
+ assert.isTrue(transaction.isDirty());
+ assert.strictEqual(Author.all().length, 0);
+ transaction.commit();
+ assert.isFalse(transaction.isDirty());
+ assert.strictEqual(Author.all().length, 5);
+
+ // re-use the transaction
+ var author = new Author({
+ "name": "Author " + (authors.length + 1)
+ });
+ author.save(transaction);
+ assert.isTrue(transaction.isDirty());
+ assert.strictEqual(transaction.inserted.length, 1);
+ transaction.commit();
+ assert.isFalse(transaction.isDirty());
+ assert.strictEqual(Author.all().length, 6);
+ return;
+};
+
+//start the test runner if we're called directly from command line
+if (require.main == module.id) {
+ require('test').run(exports);
+}

0 comments on commit 0620f53

Please sign in to comment.
Something went wrong with that request. Please try again.