Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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

  • Loading branch information...
commit 0620f534ea80efb535636f605e26057d8fc11fcb 2 parents 4f7162e + 8526062
Robert Thurnher authored
3  README.md
Source Rendered
@@ -9,9 +9,10 @@ Ringo SQLstore is a lightweight ORM/storage implementation for [RingoJS]. It use
9 9 * Simple one-to-one and one-to-many mappings (see below)
10 10 * Connection pooling
11 11
12   -Currently supported databases are H2, MySQL (5.x) and Oracle (XE).
  12 +Currently supported databases are H2, MySQL (5.x), Oracle (XE) and PostgreSQL (8.x).
13 13
14 14 SQLstore is heavily inspired by
  15 +
15 16 * [ringo-hibernate] by Robert Thurnher
16 17 * [Helma] (the predecessor of RingoJS)
17 18 * [Hibernate] Project
2  lib/ringo/storage/sql/cache.js
@@ -47,6 +47,7 @@ var Cache = function(size) {
47 47 }
48 48 if (!newTable.containsKey(key)) {
49 49 newTable.put(key, value);
  50 + log.debug("Put", key, " into cache");
50 51 if (newTable.size() >= threshold) {
51 52 log.info("Rotating cache tables at " + newTable.size() + " objects, purging "
52 53 + oldTable.size() + " objects");
@@ -54,7 +55,6 @@ var Cache = function(size) {
54 55 newTable = new java.util.HashMap(capacity, loadFactor);
55 56 }
56 57 }
57   - log.debug("Put", key, " into cache");
58 58 return;
59 59 });
60 60
69 lib/ringo/storage/sql/connectionpool.js
@@ -6,39 +6,87 @@ export("ConnectionPool");
6 6 * Creates a new connection pool instance
7 7 * @class Instances of this class represent a connection pool
8 8 * @param {Object} props The connection properties to use
9   - * @param {Number} maxConnections Optional maximum connections (defaults to 10)
  9 + * @param {Number} maxConnections Optional maximum connections
10 10 * @returns A newly created connection pool
11 11 * @constructor
12 12 */
13 13 var ConnectionPool = function(props, maxConnections) {
14 14
  15 + var driver = null;
15 16 var connections = [];
  17 + var connectionProps = (function() {
  18 + var properties = new java.util.Properties();
  19 + for (var name in props) {
  20 + if (name === "username") {
  21 + properties.put("user", props[name]);
  22 + } else {
  23 + properties.put(name, props[name]);
  24 + }
  25 + }
  26 + return properties;
  27 + })();
16 28
17 29 /**
  30 + * Returns the driver used by this connection pool
  31 + * @returns The driver
  32 + * @type java.sql.Driver
  33 + */
  34 + this.getDriver = function() {
  35 + if (driver === null) {
  36 + var classLoader = java.lang.Thread.currentThread().getContextClassLoader();
  37 + var drvr = java.lang.Class.forName(props.driver, true, classLoader).newInstance();
  38 + if (!drvr.acceptsURL(props.url)) {
  39 + throw new Error("Invalid URL " + props.url + " for driver " + props.driver);
  40 + }
  41 + driver = drvr;
  42 + }
  43 + return driver;
  44 + };
  45 +
  46 + /**
18 47 * Searches for an unused connection and returns it.
19 48 * @returns An unused connect
20 49 * @type Connection
21 50 */
22   - this.getConnection = function() {
  51 + this.getConnection = sync(function() {
23 52 var conn = null;
24 53 for each (var conn in connections) {
25   - if (conn.isValid()) {
26   - conn.lease();
  54 + if (conn.isValid() && conn.lease()) {
27 55 return conn;
28 56 }
29 57 }
30 58 // no inactive connection found, create a new one until we reach
31   - // the maxConnections limit
32   - if (connections.length < maxConnections) {
  59 + // the maxConnections limit (if defined)
  60 + if (!maxConnections || connections.length < maxConnections) {
33 61 log.debug("Creating database connection no.", connections.length, "(URL:", props.url, ", Username:", props.username + ")");
34   - conn = new Connection(this, java.sql.DriverManager.getConnection(props.url, props.username,
35   - props.password));
  62 + conn = new Connection(this, this.getDriver().connect(props.url, connectionProps));
36 63 conn.lease();
37 64 connections.push(conn);
38 65 return conn;
39 66 } else {
40 67 throw new Error("Maximum connection limit " + maxConnections + " reached");
41 68 }
  69 + });
  70 +
  71 + /**
  72 + * Returns the number of connections in this pool
  73 + * @returns The number of connections in this pool
  74 + * @type Number
  75 + */
  76 + this.size = function() {
  77 + return connections.length;
  78 + };
  79 +
  80 + /**
  81 + * Loops over all connections, closes them and empties the internal
  82 + * connection store.
  83 + */
  84 + this.closeConnections = function() {
  85 + for each (var conn in connections) {
  86 + conn.getConnection().close();
  87 + }
  88 + connections.length = 0;
  89 + return;
42 90 };
43 91
44 92 return this;
@@ -104,10 +152,13 @@ var Connection = function(pool, conn) {
104 152 };
105 153
106 154 /**
107   - * Marks this connection as not-in-use, but doesn't close it
  155 + * Marks this connection as not-in-use, but doesn't close it. In addition
  156 + * the wrapped connection is put back into writable autocommit mode.
108 157 */
109 158 this.close = function() {
110 159 inUse = false;
  160 + conn.setReadOnly(false);
  161 + conn.setAutoCommit(true);
111 162 return;
112 163 };
113 164
94 lib/ringo/storage/sql/databases/postgresql.js
... ... @@ -0,0 +1,94 @@
  1 +var types = require("../types");
  2 +var BaseDialect = require("../basedialect").BaseDialect;
  3 +
  4 +/**
  5 + * Database dialect for PostgreSQL databases
  6 + * @class Database dialect for PostgreSQL databases
  7 + * @returns A newly created PostgreSQL database dialect instance
  8 + * @constructor
  9 + */
  10 +var Dialect = function() {
  11 + this.registerColumnType("integer", new types.ColumnType(java.sql.Types.INTEGER, "int4"));
  12 + this.registerColumnType("long", new types.ColumnType(java.sql.Types.BIGINT, "int8"));
  13 + this.registerColumnType("short", new types.ColumnType(java.sql.Types.SMALLINT, "int2"));
  14 + this.registerColumnType("float", new types.ColumnType(java.sql.Types.FLOAT, "float4"));
  15 + this.registerColumnType("double", new types.ColumnType(java.sql.Types.DOUBLE, "float8"));
  16 + this.registerColumnType("character", new types.ColumnType(java.sql.Types.CHAR, "character", {
  17 + "length": 1
  18 + }));
  19 + this.registerColumnType("string", new types.ColumnType(java.sql.Types.VARCHAR, "varchar", {
  20 + "length": 4000
  21 + }));
  22 + this.registerColumnType("byte", new types.ColumnType(java.sql.Types.TINYINT, "int2"));
  23 + this.registerColumnType("boolean", new types.ColumnType(java.sql.Types.BIT, "bool"));
  24 + this.registerColumnType("date", new types.ColumnType(java.sql.Types.DATE, "date"));
  25 + this.registerColumnType("time", new types.ColumnType(java.sql.Types.TIME, "time"));
  26 + this.registerColumnType("timestamp", new types.ColumnType(java.sql.Types.TIMESTAMP, "timestamp"));
  27 + this.registerColumnType("binary", new types.ColumnType(java.sql.Types.BINARY, "bytea"));
  28 + this.registerColumnType("text", new types.ColumnType(java.sql.Types.LONGVARCHAR, "text"));
  29 +
  30 + return this;
  31 +}
  32 +// extend BaseDialect
  33 +Dialect.prototype = new BaseDialect();
  34 +
  35 +/** @ignore */
  36 +Dialect.prototype.toString = function() {
  37 + return "[Dialect PostgreSQL]";
  38 +};
  39 +
  40 +/**
  41 + * Returns true
  42 + * @returns True
  43 + * @type Boolean
  44 + */
  45 +Dialect.prototype.hasSequenceSupport = function() {
  46 + return true;
  47 +};
  48 +
  49 +/**
  50 + * Returns the SQL statement for retrieving the next value of a sequence
  51 + * @param {String} sequenceName The name of the sequence
  52 + * @returns The SQL statement
  53 + * @type String
  54 + */
  55 +Dialect.prototype.getSqlNextSequenceValue = function(sequenceName) {
  56 + return "SELECT nextval('" + sequenceName + "')";
  57 +};
  58 +
  59 +/**
  60 + * Extends the SQL statement passed as argument with a limit restriction
  61 + * @param {String} sql The SQL statement to add the limit restriction to
  62 + * @param {Limit} limit The limit
  63 + * @returns The SQL statement
  64 + * @type String
  65 + */
  66 +Dialect.prototype.getSqlLimit = function(sql, limit) {
  67 + return sql + " LIMIT " + limit;
  68 +};
  69 +
  70 +/**
  71 + * Extends the SQL statement passed as argument with an offset restriction
  72 + * @param {String} sql The SQL statement to add the offset restriction to
  73 + * @param {Number} offset The offset
  74 + * @returns The SQL statement
  75 + * @type String
  76 + */
  77 +Dialect.prototype.getSqlOffset = function(sql, offset) {
  78 + return sql + " LIMIT ALL OFFSET " + offset;
  79 +};
  80 +
  81 +/**
  82 + * Extends the SQL statement passed as argument with a range restriction
  83 + * @param {String} sql The SQL statement to add the range restriction to
  84 + * @param {Number} offset The offset
  85 + * @param {Limit} limit The limit
  86 + * @returns The SQL statement
  87 + * @type String
  88 + */
  89 +Dialect.prototype.getSqlRange = function(sql, offset, limit) {
  90 + return sql + " LIMIT " + limit + " OFFSET " + offset;
  91 +};
  92 +
  93 +
  94 +exports = new Dialect();
66 lib/ringo/storage/sql/store.js
@@ -108,14 +108,18 @@ var Store = function(props, maxConnections) {
108 108 };
109 109 // create the table if it doesn't exist already
110 110 var conn = this.getConnection();
111   - var schemaName = m.schemaName || this.dialect.getDefaultSchema(conn);
112   - if (sqlUtils.tableExists(conn, m.tableName, schemaName) === false) {
113   - this.createTable(conn, this.dialect, m);
114   - if (m.id.hasSequence() && this.dialect.hasSequenceSupport()) {
115   - sqlUtils.createSequence(conn, this.dialect, m.schemaName, m.id.sequence);
  111 + try {
  112 + var schemaName = m.schemaName || this.dialect.getDefaultSchema(conn);
  113 + if (sqlUtils.tableExists(conn, m.tableName, schemaName) === false) {
  114 + this.createTable(conn, this.dialect, m);
  115 + if (m.id.hasSequence() && this.dialect.hasSequenceSupport()) {
  116 + sqlUtils.createSequence(conn, this.dialect, m.schemaName, m.id.sequence);
  117 + }
  118 + } else {
  119 + // TODO: update table
116 120 }
117   - } else {
118   - // TODO: update table
  121 + } finally {
  122 + sqlUtils.close(conn);
119 123 }
120 124 }
121 125 return ctor;
@@ -143,6 +147,14 @@ var Store = function(props, maxConnections) {
143 147 this.createTransaction = function() {
144 148 return new Transaction(this);
145 149 };
  150 +
  151 + /**
  152 + * Closes all connections this store has opened
  153 + */
  154 + this.closeConnections = function() {
  155 + connectionPool.closeConnections();
  156 + return;
  157 + };
146 158
147 159 return this;
148 160 };
@@ -179,6 +191,8 @@ Store.prototype.determineDialect = function() {
179 191 throw new Error("Unsupported MySQL version " + majorVersion);
180 192 case "Oracle":
181 193 return require("./databases/oracle");
  194 + case "PostgreSQL":
  195 + return require("./databases/postgresql");
182 196 default:
183 197 throw new Error("Unsupported database " + productName);
184 198 }
@@ -267,16 +281,13 @@ Store.prototype.executeUpdate = function(sql, columns, values, transaction) {
267 281 var statement = null;
268 282 try {
269 283 if (transaction != null) {
270   - log.debug("Using transaction mode");
271 284 conn = transaction.getConnection();
272   - conn.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
273   - conn.setAutoCommit(false);
  285 + log.debug("Using transaction mode, connection:", conn);
274 286 } else {
275   - log.debug("Using autocommit mode");
  287 + log.debug("Using autocommit mode, connection: ", conn);
276 288 conn = this.getConnection();
277   - conn.setAutoCommit(true);
  289 + conn.setReadOnly(false);
278 290 }
279   - conn.setReadOnly(false);
280 291 statement = conn.prepareStatement(sql);
281 292 columns.forEach(function(column, idx) {
282 293 var value = values[idx];
@@ -291,7 +302,7 @@ Store.prototype.executeUpdate = function(sql, columns, values, transaction) {
291 302 throw e;
292 303 } finally {
293 304 sqlUtils.close(statement);
294   - if (transaction == undefined) {
  305 + if (conn.getAutoCommit()) {
295 306 sqlUtils.close(conn);
296 307 }
297 308 }
@@ -301,10 +312,11 @@ Store.prototype.executeUpdate = function(sql, columns, values, transaction) {
301 312 * Generates a new id for the given type, by either using a defined sequence
302 313 * or incrementing the max-ID from the table for the given type.
303 314 * @param {String} type The type to return the next unused id for
  315 + * @param {String} transaction Optional transaction
304 316 * @returns The next unused id
305 317 * @type Number
306 318 */
307   -Store.prototype.generateId = function(type) {
  319 +Store.prototype.generateId = function(type, transaction) {
308 320 var mapping = this.getEntityMapping(type);
309 321 var sqlBuf = new java.lang.StringBuffer();
310 322 var offset = 0;
@@ -322,7 +334,7 @@ Store.prototype.generateId = function(type) {
322 334
323 335 var statement = null;
324 336 var resultSet = null;
325   - var conn = this.getConnection();
  337 + var conn = (transaction || this).getConnection();
326 338 log.debug("Generating ID:", sqlBuf.toString());
327 339 try {
328 340 statement = conn.createStatement();
@@ -333,7 +345,9 @@ Store.prototype.generateId = function(type) {
333 345 } finally {
334 346 sqlUtils.close(resultSet);
335 347 sqlUtils.close(statement);
336   - sqlUtils.close(conn);
  348 + if (conn.getAutoCommit()) {
  349 + sqlUtils.close(conn);
  350 + }
337 351 }
338 352 };
339 353
@@ -418,16 +432,13 @@ Store.prototype.remove = function(key, transaction) {
418 432 var statement = null;
419 433 try {
420 434 if (transaction != null) {
421   - log.debug("Using transaction mode");
422 435 conn = transaction.getConnection();
423   - conn.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
424   - conn.setAutoCommit(false);
  436 + log.debug("Using transaction mode, connection:", conn);
425 437 } else {
426   - log.debug("Using autocommit mode");
427 438 conn = this.getConnection();
428   - conn.setAutoCommit(true);
  439 + log.debug("Using autocommit mode, connection:", conn);
  440 + conn.setReadOnly(false);
429 441 }
430   - conn.setReadOnly(false);
431 442 statement = conn.prepareStatement(sqlBuf.toString());
432 443 this.dialect.getType("integer").set(statement, key.id, 1);
433 444 var result = statement.executeUpdate();
@@ -440,7 +451,7 @@ Store.prototype.remove = function(key, transaction) {
440 451 return result;
441 452 } finally {
442 453 sqlUtils.close(statement);
443   - if (transaction == undefined) {
  454 + if (conn.getAutoCommit()) {
444 455 sqlUtils.close(conn);
445 456 }
446 457 }
@@ -496,7 +507,7 @@ Store.prototype.insert = function(entity, transaction) {
496 507 sqlBuf.append(mapping.getQualifiedTableName(this.dialect)).append(" (");
497 508
498 509 // id column
499   - var nextId = this.generateId(entity._key.type);
  510 + var nextId = this.generateId(entity._key.type, transaction);
500 511 sqlBuf.append(this.dialect.quote(mapping.id.column));
501 512 valuesBuf.append("?");
502 513 columns.push(mapping.id);
@@ -618,10 +629,7 @@ Store.prototype.getProperties = function(store, entity) {
618 629 value = new Collection(propMapping.name, query);
619 630 } else if (propMapping.isObjectMapping() && value != null) {
620 631 // load the mapped entity from database
621   - var type = propMapping.entity;
622   - if (this.isEntityExisting(type, value) === true) {
623   - value = this.get(type, value);
624   - }
  632 + value = this.get(propMapping.entity, value);
625 633 }
626 634 props[propMapping.name] = value;
627 635 }
59 lib/ringo/storage/sql/transaction.js
@@ -15,6 +15,22 @@ var Transaction = function(store) {
15 15 var deleted = [];
16 16
17 17 /**
  18 + * Resets this transaction.
  19 + * @private
  20 + */
  21 + var reset = function() {
  22 + if (connection !== null) {
  23 + connection.setTransactionIsolation(java.sql.Connection.TRANSACTION_READ_COMMITTED);
  24 + connection.close();
  25 + connection = null;
  26 + }
  27 + inserted.length = 0;
  28 + updated.length = 0;
  29 + deleted.length = 0;
  30 + return;
  31 + };
  32 +
  33 + /**
18 34 * Returns the connection of this transaction
19 35 * @returns The connection
20 36 * @type java.sql.Connection
@@ -22,11 +38,32 @@ var Transaction = function(store) {
22 38 this.getConnection = function() {
23 39 if (connection === null) {
24 40 connection = store.getConnection();
  41 + connection.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
  42 + connection.setReadOnly(false);
  43 + connection.setAutoCommit(false);
25 44 }
26 45 return connection;
27 46 };
28 47
29 48 /**
  49 + * Commits all changes made in this transaction
  50 + */
  51 + this.commit = function() {
  52 + connection.commit();
  53 + reset();
  54 + return;
  55 + };
  56 +
  57 + /**
  58 + * Rolls back all changes made in this transaction
  59 + */
  60 + this.rollback = function() {
  61 + connection.rollback();
  62 + reset();
  63 + return;
  64 + };
  65 +
  66 + /**
30 67 * Returns true if this transaction is dirty
31 68 * @returns True if this transaction is dirty
32 69 * @type Boolean
@@ -74,25 +111,3 @@ Transaction.prototype.toString = function() {
74 111 this.updated.length + " updated, " +
75 112 this.deleted.length + " deleted)]";
76 113 };
77   -
78   -/**
79   - * Commits all changes made in this transaction
80   - */
81   -Transaction.prototype.commit = function() {
82   - this.getConnection().commit();
83   - this.inserted.length = 0;
84   - this.updated.length = 0;
85   - this.deleted.length = 0;
86   - return;
87   -};
88   -
89   -/**
90   - * Rolls back all changes made in this transaction
91   - */
92   -Transaction.prototype.rollback = function() {
93   - this.getConnection().rollback();
94   - this.inserted.length = 0;
95   - this.updated.length = 0;
96   - this.deleted.length = 0;
97   - return;
98   -};
17 lib/ringo/storage/sql/util.js
@@ -70,12 +70,13 @@ function createTable(conn, dialect, schemaName, tableName, columns, primaryKey)
70 70
71 71 var statement = null;
72 72 try {
73   - conn.setReadOnly(false);
  73 + if (conn.isReadOnly()) {
  74 + conn.setReadOnly(false);
  75 + }
74 76 statement = conn.createStatement();
75 77 statement.executeUpdate(sqlBuf.toString());
76 78 } finally {
77 79 close(statement);
78   - close(conn);
79 80 }
80 81 return;
81 82 };
@@ -97,12 +98,13 @@ function createSequence(conn, dialect, schemaName, sequenceName) {
97 98 log.debug("Creating sequence: " + sqlBuf.toString());
98 99 var statement = null;
99 100 try {
100   - conn.setReadOnly(false);
  101 + if (conn.isReadOnly()) {
  102 + conn.setReadOnly(false);
  103 + }
101 104 statement = conn.createStatement();
102 105 statement.executeUpdate(sqlBuf.toString());
103 106 } finally {
104 107 close(statement);
105   - close(conn);
106 108 }
107 109 return
108 110 };
@@ -195,7 +197,6 @@ function getTables(conn, schemaName, tableName) {
195 197 return result;
196 198 } finally {
197 199 close(tableMetaData);
198   - close(conn);
199 200 }
200 201 };
201 202
@@ -220,7 +221,6 @@ function tableExists(conn, tableName, schemaName) {
220 221 }
221 222 } finally {
222 223 close(tableMetaData);
223   - close(conn);
224 224 }
225 225 return result;
226 226 };
@@ -264,12 +264,13 @@ function drop(conn, dialect, typeName, objectName, schemaName) {
264 264 log.debug("Dropping:", sqlBuf.toString());
265 265 var statement = null;
266 266 try {
267   - conn.setReadOnly(false);
  267 + if (conn.isReadOnly()) {
  268 + conn.setReadOnly(false);
  269 + }
268 270 statement = conn.createStatement();
269 271 statement.execute(sqlBuf.toString());
270 272 } finally {
271 273 close(statement);
272   - close(conn);
273 274 }
274 275 return;
275 276 }
5 package.json
@@ -7,7 +7,8 @@
7 7 "contributors": [],
8 8 "jars": [
9 9 "jars/h2-1.2.138.jar",
10   - "jars/mysql-connector-java-5.1.6-bin.jar",
11   - "jars/ojdbc6.jar"
  10 + "jars/mysql.jar",
  11 + "jars/ojdbc6.jar",
  12 + "jars/postgresql.jar"
12 13 ]
13 14 }
2  test/all.js
... ... @@ -1,7 +1,9 @@
  1 +exports.testConnectionPool = require("./connectionpool_test");
1 2 exports.testCache = require("./cache_test");
2 3 exports.testH2 = require("./h2_test");
3 4 exports.testMysql = require("./mysql_test");
4 5 exports.testOracle = require("./oracle_test");
  6 +exports.testOracle = require("./postgresql_test");
5 7
6 8 //start the test runner if we're called directly from command line
7 9 if (require.main == module.id) {
2  test/cache_test.js
@@ -35,7 +35,7 @@ exports.testCache = function() {
35 35
36 36 exports.testRotation = function() {
37 37 var cache = new Cache();
38   - for (var i=0; i<1001; i+=1) {
  38 + for (var i=1; i<=1001; i+=1) {
39 39 cache.put("obj" + i, i);
40 40 }
41 41 assert.strictEqual(cache.size(), 501);
38 test/connectionpool_test.js
... ... @@ -0,0 +1,38 @@
  1 +var assert = require("assert");
  2 +var ConnectionPool = require("ringo/storage/sql/connectionpool").ConnectionPool;
  3 +var dbProps = {
  4 + "url": "jdbc:h2:mem:test",
  5 + "driver": "org.h2.Driver"
  6 +};
  7 +
  8 +exports.testGetConnection = function() {
  9 + var pool = new ConnectionPool(dbProps);
  10 + // first connection
  11 + var conn1 = pool.getConnection();
  12 + assert.strictEqual(pool.size(), 1);
  13 + assert.isNotNull(conn1);
  14 + assert.isTrue(conn1.isInUse());
  15 + assert.isTrue(conn1.isValid());
  16 + conn1.close();
  17 + assert.isFalse(conn1.isInUse());
  18 + assert.strictEqual(pool.size(), 1);
  19 +
  20 + // retrieve connection again - must be above connection, since it has been closed
  21 + var conn2 = pool.getConnection();
  22 + assert.strictEqual(conn1, conn2);
  23 + assert.strictEqual(pool.size(), 1);
  24 +
  25 + // retrieve new connection
  26 + var conn3 = pool.getConnection();
  27 + assert.strictEqual(pool.size(), 2);
  28 + assert.notStrictEqual(conn1, conn3);
  29 + assert.notStrictEqual(conn2, conn3);
  30 + assert.isFalse(conn1.getConnection().equals(conn3.getConnection()));
  31 + assert.isFalse(conn2.getConnection().equals(conn3.getConnection()));
  32 + return;
  33 +};
  34 +
  35 +//start the test runner if we're called directly from command line
  36 +if (require.main == module.id) {
  37 + require('test').run(exports);
  38 +}
12 test/h2_test.js
... ... @@ -1,8 +1,12 @@
1   -exports = require("./store_test");
2   -exports.setDbProps({
3   - "url": "jdbc:h2:mem:test",
  1 +var dbProps = {
  2 + "url": "jdbc:h2:mem:test;MVCC=TRUE",
4 3 "driver": "org.h2.Driver"
5   -});
  4 +};
  5 +
  6 +exports.testTransaction = require("./transaction_test");
  7 +exports.testTransaction.setDbProps(dbProps);
  8 +exports.testStore = require("./store_test");
  9 +exports.testStore.setDbProps(dbProps);
6 10
7 11 //start the test runner if we're called directly from command line
8 12 if (require.main == module.id) {
10 test/mysql_test.js
... ... @@ -1,10 +1,14 @@
1   -exports = require("./store_test");
2   -exports.setDbProps({
  1 +var dbProps = {
3 2 "url": "jdbc:mysql://localhost/test",
4 3 "driver": "com.mysql.jdbc.Driver",
5 4 "username": "test",
6 5 "password": "test"
7   -});
  6 +};
  7 +
  8 +exports.testTransaction = require("./transaction_test");
  9 +exports.testTransaction.setDbProps(dbProps);
  10 +exports.testStore = require("./store_test");
  11 +exports.testStore.setDbProps(dbProps);
8 12
9 13 //start the test runner if we're called directly from command line
10 14 if (require.main == module.id) {
12 test/oracle_test.js
... ... @@ -1,10 +1,14 @@
1   -exports = require("./store_test");
2   -exports.setDbProps({
3   - "url": "jdbc:oracle:thin:@192.168.1.212:1524:XE",
  1 +var dbProps = {
  2 + "url": "jdbc:oracle:thin:@192.168.1.215:1524:XE",
4 3 "driver": "oracle.jdbc.driver.OracleDriver",
5 4 "username": "test",
6 5 "password": "test"
7   -});
  6 +};
  7 +
  8 +exports.testTransaction = require("./transaction_test");
  9 +exports.testTransaction.setDbProps(dbProps);
  10 +exports.testStore = require("./store_test");
  11 +exports.testStore.setDbProps(dbProps);
8 12
9 13 //start the test runner if we're called directly from command line
10 14 if (require.main == module.id) {
16 test/postgresql_test.js
... ... @@ -0,0 +1,16 @@
  1 +var dbProps = {
  2 + "url": "jdbc:postgresql://192.168.1.215/test",
  3 + "driver": "org.postgresql.Driver",
  4 + "username": "test",
  5 + "password": "test"
  6 +};
  7 +
  8 +exports.testTransaction = require("./transaction_test");
  9 +exports.testTransaction.setDbProps(dbProps);
  10 +exports.testStore = require("./store_test");
  11 +exports.testStore.setDbProps(dbProps);
  12 +
  13 +//start the test runner if we're called directly from command line
  14 +if (require.main == module.id) {
  15 + require('test').run(exports);
  16 +}
6 test/store_test.js
@@ -92,7 +92,6 @@ const MAPPING_AUTHOR = {
92 92 };
93 93
94 94 function populate(store) {
95   - var transaction = store.createTransaction();
96 95 var authors = [];
97 96 for (var i=1; i<=5; i+=1) {
98 97 var author = new Author({
@@ -101,6 +100,7 @@ function populate(store) {
101 100 author.save();
102 101 authors.push(author);
103 102 }
  103 + var transaction = store.createTransaction();
104 104 for (var i=0; i<10; i+=1) {
105 105 var nr = i + 1;
106 106 var props = {
@@ -144,11 +144,13 @@ exports.tearDown = function() {
144 144 var schemaName = ctor.mapping.schemaName || store.dialect.getDefaultSchema(conn);
145 145 if (sqlUtils.tableExists(conn, ctor.mapping.tableName, schemaName)) {
146 146 sqlUtils.dropTable(conn, store.dialect, ctor.mapping.tableName, schemaName);
147   - if (store.dialect.hasSequenceSupport()) {
  147 + if (ctor.mapping.id.hasSequence() && store.dialect.hasSequenceSupport()) {
148 148 sqlUtils.dropSequence(conn, store.dialect, ctor.mapping.id.sequence, schemaName);
149 149 }
150 150 }
151 151 });
  152 + conn.close();
  153 + store.closeConnections();
152 154 return;
153 155 };
154 156
88 test/transaction_test.js
... ... @@ -0,0 +1,88 @@
  1 +var assert = require("assert");
  2 +var Store = require("ringo/storage/sql/store").Store;
  3 +var sqlUtils = require("ringo/storage/sql/util");
  4 +
  5 +var store = null;
  6 +var Author = null;
  7 +
  8 +const MAPPING_AUTHOR = {
  9 + "table": "author",
  10 + "id": {
  11 + "column": "author_id"
  12 + },
  13 + "properties": {
  14 + "name": {
  15 + "type": "string",
  16 + "column": "author_name",
  17 + "nullable": false
  18 + }
  19 + }
  20 +};
  21 +
  22 +var dbProps = {
  23 + "url": "jdbc:h2:mem:test;MVCC=TRUE",
  24 + "driver": "org.h2.Driver"
  25 +};
  26 +
  27 +exports.setDbProps = function(props) {
  28 + dbProps = props;
  29 +};
  30 +
  31 +exports.setUp = function() {
  32 + store = new Store(dbProps);
  33 + Author = store.defineEntity("Author", MAPPING_AUTHOR);
  34 + return;
  35 +};
  36 +
  37 +exports.tearDown = function() {
  38 + var conn = store.getConnection();
  39 + var schemaName = Author.mapping.schemaName || store.dialect.getDefaultSchema(conn);
  40 + if (sqlUtils.tableExists(conn, Author.mapping.tableName, schemaName)) {
  41 + sqlUtils.dropTable(conn, store.dialect, Author.mapping.tableName, schemaName);
  42 + if (Author.mapping.id.hasSequence() && store.dialect.hasSequenceSupport()) {
  43 + sqlUtils.dropSequence(conn, store.dialect, Author.mapping.id.sequence, schemaName);
  44 + }
  45 + }
  46 + store.closeConnections();
  47 + store = null;
  48 + Author = null;
  49 + return;
  50 +};
  51 +
  52 +exports.testTransaction = function() {
  53 + var transaction = store.createTransaction();
  54 + assert.isFalse(transaction.isDirty());
  55 +
  56 + var authors = [];
  57 + // insert some test objects
  58 + for (var i=0; i<5; i+=1) {
  59 + var author = new Author({
  60 + "name": "Author " + (i + 1)
  61 + });
  62 + author.save(transaction);
  63 + authors.push(author);
  64 + }
  65 + assert.strictEqual(transaction.inserted.length, authors.length);
  66 + assert.isTrue(transaction.isDirty());
  67 + assert.strictEqual(Author.all().length, 0);
  68 + transaction.commit();
  69 + assert.isFalse(transaction.isDirty());
  70 + assert.strictEqual(Author.all().length, 5);
  71 +
  72 + // re-use the transaction
  73 + var author = new Author({
  74 + "name": "Author " + (authors.length + 1)
  75 + });
  76 + author.save(transaction);
  77 + assert.isTrue(transaction.isDirty());
  78 + assert.strictEqual(transaction.inserted.length, 1);
  79 + transaction.commit();
  80 + assert.isFalse(transaction.isDirty());
  81 + assert.strictEqual(Author.all().length, 6);
  82 + return;
  83 +};
  84 +
  85 +//start the test runner if we're called directly from command line
  86 +if (require.main == module.id) {
  87 + require('test').run(exports);
  88 +}

0 comments on commit 0620f53

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